recommendable 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://secure.travis-ci.org/davidcelis/recommendable.png)](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/
|