recommendable 0.1.2
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/.gitignore +57 -0
- data/.travis.yml +3 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +118 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +340 -0
- data/Rakefile +26 -0
- data/TODO +6 -0
- data/app/jobs/recommendable/recommendation_refresher.rb +12 -0
- data/app/models/recommendable/dislike.rb +9 -0
- data/app/models/recommendable/ignore.rb +9 -0
- data/app/models/recommendable/like.rb +9 -0
- data/app/models/recommendable/stashed_item.rb +9 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20120124193723_create_likes.rb +17 -0
- data/db/migrate/20120124193728_create_dislikes.rb +17 -0
- data/db/migrate/20120127092558_create_ignores.rb +17 -0
- data/db/migrate/20120131173909_create_stashed_items.rb +17 -0
- data/lib/generators/recommendable/USAGE +8 -0
- data/lib/generators/recommendable/install_generator.rb +47 -0
- data/lib/generators/recommendable/templates/initializer.rb +18 -0
- data/lib/recommendable.rb +19 -0
- data/lib/recommendable/acts_as_recommendable.rb +85 -0
- data/lib/recommendable/acts_as_recommended_to.rb +659 -0
- data/lib/recommendable/engine.rb +13 -0
- data/lib/recommendable/exceptions.rb +4 -0
- data/lib/recommendable/railtie.rb +6 -0
- data/lib/recommendable/version.rb +3 -0
- data/lib/tasks/recommendable_tasks.rake +1 -0
- data/recommendable.gemspec +32 -0
- data/script/rails +8 -0
- data/spec/configuration_spec.rb +9 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/bully.rb +2 -0
- data/spec/dummy/app/models/movie.rb +3 -0
- data/spec/dummy/app/models/php_framework.rb +2 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +56 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/recommendable.rb +16 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/migrate/20120128005553_create_likes.recommendable.rb +18 -0
- data/spec/dummy/db/migrate/20120128005554_create_dislikes.recommendable.rb +18 -0
- data/spec/dummy/db/migrate/20120128005555_create_ignores.recommendable.rb +18 -0
- data/spec/dummy/db/migrate/20120128020228_create_users.rb +9 -0
- data/spec/dummy/db/migrate/20120128020413_create_movies.rb +10 -0
- data/spec/dummy/db/migrate/20120128024632_create_php_frameworks.rb +9 -0
- data/spec/dummy/db/migrate/20120128024804_create_bullies.rb +9 -0
- data/spec/dummy/db/migrate/20120131195416_create_stashed_items.recommendable.rb +18 -0
- data/spec/dummy/db/schema.rb +89 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/recommendable_dummy_development +0 -0
- data/spec/dummy/recommendable_dummy_test +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories.rb +16 -0
- data/spec/models/dislike_spec.rb +27 -0
- data/spec/models/ignore_spec.rb +27 -0
- data/spec/models/like_spec.rb +28 -0
- data/spec/models/stashed_item_spec.rb +27 -0
- data/spec/models/user_benchmark_spec.rb +49 -0
- data/spec/models/user_spec.rb +320 -0
- data/spec/spec_helper.rb +28 -0
- metadata +254 -0
data/.gitignore
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# RVM / rbenv version files
|
2
|
+
.rvmrc
|
3
|
+
.rbenv-version
|
4
|
+
|
5
|
+
# rcov generated
|
6
|
+
coverage
|
7
|
+
|
8
|
+
# rdoc generated
|
9
|
+
rdoc
|
10
|
+
|
11
|
+
# yard generated
|
12
|
+
doc
|
13
|
+
.yardoc
|
14
|
+
|
15
|
+
# bundler
|
16
|
+
.bundle
|
17
|
+
vendor/bundle
|
18
|
+
|
19
|
+
# jeweler generated
|
20
|
+
pkg
|
21
|
+
|
22
|
+
# logging
|
23
|
+
*.log
|
24
|
+
|
25
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
26
|
+
#
|
27
|
+
# * Create a file at ~/.gitignore
|
28
|
+
# * Include files you want ignored
|
29
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
30
|
+
#
|
31
|
+
# After doing this, these files will be ignored in all your git projects,
|
32
|
+
# saving you from having to 'pollute' every project you touch with them
|
33
|
+
#
|
34
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
35
|
+
#
|
36
|
+
# For MacOS:
|
37
|
+
|
38
|
+
.DS_Store
|
39
|
+
|
40
|
+
# For TextMate
|
41
|
+
*.tmproj
|
42
|
+
tmtags
|
43
|
+
|
44
|
+
# For emacs:
|
45
|
+
#*~
|
46
|
+
#\#*
|
47
|
+
#.\#*
|
48
|
+
|
49
|
+
# For vim:
|
50
|
+
*.swp
|
51
|
+
|
52
|
+
# For redcar:
|
53
|
+
#.redcar
|
54
|
+
|
55
|
+
# For rubinius:
|
56
|
+
#*.rbc
|
57
|
+
.rake_tasks*
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
gem "rails", ">= 3.0.0"
|
4
|
+
gem "redis", "~> 2.2.0"
|
5
|
+
gem "resque", "~> 1.19.0"
|
6
|
+
gem "resque-loner", "~> 1.2.0"
|
7
|
+
|
8
|
+
# Add dependencies to develop your gem here.
|
9
|
+
# Include everything needed to run rake, tests, features, etc.
|
10
|
+
group :development do
|
11
|
+
gem "sqlite3"
|
12
|
+
gem "minitest"
|
13
|
+
gem "shoulda"
|
14
|
+
gem "miniskirt"
|
15
|
+
gem "yard", "~> 0.6.0"
|
16
|
+
gem "bundler", "~> 1.0.0"
|
17
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.2.0)
|
5
|
+
actionpack (= 3.2.0)
|
6
|
+
mail (~> 2.4.0)
|
7
|
+
actionpack (3.2.0)
|
8
|
+
activemodel (= 3.2.0)
|
9
|
+
activesupport (= 3.2.0)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
journey (~> 1.0.0)
|
13
|
+
rack (~> 1.4.0)
|
14
|
+
rack-cache (~> 1.1)
|
15
|
+
rack-test (~> 0.6.1)
|
16
|
+
sprockets (~> 2.1.2)
|
17
|
+
activemodel (3.2.0)
|
18
|
+
activesupport (= 3.2.0)
|
19
|
+
builder (~> 3.0.0)
|
20
|
+
activerecord (3.2.0)
|
21
|
+
activemodel (= 3.2.0)
|
22
|
+
activesupport (= 3.2.0)
|
23
|
+
arel (~> 3.0.0)
|
24
|
+
tzinfo (~> 0.3.29)
|
25
|
+
activeresource (3.2.0)
|
26
|
+
activemodel (= 3.2.0)
|
27
|
+
activesupport (= 3.2.0)
|
28
|
+
activesupport (3.2.0)
|
29
|
+
i18n (~> 0.6)
|
30
|
+
multi_json (~> 1.0)
|
31
|
+
arel (3.0.0)
|
32
|
+
builder (3.0.0)
|
33
|
+
erubis (2.7.0)
|
34
|
+
hike (1.2.1)
|
35
|
+
i18n (0.6.0)
|
36
|
+
journey (1.0.0)
|
37
|
+
json (1.6.5)
|
38
|
+
mail (2.4.1)
|
39
|
+
i18n (>= 0.4.0)
|
40
|
+
mime-types (~> 1.16)
|
41
|
+
treetop (~> 1.4.8)
|
42
|
+
mime-types (1.17.2)
|
43
|
+
miniskirt (1.1.1)
|
44
|
+
activesupport (>= 2.2)
|
45
|
+
minitest (2.10.1)
|
46
|
+
multi_json (1.0.4)
|
47
|
+
polyglot (0.3.3)
|
48
|
+
rack (1.4.0)
|
49
|
+
rack-cache (1.1)
|
50
|
+
rack (>= 0.4)
|
51
|
+
rack-protection (1.2.0)
|
52
|
+
rack
|
53
|
+
rack-ssl (1.3.2)
|
54
|
+
rack
|
55
|
+
rack-test (0.6.1)
|
56
|
+
rack (>= 1.0)
|
57
|
+
rails (3.2.0)
|
58
|
+
actionmailer (= 3.2.0)
|
59
|
+
actionpack (= 3.2.0)
|
60
|
+
activerecord (= 3.2.0)
|
61
|
+
activeresource (= 3.2.0)
|
62
|
+
activesupport (= 3.2.0)
|
63
|
+
bundler (~> 1.0)
|
64
|
+
railties (= 3.2.0)
|
65
|
+
railties (3.2.0)
|
66
|
+
actionpack (= 3.2.0)
|
67
|
+
activesupport (= 3.2.0)
|
68
|
+
rack-ssl (~> 1.3.2)
|
69
|
+
rake (>= 0.8.7)
|
70
|
+
rdoc (~> 3.4)
|
71
|
+
thor (~> 0.14.6)
|
72
|
+
rake (0.9.2.2)
|
73
|
+
rdoc (3.12)
|
74
|
+
json (~> 1.4)
|
75
|
+
redis (2.2.2)
|
76
|
+
redis-namespace (1.0.3)
|
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)
|
85
|
+
shoulda (2.11.3)
|
86
|
+
sinatra (1.3.2)
|
87
|
+
rack (~> 1.3, >= 1.3.6)
|
88
|
+
rack-protection (~> 1.2)
|
89
|
+
tilt (~> 1.3, >= 1.3.3)
|
90
|
+
sprockets (2.1.2)
|
91
|
+
hike (~> 1.2)
|
92
|
+
rack (~> 1.0)
|
93
|
+
tilt (~> 1.1, != 1.3.0)
|
94
|
+
sqlite3 (1.3.5)
|
95
|
+
thor (0.14.6)
|
96
|
+
tilt (1.3.3)
|
97
|
+
treetop (1.4.10)
|
98
|
+
polyglot
|
99
|
+
polyglot (>= 0.3.1)
|
100
|
+
tzinfo (0.3.31)
|
101
|
+
vegas (0.1.11)
|
102
|
+
rack (>= 1.0.0)
|
103
|
+
yard (0.6.8)
|
104
|
+
|
105
|
+
PLATFORMS
|
106
|
+
ruby
|
107
|
+
|
108
|
+
DEPENDENCIES
|
109
|
+
bundler (~> 1.0.0)
|
110
|
+
miniskirt
|
111
|
+
minitest
|
112
|
+
rails (>= 3.0.0)
|
113
|
+
redis (~> 2.2.0)
|
114
|
+
resque (~> 1.19.0)
|
115
|
+
resque-loner (~> 1.2.0)
|
116
|
+
shoulda
|
117
|
+
sqlite3
|
118
|
+
yard (~> 0.6.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 David Celis
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,340 @@
|
|
1
|
+
# recommendable [](http://travis-ci.org/davidcelis/recommendable)
|
2
|
+
|
3
|
+
Recommendable is a Rails Engine to add Like/Dislike functionality to your
|
4
|
+
application. It uses Redis to generate recommendations quickly through a
|
5
|
+
collaborative filtering algorithm that I modified myself. Your users' tastes
|
6
|
+
are compared with one another and used to give them great recommendations!
|
7
|
+
Yes, Redis is required. Scroll to the end of the README for more info on that.
|
8
|
+
|
9
|
+
Why Likes and Dislikes?
|
10
|
+
-----------------------
|
11
|
+
|
12
|
+
[I hate five-star rating scales][0].
|
13
|
+
|
14
|
+
**tl;dr:** Binary voting habits are most certainly not an odd phenomenon.
|
15
|
+
People tend to vote in only two different ways. Some folks give either 1 star
|
16
|
+
or 5 stars. Some people fluctuate between 3 and 4 stars. There are always
|
17
|
+
outliers, but what it comes down to is this: a person's binary votes indicate,
|
18
|
+
in general, a dislike or like of what they're voting on. I'm just giving the
|
19
|
+
people what they want.
|
20
|
+
|
21
|
+
Installation
|
22
|
+
------------
|
23
|
+
|
24
|
+
Add the following to your Rails application's `Gemfile`:
|
25
|
+
|
26
|
+
``` ruby
|
27
|
+
gem "recommendable"
|
28
|
+
```
|
29
|
+
|
30
|
+
After your `bundle install`, you can then run:
|
31
|
+
|
32
|
+
$ rails g recommendable:install (--user-class=User)
|
33
|
+
|
34
|
+
After running the installation generator, you should double check
|
35
|
+
`config/initializers/recommendable.rb` for options on configuring your Redis
|
36
|
+
connection.
|
37
|
+
|
38
|
+
Finally, Recommendable uses Resque to place users in a queue. Users must wait
|
39
|
+
their turn to regenerate recommendations so that your application does not get
|
40
|
+
throttled. Don't worry, though! Most of the time, your users will barely have
|
41
|
+
to wait. In fact, you can run multiple resque workers if you wish.
|
42
|
+
|
43
|
+
Assuming you have `redis-server` running...
|
44
|
+
|
45
|
+
$ QUEUE=recommendable rake environment resque:work
|
46
|
+
|
47
|
+
You can run this command multiple times if you wish to start more than one
|
48
|
+
worker. This is the standard rake task for starting a Resque worker so, for
|
49
|
+
more options on this task, head over to [defunkt/resque][1]
|
50
|
+
|
51
|
+
Usage
|
52
|
+
-----
|
53
|
+
|
54
|
+
In your Rails model that represents your application's user:
|
55
|
+
|
56
|
+
``` ruby
|
57
|
+
class User < ActiveRecord::Base
|
58
|
+
acts_as_recommended_to
|
59
|
+
|
60
|
+
# ...
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Then, from any Rails model you wish your `user` to be able to `like` or `dislike`:
|
65
|
+
|
66
|
+
``` ruby
|
67
|
+
class Movie < ActiveRecord::Base
|
68
|
+
acts_as_recommendable
|
69
|
+
|
70
|
+
# ...
|
71
|
+
end
|
72
|
+
|
73
|
+
class Show < ActiveRecord::Base
|
74
|
+
acts_as_recommendable
|
75
|
+
|
76
|
+
# ...
|
77
|
+
end
|
78
|
+
````
|
79
|
+
|
80
|
+
And that's it!
|
81
|
+
|
82
|
+
### Liking/Disliking
|
83
|
+
|
84
|
+
At this point, your user will be ready to `like` movies...
|
85
|
+
|
86
|
+
``` ruby
|
87
|
+
current_user.like Movie.create(:title => '2001: A Space Odyssey', :year => 1968)
|
88
|
+
#=> true
|
89
|
+
```
|
90
|
+
|
91
|
+
... or `dislike` them:
|
92
|
+
|
93
|
+
``` ruby
|
94
|
+
current_user.dislike Movie.create(:title => 'Star Wars: Episode I - The Phantom Menace', :year => 1999)
|
95
|
+
#=> true
|
96
|
+
```
|
97
|
+
|
98
|
+
In addition, several helpful methods are available to your user now:
|
99
|
+
|
100
|
+
``` ruby
|
101
|
+
current_user.likes? Movie.find_by_title('2001: A Space Odyssey')
|
102
|
+
#=> true
|
103
|
+
current_user.dislikes? Movie.find_by_title('Star Wars: Episode I - The Phantom Menace')
|
104
|
+
#=> true
|
105
|
+
other_movie = Movie.create('Back to the Future', :year => 1985)
|
106
|
+
current_user.dislikes? other_movie
|
107
|
+
#=> false
|
108
|
+
current_user.like other_movie
|
109
|
+
#=> true
|
110
|
+
current_user.liked
|
111
|
+
#=> [#<Movie name: '2001: A Space Odyssey', year: 1968>, #<Movie name: 'Back to the Future', :year => 1985>]
|
112
|
+
current_user.disliked
|
113
|
+
#=> [#<Movie name: 'Star Wars: Episode I - The Phantom Menace', year: 1999>]
|
114
|
+
```
|
115
|
+
|
116
|
+
Because you are allowed to declare multiple models as recommendable, you may
|
117
|
+
wish to return a set of liked or disliked objects for only one of those
|
118
|
+
models.
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
current_user.liked_for(Movie)
|
122
|
+
#=> [#<Movie name: '2001: A Space Odyssey', year: 1968>, #<Movie name: 'Back to the Future', :year => 1985>]
|
123
|
+
current_user.disliked_for(Show)
|
124
|
+
#=> []
|
125
|
+
```
|
126
|
+
|
127
|
+
### Ignoring
|
128
|
+
|
129
|
+
If you want to give your user the ability to `ignore` recommendations or even
|
130
|
+
just hide stuff on your website that they couldn't care less about, you can!
|
131
|
+
|
132
|
+
``` ruby
|
133
|
+
weird_movie_nobody_wants_to_watch = Movie.create(:title => 'Cool World', :year => 1998)
|
134
|
+
current_user.ignore weird_movie_nobody_wants_to_watch
|
135
|
+
#=> true
|
136
|
+
current_user.ignored
|
137
|
+
#=> [#<Movie name: 'Cool World', year: 1998>]
|
138
|
+
current_user.ignored_for(Show)
|
139
|
+
#=> []
|
140
|
+
```
|
141
|
+
|
142
|
+
Do what you will with this list of records. The power is yours.
|
143
|
+
|
144
|
+
### Saving for later
|
145
|
+
|
146
|
+
Your user might want to maintain a list of items to try later but still receive
|
147
|
+
new recommendations. For this, you can use Recommendable::StashedItems. Note that
|
148
|
+
adding an item to a user's stash will remove it from their list of recommendations.
|
149
|
+
Additionally, an item can not be stashed if the user already likes or dislikes it.
|
150
|
+
|
151
|
+
``` ruby
|
152
|
+
movie_to_watch_later = Movie.create(:title => 'The Descendants', :year => 2011)
|
153
|
+
current_user.stash(movie_to_watch_later)
|
154
|
+
#=> true
|
155
|
+
current_user.stashed
|
156
|
+
#=> [#<Movie name: 'The Descendants', year: 2011>]
|
157
|
+
# Later...
|
158
|
+
current_user.like(movie_to_watch_later)
|
159
|
+
#=> true
|
160
|
+
current_user.stash(movie_to_watch_later)
|
161
|
+
#=> nil
|
162
|
+
current_user.has_stashed?(movie_to_watch_later)
|
163
|
+
#=> false
|
164
|
+
```
|
165
|
+
|
166
|
+
### Unliking/Undisliking/Unignoring/Unstashing
|
167
|
+
|
168
|
+
Note that liking a movie that has already been disliked (or vice versa) will
|
169
|
+
simply destroy the old rating and create a new one. If a user attempts to `like`
|
170
|
+
a movie that they already like, however, nothing happens and `nil` is returned.
|
171
|
+
If you wish to manually remove an item from a user's likes or dislikes or
|
172
|
+
ignored records, you can:
|
173
|
+
|
174
|
+
``` ruby
|
175
|
+
current_user.like Movie.create(:title => 'Avatar', :year => 2009)
|
176
|
+
#=> true
|
177
|
+
current_user.unlike Movie.find_by_title('Avatar')
|
178
|
+
#=> true
|
179
|
+
current_user.liked
|
180
|
+
#=> []
|
181
|
+
```
|
182
|
+
|
183
|
+
You can use `undislike`, `unignore` and `unstash` in the same fashion. So, as
|
184
|
+
far as the Likes and Dislikes go, do you think that's enough? Because I didn't.
|
185
|
+
|
186
|
+
``` ruby
|
187
|
+
friend = User.create(:username => 'joeblow')
|
188
|
+
awesome_movie = Movie.find_by_title('2001: A Space Odyssey')
|
189
|
+
friend.like awesome_movie
|
190
|
+
#=> true
|
191
|
+
awesome_movie.liked_by
|
192
|
+
#=> [#<User username: 'davidcelis'>, #<User username: 'joeblow'>]
|
193
|
+
Movie.find_by_title('Star Wars: Episode I - The Phantom Menace').disliked_by
|
194
|
+
#=> [#<User username: 'davidcelis'>]
|
195
|
+
current_user.common_likes_with(friend)
|
196
|
+
#=> [#<Movie title: '2001: A Space Odyssey', year: 1968>]
|
197
|
+
```
|
198
|
+
|
199
|
+
`common_dislikes_with` and `disagreements_with` are available for similar use.
|
200
|
+
|
201
|
+
Recommendations
|
202
|
+
---------------
|
203
|
+
|
204
|
+
When a user submits a new `Like` or `Dislike`, they enter a queue to have their
|
205
|
+
recommendations refreshed. Once that user exits the queue, you can retrieve
|
206
|
+
these like so:
|
207
|
+
|
208
|
+
``` ruby
|
209
|
+
current_user.recommendations
|
210
|
+
#=> [#<Movie highly_recommended>, #<Show somewhat_recommended>, #<Movie meh>]
|
211
|
+
current_user.recommendations_for(Show)
|
212
|
+
#=> [#<Show somewhat_recommended>]
|
213
|
+
```
|
214
|
+
|
215
|
+
The top recommendations are returned in an array ordered by how good recommendable
|
216
|
+
believes the recommendation to be (from best to worst).
|
217
|
+
|
218
|
+
``` ruby
|
219
|
+
current_user.like somewhat_recommended_show
|
220
|
+
#=> true
|
221
|
+
current_user.recommendations
|
222
|
+
#=> [#<Movie highly_recommended>, #<Movie meh>]
|
223
|
+
```
|
224
|
+
|
225
|
+
Finally, you can also get a list of the users found to be most similar to your
|
226
|
+
current user:
|
227
|
+
|
228
|
+
``` ruby
|
229
|
+
current_user.similar_raters
|
230
|
+
#=> [#<User username: 'joe-blow'>, #<User username: 'less-so-than-joe-blow']
|
231
|
+
```
|
232
|
+
|
233
|
+
Likewise, this list is ordered from most similar to least similar.
|
234
|
+
|
235
|
+
Documentation
|
236
|
+
-------------
|
237
|
+
|
238
|
+
Some of the above methods are tweakable with options. For example, you can
|
239
|
+
adjust the number of recommendations returned to you (the default is 10) and
|
240
|
+
the number of similar uses returned (also 10). To see these options, check
|
241
|
+
the documentation.
|
242
|
+
|
243
|
+
A note on Redis
|
244
|
+
---------------
|
245
|
+
|
246
|
+
Recommendable currently depends on [Redis](http://redis.io/). It will install
|
247
|
+
the redis-rb gem as a dependency, but you must install Redis and run it
|
248
|
+
yourself. Also note that your Redis database must be persistent. Recommendable
|
249
|
+
will use Redis to permanently store sorted sets to quickly access recommendations.
|
250
|
+
Please take care with your Redis database! Fortunately, if you do lose your
|
251
|
+
Redis database, there's hope (more on that later).
|
252
|
+
|
253
|
+
Installing Redis
|
254
|
+
----------------
|
255
|
+
|
256
|
+
Recommendable requires Redis to deliver recommendations. Why? Because my
|
257
|
+
collaborative filtering algorithm is based almost entirely on set math, and
|
258
|
+
Ruby's Set class just won't cut it for fast recommendations.
|
259
|
+
|
260
|
+
### Homebrew
|
261
|
+
|
262
|
+
For Mac OS X users, homebrew is by far the easiest way to install Redis.
|
263
|
+
|
264
|
+
$ brew install redis
|
265
|
+
$ redis-server /usr/local/etc/redis.conf
|
266
|
+
|
267
|
+
You should now have Redis running as a daemon on localhost:6379
|
268
|
+
|
269
|
+
### Via Resque
|
270
|
+
|
271
|
+
Resque (which is also a dependency of recommendable) includes Rake tasks that
|
272
|
+
will install and run Redis for you:
|
273
|
+
|
274
|
+
$ git clone git://github.com/defunkt/resque.git
|
275
|
+
$ cd resque
|
276
|
+
$ rake redis:install dtach:install
|
277
|
+
$ rake redis:start
|
278
|
+
|
279
|
+
If you do not have admin rights to your machine:
|
280
|
+
|
281
|
+
$ git clone git://github.com/defunkt/resque.git
|
282
|
+
$ cd resque
|
283
|
+
$ PREFIX=<your_prefix> rake redis:install dtach:install
|
284
|
+
$ rake redis:start
|
285
|
+
|
286
|
+
Redis will now be running on localhost:6379. After a second, you can hit `ctrl-\`
|
287
|
+
to detach and keep Redis running in the background.
|
288
|
+
|
289
|
+
(Thanks to [defunkt][1] for mentioning this
|
290
|
+
method, and thanks to [ezmobius][2] for
|
291
|
+
making it possible)
|
292
|
+
|
293
|
+
Manually regenerating recommendations
|
294
|
+
-------------------------------------
|
295
|
+
|
296
|
+
If a catastrophe occurs and your Redis database is either destroyed or rendered
|
297
|
+
unusable in some other way, there is hope. You can run the following from your
|
298
|
+
application's console (assuming your user class is User):
|
299
|
+
|
300
|
+
User.all.each do |user|
|
301
|
+
user.update_similarities
|
302
|
+
user.update_recommendations
|
303
|
+
end
|
304
|
+
|
305
|
+
But please try not to have to do this manually!
|
306
|
+
|
307
|
+
Contributing to recommendable
|
308
|
+
-----------------------------
|
309
|
+
|
310
|
+
Read the [Contributing][3] wiki page first.
|
311
|
+
|
312
|
+
Once you've made your great commits:
|
313
|
+
|
314
|
+
1. [Fork][4] recommendable
|
315
|
+
2. Create a feature branch
|
316
|
+
3. Write your code (and tests please)
|
317
|
+
4. Push to your branch's origin
|
318
|
+
5. Create a [Pull Request][5] from your branch
|
319
|
+
6. That's it!
|
320
|
+
|
321
|
+
Links
|
322
|
+
-----
|
323
|
+
* Code: `git clone git://github.com/davidcelis/recommendable.git`
|
324
|
+
* Home: <http://github.com/davidcelis/recommendable>
|
325
|
+
* Docs: <http://rubydoc.info/gems/recommendable/0.1.0/frames>
|
326
|
+
* Bugs: <http://github.com/davidcelis/recommendable/issues>
|
327
|
+
* Gems: <http://rubygems.org/gems/recommendable>
|
328
|
+
|
329
|
+
Copyright
|
330
|
+
---------
|
331
|
+
|
332
|
+
Copyright © 2012 David Celis. See LICENSE.txt for
|
333
|
+
further details.
|
334
|
+
|
335
|
+
[0]: http://davidcelis.com/blog/2012/02/01/why-i-hate-five-star-ratings/
|
336
|
+
[1]: https://github.com/defunkt/resque
|
337
|
+
[2]: https://github.com/ezmobius/redis-rb
|
338
|
+
[3]: http://wiki.github.com/defunkt/resque/contributing
|
339
|
+
[4]: http://help.github.com/forking/
|
340
|
+
[5]: http://help.github.com/pull-requests/
|