recommendable 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +16 -0
- data/README.markdown +9 -3
- data/TODO +2 -3
- data/app/{jobs → workers}/recommendable/recommendation_refresher.rb +3 -2
- data/lib/recommendable/acts_as_recommendable.rb +14 -1
- data/lib/recommendable/acts_as_recommended_to.rb +34 -33
- data/lib/recommendable/railtie.rb +9 -0
- data/lib/recommendable/version.rb +1 -1
- data/recommendable.gemspec +1 -1
- data/spec/dummy/recommendable_dummy_test +0 -0
- data/spec/models/user_benchmark_spec.rb +2 -2
- metadata +26 -25
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Changelog
|
2
|
+
=========
|
3
|
+
|
4
|
+
0.1.2 (current version)
|
5
|
+
-----------------------
|
6
|
+
|
7
|
+
* Fix an issue that could cause similarity values between users to be incorrect.
|
8
|
+
* `User#common_likes_with` and `User#common_dislikes_with` now return the actual Model instances by default. Accordingly, these methods are no longer private.
|
9
|
+
|
10
|
+
0.1.1
|
11
|
+
-----
|
12
|
+
* Introduce the "stashed item" feature. This gives the ability for users to save an item in a list to try later, while at the same time removing it from their list of recommendations. This lets your users keep getting new recommendations without necessarily having to rate what they know they want to try.
|
13
|
+
|
14
|
+
0.1.0
|
15
|
+
-----
|
16
|
+
* Welcome to recommendable!
|
data/README.markdown
CHANGED
@@ -29,7 +29,9 @@ Add the following to your Rails application's `Gemfile`:
|
|
29
29
|
|
30
30
|
After your `bundle install`, you can then run:
|
31
31
|
|
32
|
-
|
32
|
+
``` bash
|
33
|
+
$ rails g recommendable:install (--user-class=User)
|
34
|
+
```
|
33
35
|
|
34
36
|
After running the installation generator, you should double check
|
35
37
|
`config/initializers/recommendable.rb` for options on configuring your Redis
|
@@ -42,7 +44,9 @@ to wait. In fact, you can run multiple resque workers if you wish.
|
|
42
44
|
|
43
45
|
Assuming you have `redis-server` running...
|
44
46
|
|
45
|
-
|
47
|
+
``` bash
|
48
|
+
$ QUEUE=recommendable rake environment resque:work
|
49
|
+
```
|
46
50
|
|
47
51
|
You can run this command multiple times if you wish to start more than one
|
48
52
|
worker. This is the standard rake task for starting a Resque worker so, for
|
@@ -194,6 +198,8 @@ Movie.find_by_title('Star Wars: Episode I - The Phantom Menace').disliked_by
|
|
194
198
|
#=> [#<User username: 'davidcelis'>]
|
195
199
|
current_user.common_likes_with(friend)
|
196
200
|
#=> [#<Movie title: '2001: A Space Odyssey', year: 1968>]
|
201
|
+
current_user.common_likes_with(friend, :class => Show)
|
202
|
+
#=> []
|
197
203
|
```
|
198
204
|
|
199
205
|
`common_dislikes_with` and `disagreements_with` are available for similar use.
|
@@ -322,7 +328,7 @@ Links
|
|
322
328
|
-----
|
323
329
|
* Code: `git clone git://github.com/davidcelis/recommendable.git`
|
324
330
|
* Home: <http://github.com/davidcelis/recommendable>
|
325
|
-
* Docs: <http://rubydoc.info/gems/recommendable/
|
331
|
+
* Docs: <http://rubydoc.info/gems/recommendable/frames>
|
326
332
|
* Bugs: <http://github.com/davidcelis/recommendable/issues>
|
327
333
|
* Gems: <http://rubygems.org/gems/recommendable>
|
328
334
|
|
data/TODO
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
= TODO
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
* Make public methods to return likes/dislikes that are common between two users
|
3
|
+
* Recalculate similarity values ONLY for users that have liked/disliked the item
|
4
|
+
that self just liked/disliked
|
6
5
|
* Allow the option NOT to queue up on like/dislike/ignore?
|
@@ -3,9 +3,10 @@ module Recommendable
|
|
3
3
|
include Resque::Plugins::UniqueJob
|
4
4
|
@queue = :recommendable
|
5
5
|
|
6
|
-
def self.perform(user_id)
|
6
|
+
def self.perform(user_id, other_ids)
|
7
7
|
user = Recommendable.user_class.find(user_id)
|
8
|
-
|
8
|
+
return if other_ids.empty?
|
9
|
+
user.send :update_similarities, other_ids
|
9
10
|
user.send :update_recommendations
|
10
11
|
end
|
11
12
|
end
|
@@ -22,6 +22,19 @@ module Recommendable
|
|
22
22
|
def has_been_rated?
|
23
23
|
likes.count + dislikes.count > 0
|
24
24
|
end
|
25
|
+
|
26
|
+
# Returns an array of users that have liked or disliked this item.
|
27
|
+
# @return [Array] an array of users
|
28
|
+
def rated_by
|
29
|
+
liked_by + disliked_by
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns an array of IDs of users that have liked or disliked this item.
|
33
|
+
# @return [Array] an array of user IDs
|
34
|
+
# @private
|
35
|
+
def rates_by
|
36
|
+
likes.map(&:user_id) + dislikes.map(&:user_id)
|
37
|
+
end
|
25
38
|
|
26
39
|
# Used for setup purposes. Calls convenience methods to create sets
|
27
40
|
# in redis of users that both like and dislike this object.
|
@@ -39,7 +52,7 @@ module Recommendable
|
|
39
52
|
Recommendable.redis.del "#{self.class}:#{id}:disliked_by"
|
40
53
|
end
|
41
54
|
|
42
|
-
private :likes, :dislikes, :ignores, :stashes,
|
55
|
+
private :likes, :dislikes, :ignores, :stashes, :rates_by,
|
43
56
|
:create_recommendable_sets, :destroy_recommendable_sets
|
44
57
|
end
|
45
58
|
end
|
@@ -42,12 +42,9 @@ module Recommendable
|
|
42
42
|
def like(object)
|
43
43
|
raise RecordNotRecommendableError unless object.recommendable?
|
44
44
|
return if likes?(object)
|
45
|
-
|
46
|
-
unstash(object)
|
47
|
-
unignore(object)
|
48
|
-
unpredict(object)
|
45
|
+
completely_unrecommend(object)
|
49
46
|
likes.create!(:likeable_id => object.id, :likeable_type => object.class.to_s)
|
50
|
-
Resque.enqueue RecommendationRefresher, self.id
|
47
|
+
Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
|
51
48
|
true
|
52
49
|
end
|
53
50
|
|
@@ -65,7 +62,7 @@ module Recommendable
|
|
65
62
|
# @return true if object is unliked, nil if nothing happened
|
66
63
|
def unlike(object)
|
67
64
|
if likes.where(:likeable_id => object.id, :likeable_type => object.class.to_s).first.try(:destroy)
|
68
|
-
Resque.enqueue RecommendationRefresher, self.id
|
65
|
+
Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
|
69
66
|
true
|
70
67
|
end
|
71
68
|
end
|
@@ -114,12 +111,9 @@ module Recommendable
|
|
114
111
|
def dislike(object)
|
115
112
|
raise RecordNotRecommendableError unless object.recommendable?
|
116
113
|
return if dislikes?(object)
|
117
|
-
|
118
|
-
unstash(object)
|
119
|
-
unignore(object)
|
120
|
-
unpredict(object)
|
114
|
+
completely_unrecommend(object)
|
121
115
|
dislikes.create!(:dislikeable_id => object.id, :dislikeable_type => object.class.to_s)
|
122
|
-
Resque.enqueue RecommendationRefresher, self.id
|
116
|
+
Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
|
123
117
|
true
|
124
118
|
end
|
125
119
|
|
@@ -137,7 +131,7 @@ module Recommendable
|
|
137
131
|
# @return true if object is removed from self's dislikes, nil if nothing happened
|
138
132
|
def undislike(object)
|
139
133
|
if dislikes.where(:dislikeable_id => object.id, :dislikeable_type => object.class.to_s).first.try(:destroy)
|
140
|
-
Resque.enqueue RecommendationRefresher, self.id
|
134
|
+
Resque.enqueue RecommendationRefresher, self.id, object.send(:rates_by)
|
141
135
|
true
|
142
136
|
end
|
143
137
|
end
|
@@ -252,10 +246,7 @@ module Recommendable
|
|
252
246
|
def ignore(object)
|
253
247
|
raise RecordNotRecommendableError unless object.recommendable?
|
254
248
|
return if has_ignored?(object)
|
255
|
-
|
256
|
-
undislike(object)
|
257
|
-
unstash(object)
|
258
|
-
unpredict(object)
|
249
|
+
completely_unrecommend(object)
|
259
250
|
ignores.create!(:ignoreable_id => object.id, :ignoreable_type => object.class.to_s)
|
260
251
|
true
|
261
252
|
end
|
@@ -309,10 +300,6 @@ module Recommendable
|
|
309
300
|
end
|
310
301
|
|
311
302
|
module RecommendationMethods
|
312
|
-
def self.acts_as_recommended_to? ; true ; end
|
313
|
-
|
314
|
-
def can_receive_recommendations? ; self.class.acts_as_recommended_to? ; end
|
315
|
-
|
316
303
|
# Checks to see if self has already liked or disliked a passed object.
|
317
304
|
#
|
318
305
|
# @param [Object] object the object you want to check
|
@@ -362,15 +349,13 @@ module Recommendable
|
|
362
349
|
|
363
350
|
unioned_predictions = "#{self.class}:#{id}:predictions"
|
364
351
|
Recommendable.redis.zunionstore unioned_predictions, Recommendable.recommendable_classes.map {|klass| predictions_set_for(klass)}
|
365
|
-
return [] if Recommendable.redis.zcard(unioned_predictions) == 0
|
366
352
|
|
367
353
|
recommendations = Recommendable.redis.zrevrange(unioned_predictions, 0, options[:count]).map do |object|
|
368
354
|
klass, id = object.split(":")
|
369
355
|
klass.constantize.find(id)
|
370
356
|
end
|
371
357
|
|
372
|
-
Recommendable.redis.del
|
373
|
-
return recommendations
|
358
|
+
Recommendable.redis.del(unioned_predictions) and return recommendations
|
374
359
|
end
|
375
360
|
|
376
361
|
# Get a list of recommendations for self on a single recommendable type.
|
@@ -385,16 +370,16 @@ module Recommendable
|
|
385
370
|
defaults = { :count => 10 }
|
386
371
|
options = defaults.merge options
|
387
372
|
|
388
|
-
|
389
|
-
return recommendations if likes_for(klass).count + dislikes_for(klass).count == 0 || Recommendable.redis.zcard(predictions_set_for(klass)) == 0
|
373
|
+
return [] if likes_for(klass).count + dislikes_for(klass).count == 0 || Recommendable.redis.zcard(predictions_set_for(klass)) == 0
|
390
374
|
|
375
|
+
recommendations = []
|
391
376
|
i = 0
|
392
377
|
until recommendations.size == options[:count]
|
393
378
|
prediction = Recommendable.redis.zrevrange(predictions_set_for(klass), i, i).first
|
394
379
|
return recommendations unless prediction # User might not have enough recommendations to return
|
395
380
|
|
396
381
|
object = klassify(klass).find(prediction.split(":")[1])
|
397
|
-
recommendations << object
|
382
|
+
recommendations << object
|
398
383
|
i += 1
|
399
384
|
end
|
400
385
|
|
@@ -414,7 +399,7 @@ module Recommendable
|
|
414
399
|
#
|
415
400
|
# @param [Object] object the object to fetch the probability for
|
416
401
|
# @return [Float] the likelihood of self disliking the passed object
|
417
|
-
# @see #
|
402
|
+
# @see {#probability_of_liking}
|
418
403
|
def probability_of_disliking(object)
|
419
404
|
-probability_of_liking(object)
|
420
405
|
end
|
@@ -434,7 +419,7 @@ module Recommendable
|
|
434
419
|
rater.create_recommended_to_sets
|
435
420
|
agreements = common_likes_with(rater, :return_records => false).size
|
436
421
|
agreements += common_dislikes_with(rater, :return_records => false).size
|
437
|
-
disagreements = disagreements_with(rater).size
|
422
|
+
disagreements = disagreements_with(rater, :return_records => false).size
|
438
423
|
|
439
424
|
similarity = (agreements - disagreements).to_f / (likes.count + dislikes.count)
|
440
425
|
rater.destroy_recommended_to_sets
|
@@ -462,7 +447,7 @@ module Recommendable
|
|
462
447
|
in_common = Recommendable.redis.sinter(likes_set_for(klass), rater.likes_set_for(klass))
|
463
448
|
|
464
449
|
if options[:return_records]
|
465
|
-
klassify(klass).find in_common
|
450
|
+
klassify(klass).find in_common
|
466
451
|
else
|
467
452
|
in_common.map {|id| "#{klassify(klass)}:#{id}"}
|
468
453
|
end
|
@@ -491,7 +476,7 @@ module Recommendable
|
|
491
476
|
in_common = Recommendable.redis.sinter(dislikes_set_for(klass), rater.dislikes_set_for(klass))
|
492
477
|
|
493
478
|
if options[:return_records]
|
494
|
-
klassify(klass).find in_common
|
479
|
+
klassify(klass).find in_common
|
495
480
|
else
|
496
481
|
in_common.map {|id| "#{klassify(klass)}:#{id}"}
|
497
482
|
end
|
@@ -562,13 +547,14 @@ module Recommendable
|
|
562
547
|
# other users. This is called in the Resque job to refresh recommendations.
|
563
548
|
#
|
564
549
|
# @private
|
565
|
-
def update_similarities
|
550
|
+
def update_similarities(rater_ids = nil)
|
566
551
|
return unless has_rated_anything?
|
567
552
|
create_recommended_to_sets
|
553
|
+
rater_ids ||= Recommendable.user_class.select(:id).map!(&:id)
|
568
554
|
|
569
|
-
Recommendable.user_class.
|
555
|
+
Recommendable.user_class.find(rater_ids).each do |rater|
|
570
556
|
next if self == rater || !rater.can_rate?
|
571
|
-
Recommendable.redis.zadd similarity_set, similarity_with(rater),
|
557
|
+
Recommendable.redis.zadd similarity_set, similarity_with(rater), rater.id
|
572
558
|
end
|
573
559
|
|
574
560
|
destroy_recommended_to_sets
|
@@ -644,6 +630,21 @@ module Recommendable
|
|
644
630
|
end
|
645
631
|
end
|
646
632
|
|
633
|
+
# Used internally during liking/disliking/stashing/ignoring objects. This
|
634
|
+
# will prep an object to be liked, disliked, etc. by making sure that self
|
635
|
+
# doesn't already have this item in their list of likes, dislikes, stashed
|
636
|
+
# items or ignored items.
|
637
|
+
#
|
638
|
+
# param [Object] object the object to destroy Recommendable models for
|
639
|
+
# @private
|
640
|
+
def completely_unrecommend(object)
|
641
|
+
unlike(object)
|
642
|
+
undislike(object)
|
643
|
+
unstash(object)
|
644
|
+
unignore(object)
|
645
|
+
unpredict(object)
|
646
|
+
end
|
647
|
+
|
647
648
|
protected :likes_set_for, :dislikes_set_for, :create_recommended_to_sets,
|
648
649
|
:destroy_recommended_to_sets
|
649
650
|
|
@@ -2,5 +2,14 @@ module Recommendable
|
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
ActiveRecord::Base.send :include, Recommendable::ActsAsRecommendedTo
|
4
4
|
ActiveRecord::Base.send :include, Recommendable::ActsAsRecommendable
|
5
|
+
|
6
|
+
# Force load models if in a non-development environment and not caching classes
|
7
|
+
config.after_initialize do |app|
|
8
|
+
force_load_models if !Rails.env.development? && !app.config.cache_classes
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.force_load_models
|
12
|
+
Dir["#{ Rails.root }/app/models/**/*.rb"].each { |m| load m }
|
13
|
+
end
|
5
14
|
end
|
6
15
|
end
|
data/recommendable.gemspec
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path('lib/recommendable/version')
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "recommendable"
|
6
6
|
s.version = Recommendable::VERSION
|
7
|
-
s.date =
|
7
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
8
8
|
|
9
9
|
s.authors = ["David Celis"]
|
10
10
|
s.email = "david@davidcelis.com"
|
Binary file
|
@@ -35,8 +35,8 @@ class UserBenchmarkSpec < MiniTest::Unit::TestCase
|
|
35
35
|
@users.sample.send(@actions.sample, m)
|
36
36
|
end
|
37
37
|
|
38
|
-
@user.update_similarities
|
39
|
-
@user.update_recommendations
|
38
|
+
@user.send :update_similarities
|
39
|
+
@user.send :update_recommendations
|
40
40
|
Recommendable.redis.flushdb
|
41
41
|
|
42
42
|
User.delete_all
|
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.1.3
|
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-02-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sqlite3
|
16
|
-
requirement: &
|
16
|
+
requirement: &70109049948340 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70109049948340
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: minitest
|
27
|
-
requirement: &
|
27
|
+
requirement: &70109049964200 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70109049964200
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: shoulda
|
38
|
-
requirement: &
|
38
|
+
requirement: &70109049963440 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70109049963440
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: yard
|
49
|
-
requirement: &
|
49
|
+
requirement: &70109049962620 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.6.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70109049962620
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bundler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70109049961560 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.0.0
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70109049961560
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: jeweler
|
71
|
-
requirement: &
|
71
|
+
requirement: &70109049959600 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 1.6.4
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70109049959600
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rcov
|
82
|
-
requirement: &
|
82
|
+
requirement: &70109049958960 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70109049958960
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: rails
|
93
|
-
requirement: &
|
93
|
+
requirement: &70109049957640 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: 3.0.0
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70109049957640
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: redis
|
104
|
-
requirement: &
|
104
|
+
requirement: &70109049971980 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ~>
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: 2.2.0
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70109049971980
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: resque
|
115
|
-
requirement: &
|
115
|
+
requirement: &70109049971120 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ~>
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: 1.19.0
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70109049971120
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: resque-loner
|
126
|
-
requirement: &
|
126
|
+
requirement: &70109049969980 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ~>
|
@@ -131,7 +131,7 @@ dependencies:
|
|
131
131
|
version: 1.2.0
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70109049969980
|
135
135
|
description: Allow a model (typically User) to Like and/or Dislike models in your
|
136
136
|
app. Generate recommendations quickly using redis.
|
137
137
|
email: david@davidcelis.com
|
@@ -141,17 +141,18 @@ extra_rdoc_files: []
|
|
141
141
|
files:
|
142
142
|
- .gitignore
|
143
143
|
- .travis.yml
|
144
|
+
- CHANGELOG.markdown
|
144
145
|
- Gemfile
|
145
146
|
- Gemfile.lock
|
146
147
|
- LICENSE.txt
|
147
148
|
- README.markdown
|
148
149
|
- Rakefile
|
149
150
|
- TODO
|
150
|
-
- app/jobs/recommendable/recommendation_refresher.rb
|
151
151
|
- app/models/recommendable/dislike.rb
|
152
152
|
- app/models/recommendable/ignore.rb
|
153
153
|
- app/models/recommendable/like.rb
|
154
154
|
- app/models/recommendable/stashed_item.rb
|
155
|
+
- app/workers/recommendable/recommendation_refresher.rb
|
155
156
|
- config/routes.rb
|
156
157
|
- db/migrate/20120124193723_create_likes.rb
|
157
158
|
- db/migrate/20120124193728_create_dislikes.rb
|