Permalink
Browse files

Recommendable 2.0 - please read the CHANGELOG

Recommendable has been rewritten from the ground up. Major changes:

 * No longer require Rails (still require ActiveSupport) (fixes #36)
 * Add support for Mongoid, MongoMapper, and DataMapper (fixes #5)
 * Rename the concept of "Ignoring" items to "Hiding" items
 * Rename the concept of "Stashing" items to "Bookmarking" items
 * Store Likes/Dislikes/Hidden Items/Bookmarks in Redis as opposed to
   using Models
 * Add a Configuration class
 * Likes/Dislikes/Hidden Items/Bookmarks can now all have counters
   (fixes #42)
 * Enable support for Ruby 1.8.7 (fixes #32)
 * Greatly improve speed and performance

Signed-off-by: David Celis <david@davidcelis.com>
  • Loading branch information...
1 parent 18f939d commit 7c0d92f103e5175aab059fc2070cf2806d40cb3d David Celis committed Oct 9, 2012
Showing with 2,492 additions and 3,066 deletions.
  1. +3 −48 .gitignore
  2. +2 −0 .travis.yml
  3. +75 −7 CHANGELOG.markdown → CHANGELOG.md
  4. +1 −2 Gemfile
  5. +9 −14 Gemfile.lock
  6. 0 LICENSE.txt → LICENSE
  7. +0 −138 README.markdown
  8. +143 −0 README.md
  9. +7 −18 Rakefile
  10. +0 −5 TODO
  11. +0 −19 app/models/recommendable/dislike.rb
  12. +0 −19 app/models/recommendable/ignore.rb
  13. +0 −19 app/models/recommendable/like.rb
  14. +0 −19 app/models/recommendable/stash.rb
  15. +0 −17 app/workers/recommendable/delayed_job_worker.rb
  16. +0 −17 app/workers/recommendable/rails_worker.rb
  17. +0 −14 app/workers/recommendable/resque_worker.rb
  18. +0 −14 app/workers/recommendable/sidekiq_priority_worker.rb
  19. +0 −14 app/workers/recommendable/sidekiq_worker.rb
  20. +0 −3 config/routes.rb
  21. +0 −17 db/migrate/20120124193723_create_likes.rb
  22. +0 −17 db/migrate/20120124193728_create_dislikes.rb
  23. +0 −17 db/migrate/20120127092558_create_ignores.rb
  24. +0 −17 db/migrate/20120131173909_create_stashes.rb
  25. +0 −8 lib/generators/recommendable/USAGE
  26. +0 −37 lib/generators/recommendable/install_generator.rb
  27. +0 −23 lib/generators/recommendable/templates/initializer.rb
  28. +35 −29 lib/recommendable.rb
  29. +0 −170 lib/recommendable/acts_as_recommendable.rb
  30. +0 −819 lib/recommendable/acts_as_recommended_to.rb
  31. +35 −7 lib/recommendable/configuration.rb
  32. +0 −14 lib/recommendable/engine.rb
  33. +0 −4 lib/recommendable/exceptions.rb
  34. +3 −9 lib/recommendable/helpers.rb
  35. +150 −0 lib/recommendable/helpers/calculations.rb
  36. +23 −0 lib/recommendable/helpers/queriers.rb
  37. +29 −0 lib/recommendable/helpers/redis_key_mapper.rb
  38. +6 −0 lib/recommendable/orm/active_record.rb
  39. +7 −0 lib/recommendable/orm/data_mapper.rb
  40. +8 −0 lib/recommendable/orm/mongo_mapper.rb
  41. +7 −0 lib/recommendable/orm/mongoid.rb
  42. +0 −6 lib/recommendable/railtie.rb
  43. +83 −0 lib/recommendable/ratable.rb
  44. +26 −0 lib/recommendable/ratable/dislikable.rb
  45. +26 −0 lib/recommendable/ratable/likable.rb
  46. +109 −0 lib/recommendable/rater.rb
  47. +120 −0 lib/recommendable/rater/bookmarker.rb
  48. +122 −0 lib/recommendable/rater/disliker.rb
  49. +120 −0 lib/recommendable/rater/hider.rb
  50. +122 −0 lib/recommendable/rater/liker.rb
  51. +68 −0 lib/recommendable/rater/recommender.rb
  52. +5 −4 lib/recommendable/version.rb
  53. +16 −0 lib/recommendable/workers/delayed_job.rb
  54. +16 −0 lib/recommendable/workers/rails.rb
  55. +13 −0 lib/recommendable/workers/resque.rb
  56. +13 −0 lib/recommendable/workers/sidekiq.rb
  57. +0 −1 lib/tasks/recommendable_tasks.rake
  58. +21 −21 recommendable.gemspec
  59. +0 −8 script/rails
  60. +0 −9 spec/configuration_spec.rb
  61. +0 −261 spec/dummy/README.rdoc
  62. +0 −15 spec/dummy/app/assets/javascripts/application.js
  63. +0 −13 spec/dummy/app/assets/stylesheets/application.css
  64. +0 −3 spec/dummy/app/controllers/application_controller.rb
  65. +0 −2 spec/dummy/app/helpers/application_helper.rb
  66. 0 spec/dummy/app/models/.gitkeep
  67. +0 −2 spec/dummy/app/models/bully.rb
  68. +0 −2 spec/dummy/app/models/php_framework.rb
  69. +0 −3 spec/dummy/app/models/user.rb
  70. +0 −14 spec/dummy/app/views/layouts/application.html.erb
  71. +0 −10 spec/dummy/config/boot.rb
  72. +0 −21 spec/dummy/config/initializers/recommendable.rb
  73. +0 −5 spec/dummy/config/locales/en.yml
  74. +0 −4 spec/dummy/config/routes.rb
  75. +0 −18 spec/dummy/db/migrate/20120128005553_create_likes.recommendable.rb
  76. +0 −18 spec/dummy/db/migrate/20120128005554_create_dislikes.recommendable.rb
  77. +0 −18 spec/dummy/db/migrate/20120128005555_create_ignores.recommendable.rb
  78. +0 −9 spec/dummy/db/migrate/20120128024632_create_php_frameworks.rb
  79. +0 −9 spec/dummy/db/migrate/20120128024804_create_bullies.rb
  80. +0 −19 spec/dummy/db/migrate/20120131195416_create_stashes.recommendable.rb
  81. +0 −93 spec/dummy/db/schema.rb
  82. 0 spec/dummy/lib/assets/.gitkeep
  83. 0 spec/dummy/log/.gitkeep
  84. +0 −26 spec/dummy/public/404.html
  85. +0 −26 spec/dummy/public/422.html
  86. +0 −25 spec/dummy/public/500.html
  87. 0 spec/dummy/public/favicon.ico
  88. BIN spec/dummy/recommendable_dummy_development
  89. BIN spec/dummy/recommendable_dummy_test
  90. +0 −16 spec/factories.rb
  91. +0 −44 spec/models/dislike_spec.rb
  92. +0 −27 spec/models/ignore_spec.rb
  93. +0 −45 spec/models/like_spec.rb
  94. +0 −82 spec/models/movie_spec.rb
  95. +0 −27 spec/models/stash_spec.rb
  96. +0 −49 spec/models/user_benchmark_spec.rb
  97. +0 −494 spec/models/user_spec.rb
  98. +0 −28 spec/spec_helper.rb
  99. 0 {spec → test}/dummy/Rakefile
  100. +3 −0 test/dummy/app/models/book.rb
  101. +1 −0 {spec → test}/dummy/app/models/movie.rb
  102. +3 −0 test/dummy/app/models/rock.rb
  103. +4 −0 test/dummy/app/models/user.rb
  104. 0 {spec → test}/dummy/config.ru
  105. +10 −4 {spec → test}/dummy/config/application.rb
  106. +6 −0 test/dummy/config/boot.rb
  107. +3 −3 {spec → test}/dummy/config/database.yml
  108. 0 {spec → test}/dummy/config/environment.rb
  109. 0 {spec → test}/dummy/config/environments/development.rb
  110. +1 −1 {spec → test}/dummy/config/environments/production.rb
  111. 0 {spec → test}/dummy/config/environments/test.rb
  112. 0 {spec → test}/dummy/config/initializers/backtrace_silencers.rb
  113. 0 {spec → test}/dummy/config/initializers/inflections.rb
  114. 0 {spec → test}/dummy/config/initializers/mime_types.rb
  115. +6 −0 test/dummy/config/initializers/recommendable.rb
  116. +1 −1 {spec → test}/dummy/config/initializers/secret_token.rb
  117. 0 {spec → test}/dummy/config/initializers/session_store.rb
  118. 0 {spec → test}/dummy/config/initializers/wrap_parameters.rb
  119. +58 −0 test/dummy/config/routes.rb
  120. BIN test/dummy/db/development.sqlite3
  121. +1 −3 .../db/migrate/20120128020228_create_users.rb → test/dummy/db/migrate/20121006052300_create_users.rb
  122. +0 −2 ...b/migrate/20120128020413_create_movies.rb → test/dummy/db/migrate/20121006052339_create_movies.rb
  123. +9 −0 test/dummy/db/migrate/20121007212545_create_rocks.rb
  124. +10 −0 test/dummy/db/migrate/20121007213144_create_books.rb
  125. +42 −0 test/dummy/db/schema.rb
  126. +7 −0 test/dummy/db/seeds.rb
  127. BIN test/dummy/db/test.sqlite3
  128. 0 {spec/dummy/app/mailers → test/dummy/log}/.gitkeep
  129. 0 {spec → test}/dummy/script/rails
  130. +17 −0 test/factories.rb
  131. +46 −0 test/recommendable/helpers/calculations_test.rb
  132. +40 −0 test/recommendable/helpers/redis_key_mapper_test.rb
  133. +31 −0 test/recommendable/ratable/dislikable_test.rb
  134. +31 −0 test/recommendable/ratable/likable_test.rb
  135. +78 −0 test/recommendable/ratable_test.rb
  136. +122 −0 test/recommendable/rater/bookmarker_test.rb
  137. +122 −0 test/recommendable/rater/disliker_test.rb
  138. +122 −0 test/recommendable/rater/hider_test.rb
  139. +122 −0 test/recommendable/rater/liker_test.rb
  140. +119 −0 test/recommendable/rater/recommender_test.rb
  141. +29 −0 test/recommendable/rater_test.rb
  142. +25 −0 test/test_helper.rb
View
@@ -1,57 +1,12 @@
-# RVM / rbenv version files
-.rvmrc
-.rbenv-version
-
-# rcov generated
-coverage
-
-# rdoc generated
-rdoc
-
-# yard generated
-doc
-.yardoc
-
# bundler
.bundle
vendor/bundle
-# jeweler generated
-pkg
-
# logging
*.log
-# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
-#
-# * Create a file at ~/.gitignore
-# * Include files you want ignored
-# * Run: git config --global core.excludesfile ~/.gitignore
-#
-# After doing this, these files will be ignored in all your git projects,
-# saving you from having to 'pollute' every project you touch with them
-#
-# 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)
-#
-# For MacOS:
+# sqlite3
+*.sqlite3-journal
+# For MacOS:
.DS_Store
-
-# For TextMate
-*.tmproj
-tmtags
-
-# For emacs:
-#*~
-#\#*
-#.\#*
-
-# For vim:
-*.swp
-
-# For redcar:
-#.redcar
-
-# For rubinius:
-#*.rbc
-.rake_tasks*
View
@@ -1,3 +1,5 @@
rvm:
+ - 1.8.7
- 1.9.2
- 1.9.3
+script: bundle exec rake test
@@ -1,13 +1,81 @@
Changelog
=========
-_Future Release_
-----------------
-* Introduce a new configuration method. Please regenerate your intializer: `rails g recommendable:install --no-migrate`
-* Expire non-persistent redis sets after a certain time rather than explicitly tearing them down. Configure this in the initializer
-* Allow configuration of the Sidekiq/Resque queue name in the initializer
-* Allow configuration of whether or not to automatically enqueue users in the initializer
-* Fix a bug where `Float::NAN` was a possible prediction
+2.0.0 (Current version)
+-----------------------
+**IMPORTANT**: This is a MAJOR version bump that should greatly improve the performance of Recommendable. Most of the library has been rewritten from the ground up and, as such, there are steps you must take to make your application compatibile with this version.
+
+1. Flush your Redis database. Yes, you'll have to regenerate any recommendations at the end. Sorry.
+2. Please make a migration like the following. This migration will be irreversible if you uncomment the bit to drop the tables. If you do want to drop the tables, you should probably make a backup of your database first. I am not responsible for lost data, angry users, broken marriages, _et cetera_.
+
+```ruby
+# Require the model that receives recommendations so Recommendable detects it
+require Rails.root.join('app', 'models', 'user')
+
+class UpdateRecommendable < ActiveRecord::Migration
+ def up
+ Recommendable.redis.flushdb # Step 1
+
+ connection = ActiveRecord::Base.connection
+ # Transfer likes
+ result = connection.execute('SELECT * FROM recommendable_likes')
+ result.each do |row|
+ liked_set = Recommendable::Helpers::RedisKeyMapper.liked_set_for(row['likeable_type'], row['user_id'])
+ liked_by_set = Recommendable::Helpers::RedisKeyMapper.liked_by_set_for(row['likeable_type'], row['likeable_id'])
+ Recommendable.redis.sadd(liked_set, row['likeable_id'])
+ Recommendable.redis.sadd(liked_by_set, row['user_id'])
+ end
+
+ # Transfer dislikes
+ result = connection.execute('SELECT * FROM recommendable_dislikes')
+ result.each do |row|
+ disliked_set = Recommendable::Helpers::RedisKeyMapper.disliked_set_for(row['dislikeable_type'], row['user_id'])
+ disliked_by_set = Recommendable::Helpers::RedisKeyMapper.disliked_by_set_for(row['dislikeable_type'], row['dislikeable_id'])
+ Recommendable.redis.sadd(disliked_set, row['dislikeable_id'])
+ Recommendable.redis.sadd(disliked_by_set, row['user_id'])
+ end
+
+ # Transfer hidden items
+ result = connection.execute('SELECT * FROM recommendable_ignores')
+ result.each do |row|
+ set = Recommendable::Helpers::RedisKeyMapper.hidden_set_for(row['ignorable_type'], row['user_id'])
+ Recommendable.redis.sadd(set, row['ignorable_id'])
+ end
+
+ # Transfer bookmarks
+ result = connection.execute('SELECT * FROM recommendable_stashes')
+ result.each do |row|
+ set = Recommendable::Helpers::RedisKeyMapper.bookmarked_set_for(row['stashable_type'], row['user_id'])
+ Recommendable.redis.sadd(set, row['stashable_id'])
+ end
+
+ # Recalculate scores
+ Recommendable.config.ratable_classes.each do |klass|
+ klass.pluck(:id).each { |id| Recommendable::Helpers::Calculations.update_score_for(klass, id) }
+ end
+
+ # Remove old tables. Uncomment this if you're feeling confident. Please
+ # back up your database before doing this. Thanks.
+ # drop_table :recommendable_likes
+ # drop_table :recommendable_dislikes
+ # drop_table :recommendable_ignores
+ # drop_Table :recommendable_stashes
+ end
+
+ def down
+ # Recreate the tables from previous migrations, if you must.
+ end
+end
+
+```
+
+* Recommendable no longer requires Rails (it does, however, still require ActiveSupport)
+* Add support for more ORMs: DataMapper, Mongoid, and MongoMapper
+* Renamed the concept of Ignoring to Hiding
+* Renamed the concept of Stashing to Bookmarking
+* Likes, Dislikes, Hidden Items, and Bookmarked items are no longer stored as models. They are, instead, permanently stored in Redis.
+* Added a Configuration class
+* Enable Ruby 1.8.7 support
1.1.7 (Current version)
-----------------------
View
@@ -1,3 +1,2 @@
-source 'http://rubygems.org'
-# Add dependencies required to use your gem here.
+source :rubygems
gemspec
View
@@ -1,9 +1,9 @@
PATH
remote: .
specs:
- recommendable (1.1.7)
+ recommendable (2.0.0)
+ activesupport (>= 3.0.0)
hooks (>= 0.2.1)
- rails (>= 3.0.0)
redis (>= 2.2.0)
GEM
@@ -38,6 +38,7 @@ GEM
multi_json (~> 1.0)
arel (3.0.2)
builder (3.0.3)
+ database_cleaner (0.8.0)
erubis (2.7.0)
hike (1.2.1)
hooks (0.2.1)
@@ -51,7 +52,7 @@ GEM
mime-types (1.19)
miniskirt (1.2.1)
activesupport
- minitest (4.0.0)
+ minitest (4.1.0)
multi_json (1.3.6)
polyglot (0.3.3)
rack (1.4.1)
@@ -79,13 +80,7 @@ GEM
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
- redis (3.0.1)
- shoulda (3.1.1)
- shoulda-context (~> 1.0)
- shoulda-matchers (~> 1.2)
- shoulda-context (1.0.0)
- shoulda-matchers (1.3.0)
- activesupport (>= 3.0.0)
+ redis (3.0.2)
sprockets (2.1.3)
hike (~> 1.2)
rack (~> 1.0)
@@ -97,16 +92,16 @@ GEM
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.33)
- yard (0.6.8)
+ yard (0.8.2.1)
PLATFORMS
ruby
DEPENDENCIES
- bundler
+ database_cleaner
miniskirt
minitest
+ rails (>= 3.1.0)
recommendable!
- shoulda
sqlite3
- yard (~> 0.6.0)
+ yard
View
File renamed without changes.
View
@@ -1,138 +0,0 @@
-# Recommendable [![Build Status](https://secure.travis-ci.org/davidcelis/recommendable.png)](http://travis-ci.org/davidcelis/recommendable)
-
-Recommendable is an engine for Rails 3 applications to quickly add the ability for your users to Like/Dislike items and receive recommendations for new items. It uses Redis to store your recommendations and keep them sorted by how good the recommendation is.
-
-Requirements
-------------
-* Ruby 1.9.x
-* Rails 3.x or 4.x (and, currently, ActiveRecord)
-* Sidekiq or Resque (or DelayedJob)
-
-Bundling one of the queueing systems above is highly recommended to avoid having to manually refresh users' recommendations. If running on Rails 4, the built-in queueing system is supported. If you bundle [Sidekiq][sidekiq], [Resque][resque], or [DelayedJob][delayed_job], Recommendable will use your bundled queueing system instead. If bundling Resque, you should also include ['resque-loner'][resque-loner] in your Gemfile to ensure your users only get queued once (Sidekiq does this by default, and there is no current way to avoid duplicate jobs in DelayedJob).
-
-Installation
-------------
-
-Add the following to your Rails application's `Gemfile`:
-
-``` ruby
- gem 'recommendable'
-```
-
-After bundling, run the installation generator:
-
-``` bash
-$ rails g recommendable:install
-```
-
-Double check `config/initializers/recommendable.rb` for options on configuring Recommendable. After a user likes or dislikes something new, they are placed in a queue to have their recommendations updated. If you're using the basic Rails 4.0 queue, you don't need to do anything explicit. If using Sidekiq, Resque, or DelayedJob, start your workers from the command line:
-
-``` bash
-# sidekiq
-$ bundle exec sidekiq -q recommendable
-# resque
-$ QUEUE=recommendable rake environment resque:work
-# delayed_job
-$ rake jobs:work
-```
-
-If you're using Sidekiq, I recommend also bundling [sidekiq-middleware][sidekiq-middleware] to make your jobs unique. There's no reason for a user to be processed more than once at a time. If using Resque, use [resque-loner][resque-loner] for the same purpose. Bundling one of these gems is enough; the jobs will automatically become unique.
-
-Usage
------
-
-In your Rails model that will be receiving recommendations:
-
-``` ruby
-class User < ActiveRecord::Base
- recommends :movies, :shows, :other_things
-
- # ...
-end
-```
-
-That's it! Please note, however, that you may only do this in one model at this time.
-
-For more details on how to use Recommendable once it's installed and configured, [check out the more detailed README][recommendable] or see the [documentation][documentation].
-
-Installing Redis
-----------------
-
-Recommendable requires Redis to deliver recommendations. The collaborative filtering logic is based almost entirely on set math, and Redis is blazing fast for this. _NOTE: Your redis database MUST be persistent._
-
-### Mac OS X
-
-For Mac OS X users, homebrew is by far the easiest way to install Redis. Make sure to read the caveats after installation!
-
-``` bash
-$ brew install redis
-```
-
-### Linux
-
-For Linux users, there is a package on apt-get.
-
-``` bash
-$ sudo apt-get install redis-server
-$ redis-server
-```
-
-Redis will now be running on localhost:6379. After a second, you can hit `ctrl-\` to detach and keep Redis running in the background.
-
-### Redis problems?
-
-Oops, did you kill your Redis database? Not to worry. Likes, Dislikes, Ignores,
-and StashedItems are stored as models in your regular database. As long as these
-still exist, you can regenerate the similarity values and recommendations on the
-fly. But try not to have to do it!
-
-``` ruby
-User.all.each do |user|
- user.send :update_similarities
- user.send :update_recommendations
-end
-```
-
-Why not stars?
---------------
-I'll let Randall Munroe of [XKCD](http://xkcd.com/) take this one for me:
-
-[![I got lost and wandered into the world's creepiest cemetery, where the headstones just had names and star ratings. Freaked me out. When I got home I tried to leave the cemetery a bad review on Yelp, but as my hand hovered over the 'one star' button I felt this distant chill ...](http://imgs.xkcd.com/comics/star_ratings.png)](http://xkcd.com/1098/)
-
-Contributing to recommendable
------------------------------
-
-Once you've made your great commits:
-
-1. [Fork][forking] recommendable
-2. Create a feature branch
-3. Write your code (and tests please)
-4. Push to your branch's origin
-5. Create a [Pull Request][pull requests] from your branch
-6. That's it!
-
-Links
------
-* Code: `git clone git://github.com/davidcelis/recommendable.git`
-* Home: <http://github.com/davidcelis/recommendable>
-* Docs: <http://rubydoc.info/gems/recommendable/frames>
-* Bugs: <http://github.com/davidcelis/recommendable/issues>
-* Gems: <http://rubygems.org/gems/recommendable>
-
-Copyright
----------
-
-Copyright © 2012 David Celis. See LICENSE.txt for
-further details.
-
-[stars]: http://davidcelis.com/blog/2012/02/01/why-i-hate-five-star-ratings/
-[sidekiq]: https://github.com/mperham/sidekiq
-[sidekiq-middleware]: https://github.com/krasnoukhov/sidekiq-middleware
-[delayed_job]: https://github.com/tobi/delayed_job
-[resque]: https://github.com/defunkt/resque
-[resque-loner]: https://github.com/jayniz/resque-loner
-[forking]: http://help.github.com/forking/
-[pull requests]: http://help.github.com/pull-requests/
-[collaborative filtering]: http://davidcelis.com/blog/2012/02/07/collaborative-filtering-with-likes-and-dislikes/
-[recommendable]: http://davidcelis.github.com/recommendable/
-[documentation]: http://rubydoc.info/gems/recommendable/frames
Oops, something went wrong.

0 comments on commit 7c0d92f

Please sign in to comment.