recommendable 1.1.7 → 2.0.0
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/lib/recommendable.rb +38 -26
- data/lib/recommendable/configuration.rb +47 -0
- data/lib/recommendable/helpers.rb +3 -9
- data/lib/recommendable/helpers/calculations.rb +150 -0
- data/lib/recommendable/helpers/queriers.rb +23 -0
- data/lib/recommendable/helpers/redis_key_mapper.rb +29 -0
- data/lib/recommendable/orm/active_record.rb +6 -0
- data/lib/recommendable/orm/data_mapper.rb +7 -0
- data/lib/recommendable/orm/mongo_mapper.rb +8 -0
- data/lib/recommendable/orm/mongoid.rb +7 -0
- data/lib/recommendable/ratable.rb +83 -0
- data/lib/recommendable/ratable/dislikable.rb +26 -0
- data/lib/recommendable/ratable/likable.rb +26 -0
- data/lib/recommendable/rater.rb +109 -0
- data/lib/recommendable/rater/bookmarker.rb +120 -0
- data/lib/recommendable/rater/disliker.rb +122 -0
- data/lib/recommendable/rater/hider.rb +120 -0
- data/lib/recommendable/rater/liker.rb +122 -0
- data/lib/recommendable/rater/recommender.rb +68 -0
- data/lib/recommendable/version.rb +5 -4
- data/lib/recommendable/workers/delayed_job.rb +16 -0
- data/lib/recommendable/workers/rails.rb +16 -0
- data/lib/recommendable/workers/resque.rb +13 -0
- data/lib/recommendable/workers/sidekiq.rb +13 -0
- metadata +62 -131
- data/.gitignore +0 -57
- data/.travis.yml +0 -3
- data/CHANGELOG.markdown +0 -159
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -112
- data/LICENSE.txt +0 -22
- data/README.markdown +0 -135
- data/Rakefile +0 -26
- data/TODO +0 -7
- data/app/models/recommendable/dislike.rb +0 -19
- data/app/models/recommendable/ignore.rb +0 -19
- data/app/models/recommendable/like.rb +0 -19
- data/app/models/recommendable/stash.rb +0 -19
- data/app/workers/recommendable/delayed_job_worker.rb +0 -17
- data/app/workers/recommendable/rails_worker.rb +0 -17
- data/app/workers/recommendable/resque_worker.rb +0 -14
- data/app/workers/recommendable/sidekiq_worker.rb +0 -14
- data/config/routes.rb +0 -3
- data/db/migrate/20120124193723_create_likes.rb +0 -17
- data/db/migrate/20120124193728_create_dislikes.rb +0 -17
- data/db/migrate/20120127092558_create_ignores.rb +0 -17
- data/db/migrate/20120131173909_create_stashes.rb +0 -17
- data/lib/generators/recommendable/USAGE +0 -8
- data/lib/generators/recommendable/install_generator.rb +0 -40
- data/lib/generators/recommendable/templates/initializer.rb +0 -28
- data/lib/recommendable/acts_as_recommendable.rb +0 -176
- data/lib/recommendable/acts_as_recommended_to.rb +0 -774
- data/lib/recommendable/engine.rb +0 -14
- data/lib/recommendable/exceptions.rb +0 -4
- data/lib/recommendable/railtie.rb +0 -6
- data/lib/sidekiq/middleware/client/unique_jobs.rb +0 -37
- data/lib/sidekiq/middleware/server/unique_jobs.rb +0 -17
- data/lib/tasks/recommendable_tasks.rake +0 -1
- data/recommendable.gemspec +0 -30
- data/script/rails +0 -8
- data/spec/configuration_spec.rb +0 -9
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/assets/javascripts/application.js +0 -15
- data/spec/dummy/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/bully.rb +0 -2
- data/spec/dummy/app/models/movie.rb +0 -2
- data/spec/dummy/app/models/php_framework.rb +0 -2
- data/spec/dummy/app/models/user.rb +0 -3
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/config/application.rb +0 -56
- data/spec/dummy/config/boot.rb +0 -10
- data/spec/dummy/config/database.yml +0 -25
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -37
- data/spec/dummy/config/environments/production.rb +0 -67
- data/spec/dummy/config/environments/test.rb +0 -37
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -15
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/config/initializers/recommendable.rb +0 -14
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/routes.rb +0 -4
- data/spec/dummy/db/migrate/20120128005553_create_likes.recommendable.rb +0 -18
- data/spec/dummy/db/migrate/20120128005554_create_dislikes.recommendable.rb +0 -18
- data/spec/dummy/db/migrate/20120128005555_create_ignores.recommendable.rb +0 -18
- data/spec/dummy/db/migrate/20120128020228_create_users.rb +0 -9
- data/spec/dummy/db/migrate/20120128020413_create_movies.rb +0 -10
- data/spec/dummy/db/migrate/20120128024632_create_php_frameworks.rb +0 -9
- data/spec/dummy/db/migrate/20120128024804_create_bullies.rb +0 -9
- data/spec/dummy/db/migrate/20120131195416_create_stashes.recommendable.rb +0 -19
- data/spec/dummy/db/schema.rb +0 -89
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -25
- 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 +0 -6
- data/spec/factories.rb +0 -16
- data/spec/models/dislike_spec.rb +0 -41
- data/spec/models/ignore_spec.rb +0 -27
- data/spec/models/like_spec.rb +0 -42
- data/spec/models/movie_spec.rb +0 -82
- data/spec/models/stash_spec.rb +0 -27
- data/spec/models/user_benchmark_spec.rb +0 -49
- data/spec/models/user_spec.rb +0 -443
- data/spec/spec_helper.rb +0 -28
data/Rakefile
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
require 'rubygems'
|
|
4
|
-
require 'bundler'
|
|
5
|
-
|
|
6
|
-
begin
|
|
7
|
-
Bundler.setup(:default, :development)
|
|
8
|
-
rescue Bundler::BundlerError => e
|
|
9
|
-
$stderr.puts e.message
|
|
10
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
|
11
|
-
exit e.status_code
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
require 'rake'
|
|
15
|
-
|
|
16
|
-
require 'rake/testtask'
|
|
17
|
-
Rake::TestTask.new(:test) do |test|
|
|
18
|
-
test.libs << 'lib' << 'spec'
|
|
19
|
-
test.pattern = 'spec/**/*_spec.rb'
|
|
20
|
-
test.verbose = true
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
task :default => :test
|
|
24
|
-
|
|
25
|
-
require 'yard'
|
|
26
|
-
YARD::Rake::YardocTask.new
|
data/TODO
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
class Dislike < ActiveRecord::Base
|
|
3
|
-
self.table_name = 'recommendable_dislikes'
|
|
4
|
-
attr_accessible :user_id, :dislikeable_id, :dislikeable_type
|
|
5
|
-
|
|
6
|
-
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
|
7
|
-
belongs_to :dislikeable, :polymorphic => true
|
|
8
|
-
|
|
9
|
-
validates :user_id, :uniqueness => { :scope => [:dislikeable_id, :dislikeable_type],
|
|
10
|
-
:message => "has already disliked this item" },
|
|
11
|
-
:presence => true
|
|
12
|
-
validates_presence_of :dislikeable_id
|
|
13
|
-
validates_presence_of :dislikeable_type
|
|
14
|
-
|
|
15
|
-
def dislikeable_type=(sType)
|
|
16
|
-
super sType.to_s.classify.constantize.base_class.to_s
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
class Ignore < ActiveRecord::Base
|
|
3
|
-
self.table_name = 'recommendable_ignores'
|
|
4
|
-
attr_accessible :user_id, :ignorable_id, :ignorable_type
|
|
5
|
-
|
|
6
|
-
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
|
7
|
-
belongs_to :ignorable, :polymorphic => true
|
|
8
|
-
|
|
9
|
-
validates :user_id, :uniqueness => { :scope => [:ignorable_id, :ignorable_type],
|
|
10
|
-
:message => "has already ignored this item" },
|
|
11
|
-
:presence => true
|
|
12
|
-
validates_presence_of :ignorable_id
|
|
13
|
-
validates_presence_of :ignorable_type
|
|
14
|
-
|
|
15
|
-
def ignorable_type=(sType)
|
|
16
|
-
super sType.to_s.classify.constantize.base_class.to_s
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
class Like < ActiveRecord::Base
|
|
3
|
-
self.table_name = 'recommendable_likes'
|
|
4
|
-
attr_accessible :user_id, :likeable_id, :likeable_type
|
|
5
|
-
|
|
6
|
-
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
|
7
|
-
belongs_to :likeable, :polymorphic => true, :foreign_key => :likeable_id
|
|
8
|
-
|
|
9
|
-
validates :user_id, :uniqueness => { :scope => [:likeable_id, :likeable_type],
|
|
10
|
-
:message => "has already liked this item" },
|
|
11
|
-
:presence => true
|
|
12
|
-
validates_presence_of :likeable_id
|
|
13
|
-
validates_presence_of :likeable_type
|
|
14
|
-
|
|
15
|
-
def likeable_type=(sType)
|
|
16
|
-
super sType.to_s.classify.constantize.base_class.to_s
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
class Stash < ActiveRecord::Base
|
|
3
|
-
self.table_name = 'recommendable_stashes'
|
|
4
|
-
attr_accessible :user_id, :stashable_id, :stashable_type
|
|
5
|
-
|
|
6
|
-
belongs_to :user, :class_name => Recommendable.user_class.to_s, :foreign_key => :user_id
|
|
7
|
-
belongs_to :stashable, :polymorphic => :true
|
|
8
|
-
|
|
9
|
-
validates :user_id, :uniqueness => { :scope => [:stashable_id, :stashable_type],
|
|
10
|
-
:message => "has already stashed this item" },
|
|
11
|
-
:presence => true
|
|
12
|
-
validates_presence_of :stashable_id
|
|
13
|
-
validates_presence_of :stashable_type
|
|
14
|
-
|
|
15
|
-
def stashable_type=(sType)
|
|
16
|
-
super sType.to_s.classify.constantize.base_class.to_s
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
if defined?(Delayed::Job)
|
|
3
|
-
class DelayedJobWorker
|
|
4
|
-
attr_accessor :user_id
|
|
5
|
-
|
|
6
|
-
def initialize(user_id)
|
|
7
|
-
@user_id = user_id
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def perform
|
|
11
|
-
user = Recommendable.user_class.find(self.user_id)
|
|
12
|
-
user.send :update_similarities
|
|
13
|
-
user.send :update_recommendations
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
if defined?(Rails::Queueing)
|
|
3
|
-
class RailsWorker
|
|
4
|
-
attr_accessor :user_id
|
|
5
|
-
|
|
6
|
-
def initialize(user_id)
|
|
7
|
-
@user_id = user_id
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def run
|
|
11
|
-
user = Recommendable.user_class.find(self.user_id)
|
|
12
|
-
user.send :update_similarities
|
|
13
|
-
user.send :update_recommendations
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
if defined?(Resque)
|
|
3
|
-
class ResqueWorker
|
|
4
|
-
include Resque::Plugins::UniqueJob if defined?(Resque::Plugins::UniqueJob)
|
|
5
|
-
@queue = :recommendable
|
|
6
|
-
|
|
7
|
-
def self.perform(user_id)
|
|
8
|
-
user = Recommendable.user_class.find(user_id)
|
|
9
|
-
user.send :update_similarities
|
|
10
|
-
user.send :update_recommendations
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
if defined?(Sidekiq)
|
|
3
|
-
class SidekiqWorker
|
|
4
|
-
include ::Sidekiq::Worker
|
|
5
|
-
sidekiq_options :queue => :recommendable, :unique => true
|
|
6
|
-
|
|
7
|
-
def perform(user_id)
|
|
8
|
-
user = Recommendable.user_class.find(user_id)
|
|
9
|
-
user.send :update_similarities
|
|
10
|
-
user.send :update_recommendations
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
data/config/routes.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
class CreateLikes < ActiveRecord::Migration
|
|
2
|
-
def up
|
|
3
|
-
create_table :recommendable_likes do |t|
|
|
4
|
-
t.references :user
|
|
5
|
-
t.references :likeable, :polymorphic => true
|
|
6
|
-
t.timestamps
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
add_index :recommendable_likes, :likeable_id
|
|
10
|
-
add_index :recommendable_likes, :likeable_type
|
|
11
|
-
add_index :recommendable_likes, [:user_id, :likeable_id, :likeable_type], :unique => true, :name => "user_like_constraint"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def down
|
|
15
|
-
drop_table :recommendable_likes
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
class CreateDislikes < ActiveRecord::Migration
|
|
2
|
-
def up
|
|
3
|
-
create_table :recommendable_dislikes do |t|
|
|
4
|
-
t.references :user
|
|
5
|
-
t.references :dislikeable, :polymorphic => true
|
|
6
|
-
t.timestamps
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
add_index :recommendable_dislikes, :dislikeable_id
|
|
10
|
-
add_index :recommendable_dislikes, :dislikeable_type
|
|
11
|
-
add_index :recommendable_dislikes, [:user_id, :dislikeable_id, :dislikeable_type], :unique => true, :name => "user_dislike_constraint"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def down
|
|
15
|
-
drop_table :recommendable_dislikes
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
class CreateIgnores < ActiveRecord::Migration
|
|
2
|
-
def up
|
|
3
|
-
create_table :recommendable_ignores do |t|
|
|
4
|
-
t.references :user
|
|
5
|
-
t.references :ignorable, :polymorphic => true
|
|
6
|
-
t.timestamps
|
|
7
|
-
end
|
|
8
|
-
|
|
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
|
-
end
|
|
13
|
-
|
|
14
|
-
def down
|
|
15
|
-
drop_table :recommendable_ignores
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
Description:
|
|
2
|
-
This generator will install Recommendable's initializer.rb file and migrate the Like and Dislike tables into your database unless specified.
|
|
3
|
-
|
|
4
|
-
Example:
|
|
5
|
-
rails generate recommendable:install
|
|
6
|
-
|
|
7
|
-
This will create:
|
|
8
|
-
config/initializers/recommendable.rb
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
require 'rails/generators'
|
|
2
|
-
|
|
3
|
-
module Recommendable
|
|
4
|
-
module Generators
|
|
5
|
-
class InstallGenerator < Rails::Generators::Base
|
|
6
|
-
argument :redis_host, :type => :string, :default => "localhost", :desc => "The hostname your redis server is running on."
|
|
7
|
-
argument :redis_port, :type => :string, :default => "6379", :desc => "The port your redis server is running on."
|
|
8
|
-
class_option :redis_socket, :type => :string, :desc => "Indicates the UNIX socket your redis server is running on (if it is)."
|
|
9
|
-
class_option :no_migrate, :type => :boolean, :default => false, :desc => "Skip migrations. The Like and Dislike tables will not be created."
|
|
10
|
-
|
|
11
|
-
source_root File.expand_path("../templates", __FILE__)
|
|
12
|
-
|
|
13
|
-
def add_recommendable_initializer
|
|
14
|
-
path = "#{Rails.root}/config/initializers/recommendable.rb"
|
|
15
|
-
if File.exists?(path)
|
|
16
|
-
puts "Skipping config/initializers/recommendable.rb creation; file already exists!"
|
|
17
|
-
else
|
|
18
|
-
puts "Adding Recommendable initializer (config/initializers/recommendable.rb)"
|
|
19
|
-
template "initializer.rb", path
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def install_migrations
|
|
24
|
-
puts "Copying migrations..."
|
|
25
|
-
Dir.chdir(Rails.root) { puts `rake recommendable:install:migrations` }
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def run_migrations
|
|
29
|
-
unless options[:no_migrate]
|
|
30
|
-
puts "Running rake db:migrate"
|
|
31
|
-
puts `rake db:migrate`
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def finished
|
|
36
|
-
puts "Done! Recommendable has been successfully installed. Please configure it in config/intializers/recommendable.rb"
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
require "redis"
|
|
2
|
-
|
|
3
|
-
# Recommendable requires a connection to a running redis-server. Either create
|
|
4
|
-
# a new instance based on a host/port or UNIX socket, or pass in an existing
|
|
5
|
-
# Redis client instance.
|
|
6
|
-
<% if options.redis_socket %># <% end %>Recommendable.redis = Redis.new(:host => "<%= redis_host %>", :port => <%= redis_port %>)
|
|
7
|
-
|
|
8
|
-
# Connect to Redis via a UNIX socket instead
|
|
9
|
-
<% unless options.redis_socket %># <% end %>Recommendable.redis = Redis.new(:sock => "<%= options.redis_socket %>")
|
|
10
|
-
|
|
11
|
-
# Tell Redis which database to use (usually between 0 and 15). The default of 0
|
|
12
|
-
# is most likely okay unless you have another application using that database.
|
|
13
|
-
# Recommendable.redis.select "0"
|
|
14
|
-
|
|
15
|
-
# If using Sidekiq, uncomment the following or place it in config/initializers/sidekiq.rb:
|
|
16
|
-
# Sidekiq.configure_server do |config|
|
|
17
|
-
# require 'sidekiq/middleware/server/unique_jobs'
|
|
18
|
-
# config.server_middleware do |chain|
|
|
19
|
-
# chain.add Sidekiq::Middleware::Server::UniqueJobs
|
|
20
|
-
# end
|
|
21
|
-
# end
|
|
22
|
-
#
|
|
23
|
-
# Sidekiq.configure_client do |config|
|
|
24
|
-
# require 'sidekiq/middleware/client/unique_jobs'
|
|
25
|
-
# config.client_middleware do |chain|
|
|
26
|
-
# chain.add Sidekiq::Middleware::Client::UniqueJobs
|
|
27
|
-
# end
|
|
28
|
-
# end
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
module Recommendable
|
|
2
|
-
module ActsAsRecommendable
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
|
|
5
|
-
module ClassMethods
|
|
6
|
-
def acts_as_recommendable
|
|
7
|
-
class_eval do
|
|
8
|
-
Recommendable.recommendable_classes << self
|
|
9
|
-
|
|
10
|
-
has_many :recommendable_likes, :as => :likeable, :dependent => :destroy,
|
|
11
|
-
:class_name => "Recommendable::Like"
|
|
12
|
-
has_many :recommendable_dislikes, :as => :dislikeable, :dependent => :destroy,
|
|
13
|
-
:class_name => "Recommendable::Dislike"
|
|
14
|
-
has_many :recommendable_ignores, :as => :ignorable, :dependent => :destroy,
|
|
15
|
-
:class_name => "Recommendable::Ignore"
|
|
16
|
-
has_many :recommendable_stashes, :as => :stashable, :dependent => :destroy,
|
|
17
|
-
:class_name => "Recommendable::Stash"
|
|
18
|
-
|
|
19
|
-
has_many :liked_by, :through => :recommendable_likes, :source => :user,
|
|
20
|
-
:foreign_key => :user_id, :class_name => Recommendable.user_class.to_s
|
|
21
|
-
has_many :disliked_by, :through => :recommendable_dislikes, :source => :user,
|
|
22
|
-
:foreign_key => :user_id, :class_name => Recommendable.user_class.to_s
|
|
23
|
-
|
|
24
|
-
include LikeableMethods
|
|
25
|
-
include DislikeableMethods
|
|
26
|
-
|
|
27
|
-
before_destroy :remove_from_scores, :remove_from_recommendations
|
|
28
|
-
|
|
29
|
-
def self.acts_as_recommendable?() true end
|
|
30
|
-
|
|
31
|
-
def been_rated?
|
|
32
|
-
recommendable_likes.count + recommendable_dislikes.count > 0
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Returns an array of users that have liked or disliked this item.
|
|
36
|
-
# @return [Array] an array of users
|
|
37
|
-
def rated_by
|
|
38
|
-
liked_by + disliked_by
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def self.top count = 1
|
|
42
|
-
ids = Recommendable.redis.zrevrange(self.score_set, 0, count - 1).map(&:to_i)
|
|
43
|
-
|
|
44
|
-
items = self.find ids
|
|
45
|
-
return items.first if count == 1
|
|
46
|
-
|
|
47
|
-
return items.sort_by { |item| ids.index(item.id) }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
def update_score
|
|
53
|
-
return 0 unless been_rated?
|
|
54
|
-
|
|
55
|
-
z = 1.96
|
|
56
|
-
n = recommendable_likes.count + recommendable_dislikes.count
|
|
57
|
-
|
|
58
|
-
phat = recommendable_likes.count / n.to_f
|
|
59
|
-
score = (phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
|
|
60
|
-
|
|
61
|
-
Recommendable.redis.zadd self.class.score_set, score, self.id
|
|
62
|
-
true
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def remove_from_scores
|
|
66
|
-
Recommendable.redis.zrem self.class.score_set, self.id
|
|
67
|
-
true
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def remove_from_recommendations
|
|
71
|
-
Recommendable.user_class.find_each do |user|
|
|
72
|
-
user.send :completely_unrecommend, self
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Used for setup purposes. Calls convenience methods to create sets
|
|
77
|
-
# in redis of users that both like and dislike this object.
|
|
78
|
-
# @return [Array] an array containing the liked_by set and the disliked_by set
|
|
79
|
-
# @private
|
|
80
|
-
def create_recommendable_sets
|
|
81
|
-
[create_liked_by_set, create_disliked_by_set]
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Used for teardown purposes. Destroys the sets in redis created by
|
|
85
|
-
# {#create_recommendable_sets}
|
|
86
|
-
# @private
|
|
87
|
-
def destroy_recommendable_sets
|
|
88
|
-
Recommendable.redis.del "#{self.class.base_class}:#{id}:liked_by"
|
|
89
|
-
Recommendable.redis.del "#{self.class.base_class}:#{id}:disliked_by"
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Returns an array of IDs of users that have liked or disliked this item.
|
|
93
|
-
# @return [Array] an array of user IDs
|
|
94
|
-
# @private
|
|
95
|
-
def rates_by
|
|
96
|
-
recommendable_likes.map(&:user_id) + recommendable_dislikes.map(&:user_id)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def self.score_set
|
|
100
|
-
"#{self}:sorted"
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
private :recommendable_likes, :recommendable_dislikes,
|
|
104
|
-
:recommendable_ignores, :recommendable_stashes
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def acts_as_recommendable?() false end
|
|
109
|
-
|
|
110
|
-
def sti?
|
|
111
|
-
self.base_class != self && self.base_class.table_name == self.table_name
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
private
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Instance methods.
|
|
118
|
-
def recommendable?() self.class.acts_as_recommendable? end
|
|
119
|
-
|
|
120
|
-
def redis_key() "#{self.class.base_class}:#{id}" end
|
|
121
|
-
|
|
122
|
-
protected :redis_key
|
|
123
|
-
|
|
124
|
-
module LikeableMethods
|
|
125
|
-
# Retrieve the number of likes this object has received. Cached in Redis.
|
|
126
|
-
# @return [Fixnum] the number of times this object has been liked
|
|
127
|
-
def like_count
|
|
128
|
-
Recommendable.redis.get("#{redis_key}:like_count").to_i
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
private
|
|
132
|
-
|
|
133
|
-
# Updates the cache for how many times this object has been liked.
|
|
134
|
-
# @private
|
|
135
|
-
def update_like_count
|
|
136
|
-
Recommendable.redis.set "#{redis_key}:like_count", liked_by.count
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Used for setup purposes. Creates a set in redis containing users that
|
|
140
|
-
# have liked this object.
|
|
141
|
-
# @private
|
|
142
|
-
# @return [String] the key in Redis pointing to the set
|
|
143
|
-
def create_liked_by_set
|
|
144
|
-
set = "#{redis_key}:liked_by"
|
|
145
|
-
liked_by.each { |rater| Recommendable.redis.sadd set, rater.id }
|
|
146
|
-
return set
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
module DislikeableMethods
|
|
151
|
-
# Retrieve the number of dislikes this object has received. Cached in Redis.
|
|
152
|
-
# @return [Fixnum] the number of times this object has been disliked
|
|
153
|
-
def dislike_count
|
|
154
|
-
Recommendable.redis.get("#{redis_key}:dislike_count").to_i
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
private
|
|
158
|
-
|
|
159
|
-
# Updates the cache for how many times this object has been disliked.
|
|
160
|
-
# @private
|
|
161
|
-
def update_dislike_count
|
|
162
|
-
Recommendable.redis.set "#{redis_key}:dislike_count", disliked_by.count
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Used for setup purposes. Creates a set in redis containing users that
|
|
166
|
-
# have disliked this object.
|
|
167
|
-
# @private
|
|
168
|
-
# @return [String] the key in Redis pointing to the set
|
|
169
|
-
def create_disliked_by_set
|
|
170
|
-
set = "#{redis_key}:disliked_by"
|
|
171
|
-
disliked_by.each { |rater| Recommendable.redis.sadd set, rater.id }
|
|
172
|
-
return set
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|