acts_as_follower.rb 3.43 KB
require File.dirname(__FILE__) + '/follower_lib'

module ActiveRecord #:nodoc:
  module Acts #:nodoc:
    module Follower
      
      def self.included(base)
        base.extend ClassMethods
        base.class_eval do
          include FollowerLib
        end
      end
      
      module ClassMethods
        def acts_as_follower
          has_many :follows, :as => :follower, :dependent => :destroy
          include ActiveRecord::Acts::Follower::InstanceMethods
        end
      end
      
      module InstanceMethods
        
        # Returns true if this instance is following the object passed as an argument.
        def following?(followable)
          0 < Follow.unblocked.count(:all, :conditions => [
                "follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?",
                 self.id, parent_class_name(self), followable.id, parent_class_name(followable)
               ])
        end
        
        # Returns the number of objects this instance is following.
        def follow_count
          Follow.unblocked.count(:all, :conditions => ["follower_id = ? AND follower_type = ?", self.id, parent_class_name(self)])
        end
        
        # Creates a new follow record for this instance to follow the passed object.
        # Does not allow duplicate records to be created.
        def follow(followable)
          follow = get_follow(followable)
          unless follow
            Follow.create(:followable => followable, :follower => self)
          end
        end
        
        # Deletes the follow record if it exists.
        def stop_following(followable)
          follow = get_follow(followable)
          if follow
            follow.destroy
          end
        end
        
        # Returns the follow records related to this instance by type.
        def follows_by_type(followable_type)
          Follow.unblocked.find(:all, :include => [:followable], :conditions => ["follower_id = ? AND follower_type = ? AND followable_type = ?", self.id, parent_class_name(self), followable_type])
        end
        
        # Returns the follow records related to this instance with the followable included.
        def all_follows
          self.follows.unblocked.all(:include => :followable)
        end
        
        # Returns the actual records which this instance is following.
        def all_following
          all_follows.collect{ |f| f.followable }
        end
        
        # Returns the actual records of a particular type which this record is following.
        def following_by_type(followable_type)
          follows_by_type(followable_type).collect{ |f| f.followable }
        end
        
        # Allows magic names on following_by_type
        # e.g. following_users == following_by_type('User')
        def method_missing(m, *args)
          if m.to_s[/following_(.+)/]
            following_by_type($1.singularize.classify)
          else
            super
          end
        end
        
        private
        
        # Returns a follow record for the current instance and followable object.
        def get_follow(followable)
          Follow.unblocked.find(:first, :conditions => ["follower_id = ? AND follower_type = ? AND followable_id = ? AND followable_type = ?", self.id, parent_class_name(self), followable.id, parent_class_name(followable)])
        end
        
      end
      
    end
  end
end