Module: Recommendable::Ratable::ClassMethods

Defined in:
lib/recommendable/ratable.rb

Instance Method Summary collapse

Instance Method Details

#make_recommendable!Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/recommendable/ratable.rb', line 11

def make_recommendable!
  Recommendable.configure { |config| config.ratable_classes << self }

  class_eval do
    include Likable
    include Dislikable

    case
    when defined?(Sequel::Model) && ancestors.include?(Sequel::Model)
      def before_destroy() super and remove_from_recommendable! end
    when defined?(ActiveRecord::Base)            && ancestors.include?(ActiveRecord::Base),
         defined?(Mongoid::Document)             && ancestors.include?(Mongoid::Document),
         defined?(MongoMapper::Document)         && ancestors.include?(MongoMapper::Document),
         defined?(MongoMapper::EmbeddedDocument) && ancestors.include?(MongoMapper::EmbeddedDocument)
      before_destroy :remove_from_recommendable!
    when defined?(DataMapper::Resource) && ancestors.include?(DataMapper::Resource)
      before :destroy, :remove_from_recommendable!
    else
      warn "Model #{self} is not using a supported ORM. You must handle removal from Redis manually when destroying instances."
    end

    # Whether or not items belonging to this class can be recommended.
    #
    # @return true if a user class `recommends :this`
    def self.recommendable?() true end

    # Check to see if anybody has rated (liked or disliked) this object
    #
    # @return true if anybody has liked/disliked this
    def rated?
      liked_by_count > 0 || disliked_by_count > 0
    end

    # Query for the top-N items sorted by score
    #
    # @param [Hash] options a hash of options to modify which items are returned
    # @option options [Integer] :count the number of items to fetch (defaults to 1)
    # @option options [Integer] :offset an offset to allow paging through results
    # @return [Array] the top items belonging to this class, sorted by score
    def self.top(options = {})
      if options.is_a?(Integer)
        options = { :count => options }
        warn "[DEPRECATION] Recommenable::Ratable.top now takes an options hash. Please call `.top(count: #{options[:count]})` instead of just `.top(#{options[:count]})`"
      end
      options.reverse_merge!(:count => 1, :offset => 0)
      score_set = Recommendable::Helpers::RedisKeyMapper.score_set_for(self)
      ids = Recommendable.redis.zrevrange(score_set, options[:offset], options[:offset] + options[:count] - 1)

      Recommendable.query(self, ids).sort_by { |item| ids.index(item.id.to_s) }
    end

    # Returns the class that has been explicitly been made ratable, whether it is this
    # class or a superclass. This allows a ratable class and all of its subclasses to be
    # considered the same type of ratable and give recommendations from the base class
    # or any of the subclasses.
    def self.ratable_class
      ancestors.find { |klass| Recommendable.config.ratable_classes.include?(klass) }
    end

    private

    # Completely removes this item from redis. Called from a before_destroy hook.
    # @private
    def remove_from_recommendable!
      sets  = [] # SREM needed
      zsets = [] # ZREM needed
      keys  = [] # DEL  needed
      # Remove this item from the score zset
      zsets << Recommendable::Helpers::RedisKeyMapper.score_set_for(self.class)

      # Remove this item's liked_by/disliked_by sets
      keys << Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(self.class, id)
      keys << Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(self.class, id)

      # Remove this item from any user's like/dislike/hidden/bookmark sets
      %w[liked disliked hidden bookmarked].each do |action|
        sets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.send("#{action}_set_for", self.class, '*'))
      end

      # Remove this item from any user's recommendation zset
      zsets += Recommendable.redis.keys(Recommendable::Helpers::RedisKeyMapper.recommended_set_for(self.class, '*'))

      Recommendable.redis.pipelined do |redis|
        sets.each { |set| redis.srem(set, id) }
        zsets.each { |zset| redis.zrem(zset, id) }
        redis.del(*keys)
      end
    end
  end
end

#recommendable?Boolean

Whether or not items belonging to this class can be recommended.

Returns:

  • (Boolean)

    true if a user class ‘recommends :this`



105
# File 'lib/recommendable/ratable.rb', line 105

def recommendable?() false end