module Recommendable::Ratable::ClassMethods
Public Class Methods
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.
# File lib/recommendable/ratable.rb, line 66 def self.ratable_class ancestors.find { |klass| Recommendable.config.ratable_classes.include?(klass) } end
Whether or not items belonging to this class can be recommended.
@return true if a user class `recommends :this`
# File lib/recommendable/ratable.rb, line 35 def self.recommendable?() true 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
# File lib/recommendable/ratable.rb, line 50 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
Public Instance Methods
# File lib/recommendable/ratable.rb, line 20 def before_destroy() super and remove_from_recommendable! end
# 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
Check to see if anybody has rated (liked or disliked) this object
@return true if anybody has liked/disliked this
# File lib/recommendable/ratable.rb, line 40 def rated? liked_by_count > 0 || disliked_by_count > 0 end
Whether or not items belonging to this class can be recommended.
@return true if a user class `recommends :this`
# File lib/recommendable/ratable.rb, line 105 def recommendable?() false end
Completely removes this item from redis. Called from a before_destroy
hook. @private
# File lib/recommendable/ratable.rb, line 74 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