class Thoth::Post

Public Class Methods

get(name) click to toggle source

Gets the published post with the specified name, where name can be either a name or an id. Does not return drafts.

# File lib/thoth/model/post.rb, line 59
def self.get(name)
  return Post[:id => name, :is_draft => false] if name.is_a?(Numeric)

  name = name.to_s.downcase
  name =~ /^\d+$/ ? Post[:id => name, :is_draft => false] :
      Post[:name => name, :is_draft => false]
end
name_unique?(name) click to toggle source

Returns true if the specified post name is already taken or is a reserved name.

# File lib/thoth/model/post.rb, line 69
def self.name_unique?(name)
  !PostController.methods.include?(name) &&
      !PostController.instance_methods.include?(name) &&
      !Post[:name => name.to_s.downcase]
end
name_valid?(name) click to toggle source

Returns true if the specified post name consists of valid characters and is not too long or too short.

# File lib/thoth/model/post.rb, line 77
def self.name_valid?(name)
  !!(name =~ /^[0-9a-z_-]{1,64}$/i) && !(name =~ /^[0-9]+$/)
end
recent(page = 1, limit = 10) click to toggle source

Gets a paginated dataset of recent published posts sorted in reverse order by creation time. Does not return drafts.

# File lib/thoth/model/post.rb, line 83
def self.recent(page = 1, limit = 10)
  filter(:is_draft => false).reverse_order(:created_at).paginate(page,
      limit)
end
recent_drafts(page = 1, limit = 10) click to toggle source

Gets a paginated dataset of recent draft posts sorted in reverse order by creation time. Does not return published posts.

# File lib/thoth/model/post.rb, line 90
def self.recent_drafts(page = 1, limit = 10)
  filter(:is_draft => true).reverse_order(:created_at).paginate(page,
      limit)
end
suggest_name(title) click to toggle source

Returns a valid, unique post name based on the specified title. If the title is empty or cannot be converted into a valid name, an empty string will be returned.

# File lib/thoth/model/post.rb, line 98
def self.suggest_name(title)
  index = 1

  # Remove HTML entities and non-alphanumeric characters, replace spaces
  # with hyphens, and truncate the name at 64 characters.
  name = title.to_s.strip.downcase.gsub(/&[^\s;]+;/, '_').
      gsub(/[^\s0-9a-z-]/, '').gsub(/\s+/, '-')[0..63]

  # Strip off any trailing non-alphanumeric characters.
  name.gsub!(/[_-]+$/, '')

  return '' if name.empty?

  # If the name consists solely of numeric characters, add an alpha
  # character to prevent name/id ambiguity.
  name += '_' unless name =~ /[a-z_-]/

  # Ensure that the name doesn't conflict with any methods on the Post
  # controller and that no two posts have the same name.
  until self.name_unique?(name)
    if name[-1] == index
      name[-1] = (index += 1).to_s
    else
      name = name[0..62] if name.size >= 64
      name += (index += 1).to_s
    end
  end

  return name
end

Public Instance Methods

atom_url() click to toggle source

Gets the Atom feed URL for this post.

# File lib/thoth/model/post.rb, line 134
def atom_url
  Config.site['url'].chomp('/') + PostController.r(:atom, name).to_s
end
body=(body) click to toggle source
# File lib/thoth/model/post.rb, line 138
def body=(body)
  self[:body]          = body.strip
  self[:body_rendered] = RedCloth.new(wiki_to_html(body.dup.strip)).to_html
end
comments() click to toggle source

Gets a dataset of visible comments attached to this post, ordered by creation time.

# File lib/thoth/model/post.rb, line 145
def comments
  @comments ||= Comment.filter(:post_id => id, :deleted => false).order(:created_at)
end
created_at(format = nil) click to toggle source

Gets the creation time of this post. If format is provided, the time will be returned as a formatted String. See Time.strftime for details.

# File lib/thoth/model/post.rb, line 151
def created_at(format = nil)
  if new?
    format ? Time.now.strftime(format) : Time.now
  else
    format ? self[:created_at].strftime(format) : self[:created_at]
  end
end
name=(name) click to toggle source
# File lib/thoth/model/post.rb, line 159
def name=(name)
  self[:name] = name.to_s.strip.downcase unless name.nil?
end
tags() click to toggle source

Gets an Array of tags attached to this post, ordered by name.

# File lib/thoth/model/post.rb, line 164
def tags
  if new?
    @fake_tags || []
  else
    @tags ||= tags_dataset.all
  end
end
tags=(tag_names) click to toggle source
# File lib/thoth/model/post.rb, line 172
def tags=(tag_names)
  if tag_names.is_a?(String)
    tag_names = tag_names.split(',', 64)
  elsif !tag_names.is_a?(Array)
    raise ArgumentError, "Expected String or Array, got #{tag_names.class}"
  end

  tag_names = tag_names.map{|n| n.strip.downcase}.uniq.delete_if{|n|
      n.empty?}

  if new?
    # This Post hasn't been saved yet, so instead of attaching actual tags
    # to it, we'll create a bunch of fake tags just for the preview. We
    # won't create the real ones until the Post is saved.
    @fake_tags = []

    tag_names.each {|name| @fake_tags << Tag.new(:name => name)}
    @fake_tags.sort! {|a, b| a.name <=> b.name }

    return @fake_tags
  else
    real_tags = []

    # First delete any existing tag mappings for this post.
    TagsPostsMap.filter(:post_id => id).delete

    # Create new tags and new mappings.
    tag_names.each do |name|
      tag = Tag.find_or_create(:name => name)
      real_tags << tag
      TagsPostsMap.create(:post_id => id, :tag_id => tag.id)
    end

    return real_tags
  end
end
title=(title) click to toggle source
# File lib/thoth/model/post.rb, line 209
def title=(title)
  title.strip!

  # Set the post's name if it isn't already set.
  if self[:name].nil? || self[:name].empty?
    self[:name] = Post.suggest_name(title)
  end

  self[:title] = title
end
updated_at(format = nil) click to toggle source

Gets the time this post was last updated. If format is provided, the time will be returned as a formatted String. See Time.strftime for details.

# File lib/thoth/model/post.rb, line 223
def updated_at(format = nil)
  if new?
    format ? Time.now.strftime(format) : Time.now
  else
    format ? self[:updated_at].strftime(format) : self[:updated_at]
  end
end
url() click to toggle source

Gets the URL for this post.

# File lib/thoth/model/post.rb, line 232
def url
  Config.site['url'].chomp('/') + PostController.r(:/, name).to_s
end
validate() click to toggle source
# File lib/thoth/model/post.rb, line 236
def validate
  validates_presence(:name,  :message => 'Please enter a name for this post.')
  validates_presence(:title, :message => 'Please enter a title for this post.')
  validates_presence(:body,  :message => "What's the matter? Cat got your tongue?")

  validates_max_length(255, :title, :message => 'Please enter a title under 255 characters.')
  validates_max_length(64,  :name,  :message => 'Please enter a name under 64 characters.')

  validates_format(/^[0-9a-z_-]+$/i, :name, :message => 'Post names may only contain letters, numbers, underscores, and dashes.')
  validates_format(/[a-z_-]/i,       :name, :message => 'Post names must contain at least one non-numeric character.')
end