recommendable 0.1.8 → 0.2.1.1
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.
- data/CHANGELOG.markdown +27 -1
- data/Gemfile.lock +18 -10
- data/app/models/recommendable/dislike.rb +4 -0
- data/app/models/recommendable/ignore.rb +6 -3
- data/app/models/recommendable/like.rb +4 -0
- data/app/models/recommendable/{stashed_item.rb → stash.rb} +5 -2
- data/db/migrate/20120127092558_create_ignores.rb +4 -4
- data/db/migrate/20120131173909_create_stashes.rb +17 -0
- data/lib/recommendable.rb +5 -0
- data/lib/recommendable/acts_as_recommendable.rb +20 -9
- data/lib/recommendable/acts_as_recommended_to.rb +60 -26
- data/lib/recommendable/helpers.rb +8 -0
- data/lib/recommendable/version.rb +1 -1
- data/spec/dummy/db/migrate/20120128005555_create_ignores.recommendable.rb +4 -4
- data/spec/dummy/db/migrate/20120131195416_create_stashes.recommendable.rb +19 -0
- data/spec/dummy/db/schema.rb +11 -11
- data/spec/dummy/recommendable_dummy_development +0 -0
- data/spec/dummy/recommendable_dummy_test +0 -0
- data/spec/models/{stashed_item_spec.rb → stash_spec.rb} +3 -3
- metadata +85 -29
- data/db/migrate/20120131173909_create_stashed_items.rb +0 -17
- data/spec/dummy/db/migrate/20120131195416_create_stashed_items.recommendable.rb +0 -18
data/CHANGELOG.markdown
CHANGED
@@ -1,8 +1,34 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
-
0.
|
4
|
+
0.2.0 (current version)
|
5
5
|
-----------------------
|
6
|
+
* NOTE: This release is NOT backwards compatible. Please migrate your databases:
|
7
|
+
|
8
|
+
```
|
9
|
+
rename_column :recommendable_ignores, :ignoreable_id, :ignorable_id
|
10
|
+
rename_column :recommendable_ignores, :ignoreable_type, :ignorable_type
|
11
|
+
rename_table :recommendable_stashed_items, :recommendable_stashes
|
12
|
+
```
|
13
|
+
|
14
|
+
* Fix an issue with recommendable models implemented via STI
|
15
|
+
* Fix a library-wide typo of "ignoreable" to "ignorable"
|
16
|
+
|
17
|
+
0.1.9
|
18
|
+
-----
|
19
|
+
* Yanked due to breaking bug
|
20
|
+
|
21
|
+
0.1.8
|
22
|
+
-----
|
23
|
+
* Revert changes made in 0.1.7 due to licensing
|
24
|
+
|
25
|
+
0.1.7
|
26
|
+
-----
|
27
|
+
* Yanked and no longer available.
|
28
|
+
* Attempted switch from Resque to Sidekiq
|
29
|
+
|
30
|
+
0.1.6
|
31
|
+
-----
|
6
32
|
* Dynamic finders for your User class:
|
7
33
|
|
8
34
|
`current_user.liked_movies`
|
data/Gemfile.lock
CHANGED
@@ -30,8 +30,6 @@ GEM
|
|
30
30
|
multi_json (~> 1.0)
|
31
31
|
arel (3.0.0)
|
32
32
|
builder (3.0.0)
|
33
|
-
celluloid (0.10.0)
|
34
|
-
connection_pool (0.9.1)
|
35
33
|
erubis (2.7.0)
|
36
34
|
hike (1.2.1)
|
37
35
|
i18n (0.6.0)
|
@@ -50,6 +48,8 @@ GEM
|
|
50
48
|
rack (1.4.0)
|
51
49
|
rack-cache (1.1)
|
52
50
|
rack (>= 0.4)
|
51
|
+
rack-protection (1.2.0)
|
52
|
+
rack
|
53
53
|
rack-ssl (1.3.2)
|
54
54
|
rack
|
55
55
|
rack-test (0.6.1)
|
@@ -73,15 +73,20 @@ GEM
|
|
73
73
|
rdoc (3.12)
|
74
74
|
json (~> 1.4)
|
75
75
|
redis (2.2.2)
|
76
|
-
redis-namespace (1.
|
76
|
+
redis-namespace (1.0.3)
|
77
77
|
redis (< 3.0.0)
|
78
|
+
resque (1.19.0)
|
79
|
+
multi_json (~> 1.0)
|
80
|
+
redis-namespace (~> 1.0.2)
|
81
|
+
sinatra (>= 0.9.2)
|
82
|
+
vegas (~> 0.1.2)
|
83
|
+
resque-loner (1.2.0)
|
84
|
+
resque (~> 1.0)
|
78
85
|
shoulda (2.11.3)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
redis
|
84
|
-
redis-namespace
|
86
|
+
sinatra (1.3.2)
|
87
|
+
rack (~> 1.3, >= 1.3.6)
|
88
|
+
rack-protection (~> 1.2)
|
89
|
+
tilt (~> 1.3, >= 1.3.3)
|
85
90
|
sprockets (2.1.2)
|
86
91
|
hike (~> 1.2)
|
87
92
|
rack (~> 1.0)
|
@@ -93,6 +98,8 @@ GEM
|
|
93
98
|
polyglot
|
94
99
|
polyglot (>= 0.3.1)
|
95
100
|
tzinfo (0.3.31)
|
101
|
+
vegas (0.1.11)
|
102
|
+
rack (>= 1.0.0)
|
96
103
|
yard (0.6.8)
|
97
104
|
|
98
105
|
PLATFORMS
|
@@ -104,7 +111,8 @@ DEPENDENCIES
|
|
104
111
|
minitest
|
105
112
|
rails (>= 3.1.0)
|
106
113
|
redis (~> 2.2.0)
|
114
|
+
resque (~> 1.19.0)
|
115
|
+
resque-loner (~> 1.2.0)
|
107
116
|
shoulda
|
108
|
-
sidekiq
|
109
117
|
sqlite3
|
110
118
|
yard (~> 0.6.0)
|
@@ -8,5 +8,9 @@ module Recommendable
|
|
8
8
|
|
9
9
|
validates :user_id, :uniqueness => { :scope => [:dislikeable_id, :dislikeable_type],
|
10
10
|
:message => "has already disliked this item" }
|
11
|
+
|
12
|
+
def dislikeable_type=(sType)
|
13
|
+
super sType.to_s.classify.constantize.base_class.to_s
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
module Recommendable
|
2
2
|
class Ignore < ActiveRecord::Base
|
3
3
|
self.table_name = 'recommendable_ignores'
|
4
|
-
attr_accessible :user_id, :
|
4
|
+
attr_accessible :user_id, :ignorable_id, :ignorable_type
|
5
5
|
|
6
6
|
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
7
|
-
belongs_to :
|
7
|
+
belongs_to :ignorable, :polymorphic => true
|
8
8
|
|
9
|
-
validates :user_id, :uniqueness => { :scope => [:
|
9
|
+
validates :user_id, :uniqueness => { :scope => [:ignorable_id, :ignorable_type],
|
10
10
|
:message => "has already ignored this item" }
|
11
|
+
def ignorable_type=(sType)
|
12
|
+
super sType.to_s.classify.constantize.base_class.to_s
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Recommendable
|
2
|
-
class
|
3
|
-
self.table_name = '
|
2
|
+
class Stash < ActiveRecord::Base
|
3
|
+
self.table_name = 'recommendable_stashes'
|
4
4
|
attr_accessible :user_id, :stashable_id, :stashable_type
|
5
5
|
|
6
6
|
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
@@ -8,5 +8,8 @@ module Recommendable
|
|
8
8
|
|
9
9
|
validates :user_id, :uniqueness => { :scope => [:stashable_id, :stashable_type],
|
10
10
|
:message => "has already stashed this item" }
|
11
|
+
def stashable_type=(sType)
|
12
|
+
super sType.to_s.classify.constantize.base_class.to_s
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
@@ -2,13 +2,13 @@ class CreateIgnores < ActiveRecord::Migration
|
|
2
2
|
def up
|
3
3
|
create_table :recommendable_ignores do |t|
|
4
4
|
t.references :user
|
5
|
-
t.references :
|
5
|
+
t.references :ignorable, :polymorphic => true
|
6
6
|
t.timestamps
|
7
7
|
end
|
8
8
|
|
9
|
-
add_index :recommendable_ignores, :
|
10
|
-
add_index :recommendable_ignores, :
|
11
|
-
add_index :recommendable_ignores, [:user_id, :
|
9
|
+
add_index :recommendable_ignores, :ignorable_id
|
10
|
+
add_index :recommendable_ignores, :ignorable_type
|
11
|
+
add_index :recommendable_ignores, [:user_id, :ignorable_id, :ignorable_type], :unique => true, :name => "user_ignore_constraint"
|
12
12
|
end
|
13
13
|
|
14
14
|
def down
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateStashes < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :recommendable_stashes do |t|
|
4
|
+
t.references :user
|
5
|
+
t.references :stashable, :polymorphic => true
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :recommendable_stashes, :stashable_id
|
10
|
+
add_index :recommendable_stashes, :stashable_type
|
11
|
+
add_index :recommendable_stashes, [:user_id, :stashable_id, :stashable_type], :unique => true, :name => "user_stashed_constraint"
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
drop_table :recommendable_stashes
|
16
|
+
end
|
17
|
+
end
|
data/lib/recommendable.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'recommendable/engine'
|
2
|
+
require 'recommendable/helpers'
|
2
3
|
require 'recommendable/acts_as_recommended_to'
|
3
4
|
require 'recommendable/acts_as_recommendable'
|
4
5
|
require 'recommendable/exceptions'
|
@@ -12,4 +13,8 @@ module Recommendable
|
|
12
13
|
def self.recommendable_classes
|
13
14
|
@@recommendable_classes ||= []
|
14
15
|
end
|
16
|
+
|
17
|
+
def self.enqueue(user_id)
|
18
|
+
Resque.enqueue RecommendationRefresher, user_id
|
19
|
+
end
|
15
20
|
end
|
@@ -7,12 +7,19 @@ module Recommendable
|
|
7
7
|
class_eval do
|
8
8
|
Recommendable.recommendable_classes << self
|
9
9
|
|
10
|
-
has_many :likes,
|
11
|
-
|
12
|
-
has_many :
|
13
|
-
|
14
|
-
has_many :
|
15
|
-
|
10
|
+
has_many :likes, :as => :likeable, :dependent => :destroy,
|
11
|
+
:class_name => "Recommendable::Like"
|
12
|
+
has_many :dislikes, :as => :dislikeable, :dependent => :destroy,
|
13
|
+
:class_name => "Recommendable::Dislike"
|
14
|
+
has_many :ignores, :as => :ignorable, :dependent => :destroy,
|
15
|
+
:class_name => "Recommendable::Ignore"
|
16
|
+
has_many :stashes, :as => :stashable, :dependent => :destroy,
|
17
|
+
:class_name => "Recommendable::Stash"
|
18
|
+
|
19
|
+
has_many :liked_by, :through => :likes, :source => :user, :foreign_key => :user_id,
|
20
|
+
:class_name => Recommendable.user_class.to_s
|
21
|
+
has_many :disliked_by, :through => :dislikes, :source => :user, :foreign_key => :user_id,
|
22
|
+
:class_name => Recommendable.user_class.to_s
|
16
23
|
|
17
24
|
include LikeableMethods
|
18
25
|
include DislikeableMethods
|
@@ -72,8 +79,8 @@ module Recommendable
|
|
72
79
|
# {#create_recommendable_sets}
|
73
80
|
# @private
|
74
81
|
def destroy_recommendable_sets
|
75
|
-
Recommendable.redis.del "#{self.class}:#{id}:liked_by"
|
76
|
-
Recommendable.redis.del "#{self.class}:#{id}:disliked_by"
|
82
|
+
Recommendable.redis.del "#{self.class.base_class}:#{id}:liked_by"
|
83
|
+
Recommendable.redis.del "#{self.class.base_class}:#{id}:disliked_by"
|
77
84
|
end
|
78
85
|
|
79
86
|
# Returns an array of IDs of users that have liked or disliked this item.
|
@@ -92,12 +99,16 @@ module Recommendable
|
|
92
99
|
end
|
93
100
|
|
94
101
|
def acts_as_recommendable?() false end
|
102
|
+
|
103
|
+
def sti?() self.base_class != self end
|
104
|
+
|
105
|
+
private
|
95
106
|
end
|
96
107
|
|
97
108
|
# Instance methods.
|
98
109
|
def recommendable?() self.class.acts_as_recommendable? end
|
99
110
|
|
100
|
-
def redis_key() "#{self.class}:#{id}" end
|
111
|
+
def redis_key() "#{self.class.base_class}:#{id}" end
|
101
112
|
|
102
113
|
protected :redis_key
|
103
114
|
|
@@ -2,6 +2,7 @@ require 'active_support/concern'
|
|
2
2
|
|
3
3
|
module Recommendable
|
4
4
|
module ActsAsRecommendedTo
|
5
|
+
include Recommendable::Helpers
|
5
6
|
extend ActiveSupport::Concern
|
6
7
|
|
7
8
|
module ClassMethods
|
@@ -17,7 +18,7 @@ module Recommendable
|
|
17
18
|
has_many :likes, :class_name => "Recommendable::Like", :dependent => :destroy, :foreign_key => :user_id
|
18
19
|
has_many :dislikes, :class_name => "Recommendable::Dislike", :dependent => :destroy, :foreign_key => :user_id
|
19
20
|
has_many :ignores, :class_name => "Recommendable::Ignore", :dependent => :destroy, :foreign_key => :user_id
|
20
|
-
has_many :stashed_items, :class_name => "Recommendable::
|
21
|
+
has_many :stashed_items, :class_name => "Recommendable::Stash", :dependent => :destroy, :foreign_key => :user_id
|
21
22
|
|
22
23
|
include LikeMethods
|
23
24
|
include DislikeMethods
|
@@ -81,9 +82,9 @@ module Recommendable
|
|
81
82
|
raise RecordNotRecommendableError unless object.recommendable?
|
82
83
|
return if likes? object
|
83
84
|
completely_unrecommend object
|
84
|
-
likes.create! :likeable_id => object.id, :likeable_type => object.class
|
85
|
+
likes.create! :likeable_id => object.id, :likeable_type => object.class
|
85
86
|
object.send :update_score
|
86
|
-
|
87
|
+
Recommendable.enqueue self.id
|
87
88
|
true
|
88
89
|
end
|
89
90
|
|
@@ -92,7 +93,7 @@ module Recommendable
|
|
92
93
|
# @param [Object] object the object you want to check
|
93
94
|
# @return true if self likes object, false if not
|
94
95
|
def likes? object
|
95
|
-
likes.exists? :likeable_id => object.id, :likeable_type => object.class.to_s
|
96
|
+
likes.exists? :likeable_id => object.id, :likeable_type => object.class.base_class.to_s
|
96
97
|
end
|
97
98
|
|
98
99
|
# Destroys a Recommendable::Like currently associating self with object
|
@@ -100,9 +101,9 @@ module Recommendable
|
|
100
101
|
# @param [Object] object the object you want to remove from self's likes
|
101
102
|
# @return true if object is unliked, nil if nothing happened
|
102
103
|
def unlike object
|
103
|
-
if likes.where(:likeable_id => object.id, :likeable_type => object.class.to_s).first.try(:destroy)
|
104
|
+
if likes.where(:likeable_id => object.id, :likeable_type => object.class.base_class.to_s).first.try(:destroy)
|
104
105
|
object.send :update_score
|
105
|
-
|
106
|
+
Recommendable.enqueue self.id
|
106
107
|
true
|
107
108
|
end
|
108
109
|
end
|
@@ -122,7 +123,13 @@ module Recommendable
|
|
122
123
|
# @param [Class, String, Symbol] klass the class of records. Can be the class constant, or a String/Symbol representation of the class name.
|
123
124
|
# @return [Array] an array of ActiveRecord objects that self has liked belonging to klass
|
124
125
|
def liked_for klass
|
125
|
-
|
126
|
+
liked = if klass.sti?
|
127
|
+
likes.joins manual_join(klass, 'like')
|
128
|
+
else
|
129
|
+
likes.where(:likeable_type => klass.to_s).includes(:likeable)
|
130
|
+
end
|
131
|
+
|
132
|
+
liked.map(&:likeable)
|
126
133
|
end
|
127
134
|
|
128
135
|
# Get a list of Recommendable::Likes with a `#likeable_type` of the passed
|
@@ -132,7 +139,11 @@ module Recommendable
|
|
132
139
|
# @note You should not need to use this method. (see {#liked_for})
|
133
140
|
# @private
|
134
141
|
def likes_for klass
|
135
|
-
|
142
|
+
if klass.sti?
|
143
|
+
likes.joins manual_join(klass, 'like')
|
144
|
+
else
|
145
|
+
likes.where(:likeable_type => klass.to_s)
|
146
|
+
end
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
@@ -149,9 +160,9 @@ module Recommendable
|
|
149
160
|
raise RecordNotRecommendableError unless object.recommendable?
|
150
161
|
return if dislikes? object
|
151
162
|
completely_unrecommend object
|
152
|
-
dislikes.create! :dislikeable_id => object.id, :dislikeable_type => object.class
|
163
|
+
dislikes.create! :dislikeable_id => object.id, :dislikeable_type => object.class
|
153
164
|
object.send :update_score
|
154
|
-
|
165
|
+
Recommendable.enqueue self.id
|
155
166
|
true
|
156
167
|
end
|
157
168
|
|
@@ -160,7 +171,7 @@ module Recommendable
|
|
160
171
|
# @param [Object] object the object you want to check
|
161
172
|
# @return true if self dislikes object, false if not
|
162
173
|
def dislikes? object
|
163
|
-
dislikes.exists? :dislikeable_id => object.id, :dislikeable_type => object.class.to_s
|
174
|
+
dislikes.exists? :dislikeable_id => object.id, :dislikeable_type => object.class.base_class.to_s
|
164
175
|
end
|
165
176
|
|
166
177
|
# Destroys a Recommendable::Dislike currently associating self with object
|
@@ -168,9 +179,9 @@ module Recommendable
|
|
168
179
|
# @param [Object] object the object you want to remove from self's dislikes
|
169
180
|
# @return true if object is removed from self's dislikes, nil if nothing happened
|
170
181
|
def undislike object
|
171
|
-
if dislikes.where(:dislikeable_id => object.id, :dislikeable_type => object.class.to_s).first.try(:destroy)
|
182
|
+
if dislikes.where(:dislikeable_id => object.id, :dislikeable_type => object.class.base_class.to_s).first.try(:destroy)
|
172
183
|
object.send :update_score
|
173
|
-
|
184
|
+
Recommendable.enqueue self.id
|
174
185
|
true
|
175
186
|
end
|
176
187
|
end
|
@@ -190,7 +201,13 @@ module Recommendable
|
|
190
201
|
# @param [Class, String, Symbol] klass the class of records. Can be the class constant, or a String/Symbol representation of the class name.
|
191
202
|
# @return [Array] an array of ActiveRecord objects that self has disliked belonging to klass
|
192
203
|
def disliked_for klass
|
193
|
-
|
204
|
+
disliked = if klass.sti?
|
205
|
+
dislikes.joins manual_join(klass, 'dislike')
|
206
|
+
else
|
207
|
+
dislikes.where(:dislikeable_type => klass.to_s).includes(:dislikeable)
|
208
|
+
end
|
209
|
+
|
210
|
+
disliked.map(&:dislikeable)
|
194
211
|
end
|
195
212
|
|
196
213
|
# Get a list of Recommendable::Dislikes with a `#dislikeable_type` of the
|
@@ -200,12 +217,16 @@ module Recommendable
|
|
200
217
|
# @note You should not need to use this method. (see {#disliked_for})
|
201
218
|
# @private
|
202
219
|
def dislikes_for klass
|
203
|
-
|
220
|
+
if klass.sti?
|
221
|
+
dislikes.joins manual_join(klass, 'dislike')
|
222
|
+
else
|
223
|
+
dislikes.where(:dislikeable_type => klass.to_s)
|
224
|
+
end
|
204
225
|
end
|
205
226
|
end
|
206
227
|
|
207
228
|
module StashMethods
|
208
|
-
# Creates a Recommendable::
|
229
|
+
# Creates a Recommendable::Stash to associate self to a passed object.
|
209
230
|
# This will remove the item from this user's recommendations.
|
210
231
|
# If self is currently found to have liked or disliked the object, nothing
|
211
232
|
# will happen. It will, however, be unignored.
|
@@ -218,7 +239,7 @@ module Recommendable
|
|
218
239
|
return if rated?(object) || stashed?(object)
|
219
240
|
unignore object
|
220
241
|
unpredict object
|
221
|
-
stashed_items.create! :stashable_id => object.id, :stashable_type => object.class
|
242
|
+
stashed_items.create! :stashable_id => object.id, :stashable_type => object.class
|
222
243
|
true
|
223
244
|
end
|
224
245
|
|
@@ -227,15 +248,15 @@ module Recommendable
|
|
227
248
|
# @param [Object] object the object you want to check
|
228
249
|
# @return true if self has stashed object, false if not
|
229
250
|
def stashed? object
|
230
|
-
stashed_items.exists? :stashable_id => object.id, :stashable_type => object.class.to_s
|
251
|
+
stashed_items.exists? :stashable_id => object.id, :stashable_type => object.class.base_class.to_s
|
231
252
|
end
|
232
253
|
|
233
|
-
# Destroys a Recommendable::
|
254
|
+
# Destroys a Recommendable::Stash currently associating self with object
|
234
255
|
#
|
235
256
|
# @param [Object] object the object you want to remove from self's stash
|
236
257
|
# @return true if object is stashed, nil if nothing happened
|
237
258
|
def unstash object
|
238
|
-
true if stashed_items.where(:stashable_id => object.id, :stashable_type => object.class.to_s).first.try(:destroy)
|
259
|
+
true if stashed_items.where(:stashable_id => object.id, :stashable_type => object.class.base_class.to_s).first.try(:destroy)
|
239
260
|
end
|
240
261
|
|
241
262
|
# Get a list of records that self has currently stashed for later
|
@@ -253,7 +274,13 @@ module Recommendable
|
|
253
274
|
# @param [Class, String, Symbol] klass the class of records. Can be the class constant, or a String/Symbol representation of the class name.
|
254
275
|
# @return [Array] an array of ActiveRecord objects that self has stashed belonging to klass
|
255
276
|
def stashed_for klass
|
256
|
-
|
277
|
+
stashed = if klass.sti?
|
278
|
+
stashed_items.joins manual_join(klass, 'stash')
|
279
|
+
else
|
280
|
+
stashed_items.where(:stashable_type => klass.to_s).includes(:stashable)
|
281
|
+
end
|
282
|
+
|
283
|
+
stashed.map(&:stashable)
|
257
284
|
end
|
258
285
|
end
|
259
286
|
|
@@ -270,7 +297,7 @@ module Recommendable
|
|
270
297
|
raise RecordNotRecommendableError unless object.recommendable?
|
271
298
|
return if ignored? object
|
272
299
|
completely_unrecommend object
|
273
|
-
ignores.create! :
|
300
|
+
ignores.create! :ignorable_id => object.id, :ignorable_type => object.class
|
274
301
|
true
|
275
302
|
end
|
276
303
|
|
@@ -279,7 +306,7 @@ module Recommendable
|
|
279
306
|
# @param [Object] object the object you want to check
|
280
307
|
# @return true if self has ignored object, false if not
|
281
308
|
def ignored? object
|
282
|
-
ignores.exists? :
|
309
|
+
ignores.exists? :ignorable_id => object.id, :ignorable_type => object.class.base_class.to_s
|
283
310
|
end
|
284
311
|
|
285
312
|
# Destroys a Recommendable::Ignore currently associating self with object
|
@@ -287,7 +314,7 @@ module Recommendable
|
|
287
314
|
# @param [Object] object the object you want to remove from self's ignores
|
288
315
|
# @return true if object is removed from self's ignores, nil if nothing happened
|
289
316
|
def unignore object
|
290
|
-
true if ignores.where(:
|
317
|
+
true if ignores.where(:ignorable_id => object.id, :ignorable_type => object.class.base_class.to_s).first.try(:destroy)
|
291
318
|
end
|
292
319
|
|
293
320
|
# Get a list of records that self is currently ignoring
|
@@ -305,7 +332,13 @@ module Recommendable
|
|
305
332
|
# @param [Class, String, Symbol] klass the class of records. Can be the class constant, or a String/Symbol representation of the class name.
|
306
333
|
# @return [Array] an array of ActiveRecord objects that self has ignored belonging to klass
|
307
334
|
def ignored_for klass
|
308
|
-
|
335
|
+
ignored = if klass.sti?
|
336
|
+
ignores.joins manual_join(klass, 'ignore')
|
337
|
+
else
|
338
|
+
ignores.where(:ignorable_type => klass.to_s).includes(:ignorable)
|
339
|
+
end
|
340
|
+
|
341
|
+
ignored.map(&:ignorable)
|
309
342
|
end
|
310
343
|
end
|
311
344
|
|
@@ -397,7 +430,8 @@ module Recommendable
|
|
397
430
|
# @param [Class, String, Symbol] klass the class to receive recommendations for. Can be the class constant, or a String/Symbol representation of the class name.
|
398
431
|
# @return [Array] an array of ActiveRecord objects that are recommendable
|
399
432
|
def recommended_for klass
|
400
|
-
return [] if likes_for(klass).count + dislikes_for(klass).count == 0 ||
|
433
|
+
return [] if likes_for(klass.base_class).count + dislikes_for(klass.base_class).count == 0 || \
|
434
|
+
Recommendable.redis.zcard(predictions_set_for(klass)) == 0
|
401
435
|
|
402
436
|
recommendations = []
|
403
437
|
i = 0
|
@@ -3,13 +3,13 @@ class CreateIgnores < ActiveRecord::Migration
|
|
3
3
|
def up
|
4
4
|
create_table :recommendable_ignores, :force => true do |t|
|
5
5
|
t.references :user
|
6
|
-
t.references :
|
6
|
+
t.references :ignorable, :polymorphic => true
|
7
7
|
t.timestamps
|
8
8
|
end
|
9
9
|
|
10
|
-
add_index :recommendable_ignores, :
|
11
|
-
add_index :recommendable_ignores, :
|
12
|
-
add_index :recommendable_ignores, [:user_id, :
|
10
|
+
add_index :recommendable_ignores, :ignorable_id
|
11
|
+
add_index :recommendable_ignores, :ignorable_type
|
12
|
+
add_index :recommendable_ignores, [:user_id, :ignorable_id, :ignorable_type], :unique => true, :name => "user_ignore_constraint"
|
13
13
|
end
|
14
14
|
|
15
15
|
def down
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This migration comes from recommendable (originally 20120131173909)
|
2
|
+
class CreateStashes < ActiveRecord::Migration
|
3
|
+
def up
|
4
|
+
create_table :recommendable_stashes do |t|
|
5
|
+
t.references :user
|
6
|
+
t.references :stashable, :polymorphic => true
|
7
|
+
t.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :recommendable_stashes, :stashable_id
|
11
|
+
add_index :recommendable_stashes, :stashable_type
|
12
|
+
add_index :recommendable_stashes, [:user_id, :stashable_id, :stashable_type], :unique => true, :name => "user_stashed_constraint"
|
13
|
+
end
|
14
|
+
|
15
|
+
def down
|
16
|
+
drop_table :recommendable_stashes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -46,15 +46,15 @@ ActiveRecord::Schema.define(:version => 20120131195416) do
|
|
46
46
|
|
47
47
|
create_table "recommendable_ignores", :force => true do |t|
|
48
48
|
t.integer "user_id"
|
49
|
-
t.integer "
|
50
|
-
t.string "
|
51
|
-
t.datetime "created_at",
|
52
|
-
t.datetime "updated_at",
|
49
|
+
t.integer "ignorable_id"
|
50
|
+
t.string "ignorable_type"
|
51
|
+
t.datetime "created_at", :null => false
|
52
|
+
t.datetime "updated_at", :null => false
|
53
53
|
end
|
54
54
|
|
55
|
-
add_index "recommendable_ignores", ["
|
56
|
-
add_index "recommendable_ignores", ["
|
57
|
-
add_index "recommendable_ignores", ["user_id", "
|
55
|
+
add_index "recommendable_ignores", ["ignorable_id"], :name => "index_recommendable_ignores_on_ignorable_id"
|
56
|
+
add_index "recommendable_ignores", ["ignorable_type"], :name => "index_recommendable_ignores_on_ignorable_type"
|
57
|
+
add_index "recommendable_ignores", ["user_id", "ignorable_id", "ignorable_type"], :name => "user_ignore_constraint", :unique => true
|
58
58
|
|
59
59
|
create_table "recommendable_likes", :force => true do |t|
|
60
60
|
t.integer "user_id"
|
@@ -68,7 +68,7 @@ ActiveRecord::Schema.define(:version => 20120131195416) do
|
|
68
68
|
add_index "recommendable_likes", ["likeable_type"], :name => "index_recommendable_likes_on_likeable_type"
|
69
69
|
add_index "recommendable_likes", ["user_id", "likeable_id", "likeable_type"], :name => "user_like_constraint", :unique => true
|
70
70
|
|
71
|
-
create_table "
|
71
|
+
create_table "recommendable_stashes", :force => true do |t|
|
72
72
|
t.integer "user_id"
|
73
73
|
t.integer "stashable_id"
|
74
74
|
t.string "stashable_type"
|
@@ -76,9 +76,9 @@ ActiveRecord::Schema.define(:version => 20120131195416) do
|
|
76
76
|
t.datetime "updated_at", :null => false
|
77
77
|
end
|
78
78
|
|
79
|
-
add_index "
|
80
|
-
add_index "
|
81
|
-
add_index "
|
79
|
+
add_index "recommendable_stashes", ["stashable_id"], :name => "index_recommendable_stashes_on_stashable_id"
|
80
|
+
add_index "recommendable_stashes", ["stashable_type"], :name => "index_recommendable_stashes_on_stashable_type"
|
81
|
+
add_index "recommendable_stashes", ["user_id", "stashable_id", "stashable_type"], :name => "user_stashed_constraint", :unique => true
|
82
82
|
|
83
83
|
create_table "users", :force => true do |t|
|
84
84
|
t.string "username"
|
Binary file
|
Binary file
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
describe Recommendable::
|
3
|
+
class StashSpec < MiniTest::Spec
|
4
|
+
describe Recommendable::Stash do
|
5
5
|
before :each do
|
6
6
|
@user = User.create(:username => "dave")
|
7
7
|
end
|
@@ -21,7 +21,7 @@ class StashedItemSpec < MiniTest::Spec
|
|
21
21
|
|
22
22
|
@user.stash(movie).must_equal true
|
23
23
|
@user.stash(movie).must_be_nil
|
24
|
-
Recommendable::
|
24
|
+
Recommendable::Stash.count.must_equal 1
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recommendable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.2.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sqlite3
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: minitest
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: shoulda
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: yard
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ~>
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: 0.6.0
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.6.0
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: bundler
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ~>
|
@@ -65,10 +85,15 @@ dependencies:
|
|
65
85
|
version: 1.0.0
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.0.0
|
69
94
|
- !ruby/object:Gem::Dependency
|
70
95
|
name: jeweler
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ~>
|
@@ -76,10 +101,15 @@ dependencies:
|
|
76
101
|
version: 1.6.4
|
77
102
|
type: :development
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.6.4
|
80
110
|
- !ruby/object:Gem::Dependency
|
81
111
|
name: rcov
|
82
|
-
requirement:
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
83
113
|
none: false
|
84
114
|
requirements:
|
85
115
|
- - ! '>='
|
@@ -87,10 +117,15 @@ dependencies:
|
|
87
117
|
version: '0'
|
88
118
|
type: :development
|
89
119
|
prerelease: false
|
90
|
-
version_requirements:
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
91
126
|
- !ruby/object:Gem::Dependency
|
92
127
|
name: rails
|
93
|
-
requirement:
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
94
129
|
none: false
|
95
130
|
requirements:
|
96
131
|
- - ! '>='
|
@@ -98,10 +133,15 @@ dependencies:
|
|
98
133
|
version: 3.0.0
|
99
134
|
type: :runtime
|
100
135
|
prerelease: false
|
101
|
-
version_requirements:
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 3.0.0
|
102
142
|
- !ruby/object:Gem::Dependency
|
103
143
|
name: redis
|
104
|
-
requirement:
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
105
145
|
none: false
|
106
146
|
requirements:
|
107
147
|
- - ~>
|
@@ -109,10 +149,15 @@ dependencies:
|
|
109
149
|
version: 2.2.0
|
110
150
|
type: :runtime
|
111
151
|
prerelease: false
|
112
|
-
version_requirements:
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.2.0
|
113
158
|
- !ruby/object:Gem::Dependency
|
114
159
|
name: resque
|
115
|
-
requirement:
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
116
161
|
none: false
|
117
162
|
requirements:
|
118
163
|
- - ! '>='
|
@@ -120,10 +165,15 @@ dependencies:
|
|
120
165
|
version: 1.19.0
|
121
166
|
type: :runtime
|
122
167
|
prerelease: false
|
123
|
-
version_requirements:
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.19.0
|
124
174
|
- !ruby/object:Gem::Dependency
|
125
175
|
name: resque-loner
|
126
|
-
requirement:
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
127
177
|
none: false
|
128
178
|
requirements:
|
129
179
|
- - ~>
|
@@ -131,7 +181,12 @@ dependencies:
|
|
131
181
|
version: 1.2.0
|
132
182
|
type: :runtime
|
133
183
|
prerelease: false
|
134
|
-
version_requirements:
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 1.2.0
|
135
190
|
description: Allow a model (typically User) to Like and/or Dislike models in your
|
136
191
|
app. Generate recommendations quickly using redis.
|
137
192
|
email: david@davidcelis.com
|
@@ -151,13 +206,13 @@ files:
|
|
151
206
|
- app/models/recommendable/dislike.rb
|
152
207
|
- app/models/recommendable/ignore.rb
|
153
208
|
- app/models/recommendable/like.rb
|
154
|
-
- app/models/recommendable/
|
209
|
+
- app/models/recommendable/stash.rb
|
155
210
|
- app/workers/recommendable/recommendation_refresher.rb
|
156
211
|
- config/routes.rb
|
157
212
|
- db/migrate/20120124193723_create_likes.rb
|
158
213
|
- db/migrate/20120124193728_create_dislikes.rb
|
159
214
|
- db/migrate/20120127092558_create_ignores.rb
|
160
|
-
- db/migrate/
|
215
|
+
- db/migrate/20120131173909_create_stashes.rb
|
161
216
|
- lib/generators/recommendable/USAGE
|
162
217
|
- lib/generators/recommendable/install_generator.rb
|
163
218
|
- lib/generators/recommendable/templates/initializer.rb
|
@@ -166,6 +221,7 @@ files:
|
|
166
221
|
- lib/recommendable/acts_as_recommended_to.rb
|
167
222
|
- lib/recommendable/engine.rb
|
168
223
|
- lib/recommendable/exceptions.rb
|
224
|
+
- lib/recommendable/helpers.rb
|
169
225
|
- lib/recommendable/railtie.rb
|
170
226
|
- lib/recommendable/version.rb
|
171
227
|
- lib/tasks/recommendable_tasks.rake
|
@@ -209,7 +265,7 @@ files:
|
|
209
265
|
- spec/dummy/db/migrate/20120128020413_create_movies.rb
|
210
266
|
- spec/dummy/db/migrate/20120128024632_create_php_frameworks.rb
|
211
267
|
- spec/dummy/db/migrate/20120128024804_create_bullies.rb
|
212
|
-
- spec/dummy/db/migrate/
|
268
|
+
- spec/dummy/db/migrate/20120131195416_create_stashes.recommendable.rb
|
213
269
|
- spec/dummy/db/schema.rb
|
214
270
|
- spec/dummy/lib/assets/.gitkeep
|
215
271
|
- spec/dummy/log/.gitkeep
|
@@ -225,7 +281,7 @@ files:
|
|
225
281
|
- spec/models/ignore_spec.rb
|
226
282
|
- spec/models/like_spec.rb
|
227
283
|
- spec/models/movie_spec.rb
|
228
|
-
- spec/models/
|
284
|
+
- spec/models/stash_spec.rb
|
229
285
|
- spec/models/user_benchmark_spec.rb
|
230
286
|
- spec/models/user_spec.rb
|
231
287
|
- spec/spec_helper.rb
|
@@ -249,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
305
|
version: '0'
|
250
306
|
requirements: []
|
251
307
|
rubyforge_project:
|
252
|
-
rubygems_version: 1.8.
|
308
|
+
rubygems_version: 1.8.22
|
253
309
|
signing_key:
|
254
310
|
specification_version: 3
|
255
311
|
summary: Add like-based and/or dislike-based recommendations to your app.
|
@@ -1,17 +0,0 @@
|
|
1
|
-
class CreateStashedItems < ActiveRecord::Migration
|
2
|
-
def up
|
3
|
-
create_table :recommendable_stashed_items do |t|
|
4
|
-
t.references :user
|
5
|
-
t.references :stashable, :polymorphic => true
|
6
|
-
t.timestamps
|
7
|
-
end
|
8
|
-
|
9
|
-
add_index :recommendable_stashed_items, :stashable_id
|
10
|
-
add_index :recommendable_stashed_items, :stashable_type
|
11
|
-
add_index :recommendable_stashed_items, [:user_id, :stashable_id, :stashable_type], :unique => true, :name => "user_stashed_constraint"
|
12
|
-
end
|
13
|
-
|
14
|
-
def down
|
15
|
-
drop_table :recommendable_stashed_items
|
16
|
-
end
|
17
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# This migration comes from recommendable (originally 20120131173909)
|
2
|
-
class CreateStashedItems < ActiveRecord::Migration
|
3
|
-
def up
|
4
|
-
create_table :recommendable_stashed_items do |t|
|
5
|
-
t.references :user
|
6
|
-
t.references :stashable, :polymorphic => true
|
7
|
-
t.timestamps
|
8
|
-
end
|
9
|
-
|
10
|
-
add_index :recommendable_stashed_items, :stashable_id
|
11
|
-
add_index :recommendable_stashed_items, :stashable_type
|
12
|
-
add_index :recommendable_stashed_items, [:user_id, :stashable_id, :stashable_type], :unique => true, :name => "user_stashed_constraint"
|
13
|
-
end
|
14
|
-
|
15
|
-
def down
|
16
|
-
drop_table :recommendable_stashed_items
|
17
|
-
end
|
18
|
-
end
|