class Post include Mongoid::Document include Mongoid::Timestamps include Tanker if defined? Tanker STATES = ['draft', 'published'] # Scopes ========================================================================================= scope :drafts, :where => {:state => 'draft'} scope :published, :where => {:state => 'published'}, :descending => :publication_date scope :by_slug, lambda {|slug| {:where => {:slug.in => ["#{slug}".gsub('//','/'), "/#{slug}/".gsub('//','/')] } } } # Mongoid ======================================================================================== field :author field :content field :meta_keywords field :publication_date, :type => DateTime field :published_at, :type => Date field :slug field :state field :summary field :tags, :type => Array field :title field :title_short # because "title" is often too long for a decent layout index :slug index :state, :unique => false belongs_to :blog has_and_belongs_to_many :blog_categories # Behavior ======================================================================================= before_save :set_slug validates_uniqueness_of :slug # Tanker ========================================================================================= tankit 'idx' do indexes :author indexes :content indexes :summary indexes :tags indexes :title end if defined? Tanker after_destroy :delete_tank_indexes unless Rails.application.config.tanker_disabled after_save :update_tank_indexes unless Rails.application.config.tanker_disabled # Validations ==================================================================================== validates_presence_of :content, :title # Instance methods: Overrides ==================================================================== # Returns this post's publication date, defaulting to published-at. # # @return [DateTime] publication or published-at date def publication_date self[:publication_date] || self.published_at end # Instance methods =============================================================================== # Returns true if this post is an unpublished draft. # # @return [Boolean] true if draft def draft? self.state == 'draft' || self.state.nil? end # Joins this post to its associated blog categories, insuring data integrity at both ends of the # join. def fix_blog_category_join self.blog_categories.each do |cat| cat.post_ids << self.id cat.save end end # @deprecated Please use {#path} instead def full_path warn "[DEPRECATION] `full_path` is deprecated. Please use `path` instead." self.path end # @deprecated Please use {#path} instead def humanize_path warn "[DEPRECATION] `humanize_path` is deprecated. Please use `path` instead." self.path end # Returns the index of this post in the blog's collection of posts. # # @return [Fixnum] the path for this post def my_index self.blog.posts.published.to_a.index self end # Returns the next post in the blog. # # @return [Post] the next post def next_post return if draft? i = self.my_index - 1 self.blog.posts.published[i] end # Returns this post's path. # # @return [String] the path for this post def path "#{self.blog.path}/#{self.slug}".gsub('//', '/') end # Returns this post's path with a trailing slash. # # @return [String] the path for this post def path_ts "#{self.path}/" end # Returns the previous post in the blog. # # @return [Post] the previous post def previous_post return if draft? i = self.my_index + 1 i = 0 if i == self.blog.posts.published.size self.blog.posts.published[i] end # Publishes this post so it's publically available. def publish! self.update_attributes :state => 'published', :published_at => Time.zone.now end # Returns whether this post is publically available. # # @return [Boolean] true if published def published? self.state == 'published' end # Returns the first 200 characters of this post's summary, suitable for use as a meta description. # # @return [String] search description def search_description self.summary.gsub(/<\/?[^>]*>/, '')[0..199].html_safe end # Returns this post's title. # # @return [String] title def search_title self.title end # Reverts this post's status to draft so it is no longer publically available. def unpublish! self.update_attributes :state => 'draft' end # Sets the specified state for this post, forcing lowercase. # # @param [String] state def state=(arg) self[:state] = arg.downcase end # Returns this post's status. # # @return [String] status def status self.state ? self.state.capitalize : 'Draft' end private def set_slug self.slug = self.title.to_s.to_url if self.slug.blank? end end