recommendable 1.1.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. data/lib/recommendable.rb +38 -26
  2. data/lib/recommendable/configuration.rb +47 -0
  3. data/lib/recommendable/helpers.rb +3 -9
  4. data/lib/recommendable/helpers/calculations.rb +150 -0
  5. data/lib/recommendable/helpers/queriers.rb +23 -0
  6. data/lib/recommendable/helpers/redis_key_mapper.rb +29 -0
  7. data/lib/recommendable/orm/active_record.rb +6 -0
  8. data/lib/recommendable/orm/data_mapper.rb +7 -0
  9. data/lib/recommendable/orm/mongo_mapper.rb +8 -0
  10. data/lib/recommendable/orm/mongoid.rb +7 -0
  11. data/lib/recommendable/ratable.rb +83 -0
  12. data/lib/recommendable/ratable/dislikable.rb +26 -0
  13. data/lib/recommendable/ratable/likable.rb +26 -0
  14. data/lib/recommendable/rater.rb +109 -0
  15. data/lib/recommendable/rater/bookmarker.rb +120 -0
  16. data/lib/recommendable/rater/disliker.rb +122 -0
  17. data/lib/recommendable/rater/hider.rb +120 -0
  18. data/lib/recommendable/rater/liker.rb +122 -0
  19. data/lib/recommendable/rater/recommender.rb +68 -0
  20. data/lib/recommendable/version.rb +5 -4
  21. data/lib/recommendable/workers/delayed_job.rb +16 -0
  22. data/lib/recommendable/workers/rails.rb +16 -0
  23. data/lib/recommendable/workers/resque.rb +13 -0
  24. data/lib/recommendable/workers/sidekiq.rb +13 -0
  25. metadata +62 -131
  26. data/.gitignore +0 -57
  27. data/.travis.yml +0 -3
  28. data/CHANGELOG.markdown +0 -159
  29. data/Gemfile +0 -3
  30. data/Gemfile.lock +0 -112
  31. data/LICENSE.txt +0 -22
  32. data/README.markdown +0 -135
  33. data/Rakefile +0 -26
  34. data/TODO +0 -7
  35. data/app/models/recommendable/dislike.rb +0 -19
  36. data/app/models/recommendable/ignore.rb +0 -19
  37. data/app/models/recommendable/like.rb +0 -19
  38. data/app/models/recommendable/stash.rb +0 -19
  39. data/app/workers/recommendable/delayed_job_worker.rb +0 -17
  40. data/app/workers/recommendable/rails_worker.rb +0 -17
  41. data/app/workers/recommendable/resque_worker.rb +0 -14
  42. data/app/workers/recommendable/sidekiq_worker.rb +0 -14
  43. data/config/routes.rb +0 -3
  44. data/db/migrate/20120124193723_create_likes.rb +0 -17
  45. data/db/migrate/20120124193728_create_dislikes.rb +0 -17
  46. data/db/migrate/20120127092558_create_ignores.rb +0 -17
  47. data/db/migrate/20120131173909_create_stashes.rb +0 -17
  48. data/lib/generators/recommendable/USAGE +0 -8
  49. data/lib/generators/recommendable/install_generator.rb +0 -40
  50. data/lib/generators/recommendable/templates/initializer.rb +0 -28
  51. data/lib/recommendable/acts_as_recommendable.rb +0 -176
  52. data/lib/recommendable/acts_as_recommended_to.rb +0 -774
  53. data/lib/recommendable/engine.rb +0 -14
  54. data/lib/recommendable/exceptions.rb +0 -4
  55. data/lib/recommendable/railtie.rb +0 -6
  56. data/lib/sidekiq/middleware/client/unique_jobs.rb +0 -37
  57. data/lib/sidekiq/middleware/server/unique_jobs.rb +0 -17
  58. data/lib/tasks/recommendable_tasks.rake +0 -1
  59. data/recommendable.gemspec +0 -30
  60. data/script/rails +0 -8
  61. data/spec/configuration_spec.rb +0 -9
  62. data/spec/dummy/README.rdoc +0 -261
  63. data/spec/dummy/Rakefile +0 -7
  64. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  65. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  66. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  67. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  68. data/spec/dummy/app/mailers/.gitkeep +0 -0
  69. data/spec/dummy/app/models/.gitkeep +0 -0
  70. data/spec/dummy/app/models/bully.rb +0 -2
  71. data/spec/dummy/app/models/movie.rb +0 -2
  72. data/spec/dummy/app/models/php_framework.rb +0 -2
  73. data/spec/dummy/app/models/user.rb +0 -3
  74. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  75. data/spec/dummy/config.ru +0 -4
  76. data/spec/dummy/config/application.rb +0 -56
  77. data/spec/dummy/config/boot.rb +0 -10
  78. data/spec/dummy/config/database.yml +0 -25
  79. data/spec/dummy/config/environment.rb +0 -5
  80. data/spec/dummy/config/environments/development.rb +0 -37
  81. data/spec/dummy/config/environments/production.rb +0 -67
  82. data/spec/dummy/config/environments/test.rb +0 -37
  83. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  84. data/spec/dummy/config/initializers/inflections.rb +0 -15
  85. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  86. data/spec/dummy/config/initializers/recommendable.rb +0 -14
  87. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  88. data/spec/dummy/config/initializers/session_store.rb +0 -8
  89. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  90. data/spec/dummy/config/locales/en.yml +0 -5
  91. data/spec/dummy/config/routes.rb +0 -4
  92. data/spec/dummy/db/migrate/20120128005553_create_likes.recommendable.rb +0 -18
  93. data/spec/dummy/db/migrate/20120128005554_create_dislikes.recommendable.rb +0 -18
  94. data/spec/dummy/db/migrate/20120128005555_create_ignores.recommendable.rb +0 -18
  95. data/spec/dummy/db/migrate/20120128020228_create_users.rb +0 -9
  96. data/spec/dummy/db/migrate/20120128020413_create_movies.rb +0 -10
  97. data/spec/dummy/db/migrate/20120128024632_create_php_frameworks.rb +0 -9
  98. data/spec/dummy/db/migrate/20120128024804_create_bullies.rb +0 -9
  99. data/spec/dummy/db/migrate/20120131195416_create_stashes.recommendable.rb +0 -19
  100. data/spec/dummy/db/schema.rb +0 -89
  101. data/spec/dummy/lib/assets/.gitkeep +0 -0
  102. data/spec/dummy/log/.gitkeep +0 -0
  103. data/spec/dummy/public/404.html +0 -26
  104. data/spec/dummy/public/422.html +0 -26
  105. data/spec/dummy/public/500.html +0 -25
  106. data/spec/dummy/public/favicon.ico +0 -0
  107. data/spec/dummy/recommendable_dummy_development +0 -0
  108. data/spec/dummy/recommendable_dummy_test +0 -0
  109. data/spec/dummy/script/rails +0 -6
  110. data/spec/factories.rb +0 -16
  111. data/spec/models/dislike_spec.rb +0 -41
  112. data/spec/models/ignore_spec.rb +0 -27
  113. data/spec/models/like_spec.rb +0 -42
  114. data/spec/models/movie_spec.rb +0 -82
  115. data/spec/models/stash_spec.rb +0 -27
  116. data/spec/models/user_benchmark_spec.rb +0 -49
  117. data/spec/models/user_spec.rb +0 -443
  118. data/spec/spec_helper.rb +0 -28
@@ -0,0 +1,26 @@
1
+ module Recommendable
2
+ module Ratable
3
+ module Dislikable
4
+ # Fetch a list of users that have disliked this item.
5
+ #
6
+ # @return [Array] a list of users that have disliked this item
7
+ def disliked_by
8
+ Recommendable.query(Recommendable.config.user_class, disliked_by_ids)
9
+ end
10
+
11
+ # Get the number of users that have disliked this item
12
+ #
13
+ # @return [Fixnum] the number of users that have disliked this item
14
+ def disliked_by_count
15
+ Recommendable.redis.scard(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(self.class, id))
16
+ end
17
+
18
+ # Get the IDs of users that have disliked this item.
19
+ #
20
+ # @return [Array] the IDs of users that have disliked this item
21
+ def disliked_by_ids
22
+ Recommendable.redis.smembers(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(self.class, id))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Recommendable
2
+ module Ratable
3
+ module Likable
4
+ # Fetch a list of users that have liked this item.
5
+ #
6
+ # @return [Array] a list of users that have liked this item
7
+ def liked_by
8
+ Recommendable.query(Recommendable.config.user_class, liked_by_ids)
9
+ end
10
+
11
+ # Get the number of users that have liked this item
12
+ #
13
+ # @return [Fixnum] the number of users that have liked this item
14
+ def liked_by_count
15
+ Recommendable.redis.scard(Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(self.class, id))
16
+ end
17
+
18
+ # Get the IDs of users that have liked this item.
19
+ #
20
+ # @return [Array] the IDs of users that have liked this item
21
+ def liked_by_ids
22
+ Recommendable.redis.smembers(Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(self.class, id))
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,109 @@
1
+ require 'recommendable/rater/liker'
2
+ require 'recommendable/rater/disliker'
3
+ require 'recommendable/rater/hider'
4
+ require 'recommendable/rater/bookmarker'
5
+ require 'recommendable/rater/recommender'
6
+ require 'hooks'
7
+
8
+ module Recommendable
9
+ module Rater
10
+ extend ActiveSupport::Concern
11
+
12
+ module ClassMethods
13
+ def recommends(*things)
14
+ Recommendable.configure do |config|
15
+ config.ratable_classes = []
16
+ config.user_class = self
17
+ end
18
+
19
+ things.each { |thing| thing.to_s.classify.constantize.make_recommendable! }
20
+
21
+ class_eval do
22
+ include Liker
23
+ include Disliker
24
+ include Hider
25
+ include Bookmarker
26
+ include Recommender
27
+ include Hooks
28
+
29
+ if ancestors.include?(ActiveRecord::Base) || include?(Mongoid::Document) || include?(MongoMapper::Document) || include?(MongoMapper::EmbeddedDocument)
30
+ before_destroy :remove_from_recommendable!
31
+ elsif include?(DataMapper::Resource)
32
+ before :destroy, :remove_from_recommendable!
33
+ end
34
+
35
+ define_hooks :before_like, :after_like, :before_unlike, :after_unlike,
36
+ :before_dislike, :after_dislike, :before_undislike, :after_undislike,
37
+ :before_hide, :after_hide, :before_unhide, :after_unhide,
38
+ :before_bookmark, :after_bookmark, :before_unbookmark, :after_unbookmark
39
+
40
+ before_like lambda { |obj| undislike(obj) || unhide(obj) }
41
+ before_dislike lambda { |obj| unlike(obj) || unhide(obj) }
42
+
43
+ %w[like unlike dislike undislike].each do |action|
44
+ send("after_#{action}", lambda { |obj|
45
+ Recommendable::Helpers::Calculations.update_score_for(obj.class, obj.id)
46
+ Recommendable.enqueue(self.id) if Recommendable.config.auto_enqueue
47
+ })
48
+ end
49
+
50
+ %w[like dislike hide bookmark].each do |action|
51
+ send("after_#{action}", lambda { |obj| unrecommend(obj) })
52
+ end
53
+
54
+ def method_missing(method, *args, &block)
55
+ if method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked)_(.+)_in_common_with\z/
56
+ begin
57
+ send("#{$1}_in_common_with", $2.classify.constantize, *args)
58
+ rescue NameError
59
+ super
60
+ end
61
+ elsif method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked)_(.+)_count\z/
62
+ begin
63
+ send("#{$1}_count_for", $2.classify.constantize, *args)
64
+ rescue NameError
65
+ super
66
+ end
67
+ elsif method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked)_(.+)_ids\z/
68
+ begin
69
+ send("#{$1}_ids_for", $2.classify.constantize)
70
+ rescue NameError
71
+ super
72
+ end
73
+ elsif method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked|recommended)_(.+)\z/
74
+ begin
75
+ send("#{$1}_for", $2.classify.constantize)
76
+ rescue NameError
77
+ super
78
+ end
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ def respond_to?(method, include_private = false)
85
+ if method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked)_(.+)_in_common_with\z/ ||
86
+ method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked)_(.+)_ids\z/ ||
87
+ method.to_s =~ /\A((?:dis)?liked|hidden|bookmarked|recommended)_(.+)\z/
88
+ begin
89
+ true if $2.classify.constantize.recommendable?
90
+ rescue NameError
91
+ false
92
+ end
93
+ else
94
+ super
95
+ end
96
+ end
97
+
98
+ def rated?(obj)
99
+ likes?(obj) || dislikes?(obj)
100
+ end
101
+
102
+ def rated_anything?
103
+ likes_count > 0 || dislikes_count > 0
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,120 @@
1
+ module Recommendable
2
+ module Rater
3
+ module Bookmarker
4
+ # Bookmark an object. This is not possible if the object is hidden.
5
+ #
6
+ # @param [Object] obj the object to be bookmarked
7
+ # @return true if object was bookmarked successfully
8
+ # @raise [ArgumentError] if the passed object was not declared ratable
9
+ def bookmark(obj)
10
+ raise(ArgumentError, 'Object has not been declared ratable.') unless obj.respond_to?(:recommendable?) && obj.recommendable?
11
+ return if hides?(obj) || bookmarks?(obj)
12
+
13
+ run_hook(:before_bookmark, obj)
14
+ Recommendable.redis.sadd(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(obj.class, id), obj.id)
15
+ run_hook(:after_bookmark, obj)
16
+
17
+ true
18
+ end
19
+
20
+ # Check whether the user has bookmarked an object.
21
+ #
22
+ # @param [Object] obj the object in question
23
+ # @return true if the user has bookmarked obj, false if not
24
+ def bookmarks?(obj)
25
+ Recommendable.redis.sismember(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(obj.class, id), obj.id)
26
+ end
27
+
28
+ # Unbookmark an object. This removes the object from a user's set of bookmarks.
29
+ #
30
+ # @param [Object] obj the object to be unbookmarked
31
+ # @return true if the object was bookmarked successfully, nil if the object wasn't already bookmarked
32
+ def unbookmark(obj)
33
+ return unless bookmarks?(obj)
34
+
35
+ run_hook(:before_unbookmark, obj)
36
+ Recommendable.redis.srem(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(obj.class, id), obj.id)
37
+ run_hook(:after_unbookmark, obj)
38
+
39
+ true
40
+ end
41
+
42
+ # Retrieve an array of objects the user has bookmarked
43
+ #
44
+ # @return [Array] an array of records
45
+ def bookmarks
46
+ Recommendable.config.ratable_classes.map { |klass| bookmarked_for(klass) }.flatten
47
+ end
48
+
49
+ # Retrieve an array of objects both this user and another user have bookmarked
50
+ #
51
+ # @return [Array] an array of records
52
+ def bookmarks_in_common_with(user)
53
+ Recommendable.config.ratable_classes.map { |klass| bookmarked_in_common_with(klass, user) }.flatten
54
+ end
55
+
56
+ # Get the number of items the user has bookmarked
57
+ #
58
+ # @return [Fixnum] the number of bookmarked items
59
+ def bookmarks_count
60
+ Recommendable.config.ratable_classes.inject(0) do |sum, klass|
61
+ sum + bookmarked_count_for(klass)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Fetch IDs for objects belonging to a passed class that the user has bookmarked
68
+ #
69
+ # @param [String, Symbol, Class] the class for which you want IDs
70
+ # @return [Array] an array of IDs
71
+ # @private
72
+ def bookmarked_ids_for(klass)
73
+ ids = Recommendable.redis.smembers(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, id))
74
+ ids.map!(&:to_i) if [:active_record, :data_mapper].include?(Recommendable.config.orm)
75
+ ids
76
+ end
77
+
78
+ # Fetch records belonging to a passed class that the user has bookmarked
79
+ #
80
+ # @param [String, Symbol, Class] the class for which you want bookmarked records
81
+ # @return [Array] an array of bookmarked records
82
+ # @private
83
+ def bookmarked_for(klass)
84
+ Recommendable.query(klass, bookmarked_ids_for(klass))
85
+ end
86
+
87
+ # Get the number of items belonging to a passed class that the user has bookmarked
88
+ #
89
+ # @param [String, Symbol, Class] the class for which you want a count of bookmarks
90
+ # @return [Fixnum] the number of bookmarks
91
+ # @private
92
+ def bookmarked_count_for(klass)
93
+ Recommendable.redis.scard(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, id))
94
+ end
95
+
96
+ # Get a list of records that both this user and a passed user have
97
+ # bookmarked
98
+ #
99
+ # @param [User] the other user
100
+ # @param [String, Symbol, Class] the class of common bookmarked items
101
+ # @return [Array] an array of records both users have bookmarked
102
+ # @private
103
+ def bookmarked_in_common_with(klass, user)
104
+ Recommendable.query(klass, bookmarked_ids_in_common_with(klass, user))
105
+ end
106
+
107
+ # Get a list of IDs for records that both this user and a passed user have
108
+ # bookmarked
109
+ #
110
+ # @param [User] the other user
111
+ # @param [String, Symbol, Class] the class of common bookmarked items
112
+ # @return [Array] an array of IDs for records that both users have bookmarked
113
+ # @private
114
+ def bookmarked_ids_in_common_with(klass, user_id)
115
+ user_id = user_id.id if user_id.is_a?(Recommendable.config.user_class)
116
+ Recommendable.redis.sinter(Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, id), Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(klass, user_id))
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,122 @@
1
+ module Recommendable
2
+ module Rater
3
+ module Disliker
4
+ # Dislike an object. This will remove the item from a user's set of likes
5
+ # or hidden items
6
+ #
7
+ # @param [Object] obj the object to be disliked
8
+ # @return true if object was disliked successfully
9
+ # @raise [ArgumentError] if the passed object was not declared ratable
10
+ def dislike(obj)
11
+ raise(ArgumentError, 'Object has not been declared ratable.') unless obj.respond_to?(:recommendable?) && obj.recommendable?
12
+ return if dislikes?(obj)
13
+
14
+ run_hook(:before_dislike, obj)
15
+ Recommendable.redis.sadd(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(obj.class, id), obj.id)
16
+ Recommendable.redis.sadd(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(obj.class, obj.id), id)
17
+ run_hook(:after_dislike, obj)
18
+
19
+ true
20
+ end
21
+
22
+ # Check whether the user has disliked an object.
23
+ #
24
+ # @param [Object] obj the object in question
25
+ # @return true if the user has disliked obj, false if not
26
+ def dislikes?(obj)
27
+ Recommendable.redis.sismember(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(obj.class, id), obj.id)
28
+ end
29
+
30
+ # Undislike an object. This removes the object from a user's set of dislikes.
31
+ #
32
+ # @param [Object] obj the object to be undisliked
33
+ # @return true if the object was disliked successfully, nil if the object wasn't already disliked
34
+ def undislike(obj)
35
+ return unless dislikes?(obj)
36
+
37
+ run_hook(:before_undislike, obj)
38
+ Recommendable.redis.srem(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(obj.class, id), obj.id)
39
+ Recommendable.redis.srem(Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(obj.class, obj.id), id)
40
+ run_hook(:after_undislike, obj)
41
+
42
+ true
43
+ end
44
+
45
+ # Retrieve an array of objects the user has disliked
46
+ #
47
+ # @return [Array] an array of records
48
+ def dislikes
49
+ Recommendable.config.ratable_classes.map { |klass| disliked_for(klass) }.flatten
50
+ end
51
+
52
+ # Retrieve an array of objects both this user and another user have disliked
53
+ #
54
+ # @return [Array] an array of records
55
+ def dislikes_in_common_with(user)
56
+ Recommendable.config.ratable_classes.map { |klass| disliked_in_common_with(klass, user) }.flatten
57
+ end
58
+
59
+ # Get the number of items the user has disliked
60
+ #
61
+ # @return [Fixnum] the number of disliked items
62
+ def dislikes_count
63
+ Recommendable.config.ratable_classes.inject(0) do |sum, klass|
64
+ sum + disliked_count_for(klass)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # Fetch IDs for objects belonging to a passed class that the user has disliked
71
+ #
72
+ # @param [String, Symbol, Class] the class for which you want IDs
73
+ # @return [Array] an array of IDs
74
+ # @private
75
+ def disliked_ids_for(klass)
76
+ ids = Recommendable.redis.smembers(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, id))
77
+ ids.map!(&:to_i) if [:active_record, :data_mapper].include?(Recommendable.config.orm)
78
+ ids
79
+ end
80
+
81
+ # Fetch records belonging to a passed class that the user has disliked
82
+ #
83
+ # @param [String, Symbol, Class] the class for which you want disliked records
84
+ # @return [Array] an array of disliked records
85
+ # @private
86
+ def disliked_for(klass)
87
+ Recommendable.query(klass, disliked_ids_for(klass))
88
+ end
89
+
90
+ # Get the number of items belonging to a passed class that the user has disliked
91
+ #
92
+ # @param [String, Symbol, Class] the class for which you want a count of dislikes
93
+ # @return [Fixnum] the number of dislikes
94
+ # @private
95
+ def disliked_count_for(klass)
96
+ Recommendable.redis.scard(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, id))
97
+ end
98
+
99
+ # Get a list of records that both this user and a passed user have disliked
100
+ #
101
+ # @param [User] the other user
102
+ # @param [String, Symbol, Class] the class of common disliked items
103
+ # @return [Array] an array of records both users have disliked
104
+ # @private
105
+ def disliked_in_common_with(klass, user)
106
+ Recommendable.query(klass, disliked_ids_in_common_with(klass, user))
107
+ end
108
+
109
+ # Get a list of IDs for records that both this user and a passed user have
110
+ # disliked
111
+ #
112
+ # @param [User, Fixnum] the other user (or its ID)
113
+ # @param [String, Symbol, Class] the class of common disliked items
114
+ # @return [Array] an array of IDs for records that both users have disliked
115
+ # @private
116
+ def disliked_ids_in_common_with(klass, user_id)
117
+ user_id = user_id.id if user_id.is_a?(Recommendable.config.user_class)
118
+ Recommendable.redis.sinter(Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, id), Recommendable::Helpers::RedisKeyMapper.disliked_set_for(klass, user_id))
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,120 @@
1
+ module Recommendable
2
+ module Rater
3
+ module Hider
4
+ # Hide an object. This action is only possible if the user has not yet
5
+ # rated or bookmarked the object.
6
+ #
7
+ # @param [Object] obj the object to be hidden
8
+ # @return true if object was hidden successfully
9
+ # @raise [ArgumentError] if the passed object was not declared ratable
10
+ def hide(obj)
11
+ raise(ArgumentError, 'Object has not been declared ratable.') unless obj.respond_to?(:recommendable?) && obj.recommendable?
12
+ return if likes?(obj) || dislikes?(obj) || bookmarks?(obj) || hides?(obj)
13
+
14
+ run_hook(:before_hide, obj)
15
+ Recommendable.redis.sadd(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(obj.class, id), obj.id)
16
+ run_hook(:after_hide, obj)
17
+
18
+ true
19
+ end
20
+
21
+ # Check whether the user has hidden an object.
22
+ #
23
+ # @param [Object] obj the object in question
24
+ # @return true if the user has hidden obj, false if not
25
+ def hides?(obj)
26
+ Recommendable.redis.sismember(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(obj.class, id), obj.id)
27
+ end
28
+
29
+ # Unhide an object. This removes the object from a user's set of hidden items.
30
+ #
31
+ # @param [Object] obj the object to be made visible
32
+ # @return true if the object was successfully made visible, nil if the object wasn't already hidden
33
+ def unhide(obj)
34
+ return unless hides?(obj)
35
+
36
+ run_hook(:before_unhide, obj)
37
+ Recommendable.redis.srem(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(obj.class, id), obj.id)
38
+ run_hook(:after_unhide, obj)
39
+
40
+ true
41
+ end
42
+
43
+ # Retrieve an array of objects the user has hidden
44
+ #
45
+ # @return [Array] an array of records
46
+ def hiding
47
+ Recommendable.config.ratable_classes.map { |klass| hidden_for(klass) }.flatten
48
+ end
49
+
50
+ # Retrieve an array of objects both this user and another user have hidden
51
+ #
52
+ # @return [Array] an array of records
53
+ def hiding_in_common_with(user)
54
+ Recommendable.config.ratable_classes.map { |klass| hidden_in_common_with(klass, user) }.flatten
55
+ end
56
+
57
+ # Get the number of items the user has hidden
58
+ #
59
+ # @return [Fixnum] the number of hidden items
60
+ def hidden_count
61
+ Recommendable.config.ratable_classes.inject(0) do |sum, klass|
62
+ sum + hidden_count_for(klass)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # Fetch IDs for objects belonging to a passed class that the user has hidden
69
+ #
70
+ # @param [String, Symbol, Class] the class for which you want IDs
71
+ # @return [Array] an array of IDs
72
+ # @private
73
+ def hidden_ids_for(klass)
74
+ ids = Recommendable.redis.smembers(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, id))
75
+ ids.map!(&:to_i) if [:active_record, :data_mapper].include?(Recommendable.config.orm)
76
+ ids
77
+ end
78
+
79
+ # Fetch records belonging to a passed class that the user has hidden
80
+ #
81
+ # @param [String, Symbol, Class] the class for which you want hidden records
82
+ # @return [Array] an array of hidden records
83
+ # @private
84
+ def hidden_for(klass)
85
+ Recommendable.query(klass, hidden_ids_for(klass))
86
+ end
87
+
88
+ # Get the number of items belonging to a passed class that the user has hidden
89
+ #
90
+ # @param [String, Symbol, Class] the class for which you want a count of hidden items
91
+ # @return [Fixnum] the number of hidden items
92
+ # @private
93
+ def hidden_count_for(klass)
94
+ Recommendable.redis.scard(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, id))
95
+ end
96
+
97
+ # Get a list of records that both this user and a passed user have hidden
98
+ #
99
+ # @param [User] the other user
100
+ # @param [String, Symbol, Class] the class of common hidden items
101
+ # @return [Array] an array of records both users have hidden
102
+ # @private
103
+ def hidden_in_common_with(klass, user)
104
+ Recommendable.query(klass, hidden_ids_in_common_with(klass, user))
105
+ end
106
+
107
+ # Get a list of IDs for records that both this user and a passed user have
108
+ # hidden
109
+ #
110
+ # @param [User] the other user
111
+ # @param [String, Symbol, Class] the class of common hidden items
112
+ # @return [Array] an array of IDs for records that both users have hidden
113
+ # @private
114
+ def hidden_ids_in_common_with(klass, user_id)
115
+ user_id = user_id.id if user_id.is_a?(Recommendable.config.user_class)
116
+ Recommendable.redis.sinter(Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, id), Recommendable::Helpers::RedisKeyMapper.hidden_set_for(klass, user_id))
117
+ end
118
+ end
119
+ end
120
+ end