recommendable 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- recommendable (1.1.1)
4
+ recommendable (1.1.3)
5
5
  hooks
6
6
  rails (>= 3.0.0)
7
- redis (~> 2.2.0)
7
+ redis (>= 2.2.0)
8
8
 
9
9
  GEM
10
10
  remote: http://rubygems.org/
@@ -79,7 +79,7 @@ GEM
79
79
  rake (0.9.2.2)
80
80
  rdoc (3.12)
81
81
  json (~> 1.4)
82
- redis (2.2.2)
82
+ redis (3.0.1)
83
83
  shoulda (3.0.1)
84
84
  shoulda-context (~> 1.0.0)
85
85
  shoulda-matchers (~> 1.0.0)
@@ -2,7 +2,7 @@ module Recommendable
2
2
  if defined?(Sidekiq)
3
3
  class SidekiqWorker
4
4
  include ::Sidekiq::Worker
5
- sidekiq_options :queue => :recommendable
5
+ sidekiq_options :queue => :recommendable, :unique => true
6
6
 
7
7
  def perform(user_id)
8
8
  user = Recommendable.user_class.find(user_id)
@@ -11,3 +11,18 @@ require "redis"
11
11
  # Tell Redis which database to use (usually between 0 and 15). The default of 0
12
12
  # is most likely okay unless you have another application using that database.
13
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
data/lib/recommendable.rb CHANGED
@@ -6,6 +6,8 @@ require 'recommendable/exceptions'
6
6
  require 'recommendable/railtie' if defined?(Rails)
7
7
  require 'recommendable/version'
8
8
  require 'hooks'
9
+ require 'sidekiq/middleware/client/unique_jobs' if defined?(Sidekiq)
10
+ require 'sidekiq/middleware/server/unique_jobs' if defined?(Sidekiq)
9
11
 
10
12
  module Recommendable
11
13
  mattr_accessor :redis, :user_class
@@ -121,8 +121,20 @@ module Recommendable
121
121
  protected :redis_key
122
122
 
123
123
  module LikeableMethods
124
+ # Retrieve the number of likes this object has received. Cached in Redis.
125
+ # @return [Fixnum] the number of times this object has been liked
126
+ def like_count
127
+ Recommendable.redis.get("#{redis_key}:like_count").to_i
128
+ end
129
+
124
130
  private
125
131
 
132
+ # Updates the cache for how many times this object has been liked.
133
+ # @private
134
+ def update_like_count
135
+ Recommendable.redis.set "#{redis_key}:like_count", liked_by.count
136
+ end
137
+
126
138
  # Used for setup purposes. Creates a set in redis containing users that
127
139
  # have liked this object.
128
140
  # @private
@@ -135,8 +147,20 @@ module Recommendable
135
147
  end
136
148
 
137
149
  module DislikeableMethods
150
+ # Retrieve the number of dislikes this object has received. Cached in Redis.
151
+ # @return [Fixnum] the number of times this object has been disliked
152
+ def dislike_count
153
+ Recommendable.redis.get("#{redis_key}:dislike_count").to_i
154
+ end
155
+
138
156
  private
139
157
 
158
+ # Updates the cache for how many times this object has been disliked.
159
+ # @private
160
+ def update_dislike_count
161
+ Recommendable.redis.set "#{redis_key}:dislike_count", disliked_by.count
162
+ end
163
+
140
164
  # Used for setup purposes. Creates a set in redis containing users that
141
165
  # have disliked this object.
142
166
  # @private
@@ -52,7 +52,11 @@ module Recommendable
52
52
  end
53
53
 
54
54
  %w(like unlike dislike undislike).each do |action|
55
- send "after_#{action}", lambda { |obj| obj.send(:update_score) and Recommendable.enqueue(self.id) }
55
+ send "after_#{action}", lambda { |obj|
56
+ obj.send(:update_score)
57
+ obj.send "update_#{action.gsub('un', '')}_count"
58
+ Recommendable.enqueue(self.id)
59
+ }
56
60
  end
57
61
 
58
62
  before_stash { |obj| unignore(obj) and unpredict(obj) }
@@ -1,7 +1,7 @@
1
1
  module Recommendable
2
2
  MAJOR = 1
3
3
  MINOR = 1
4
- PATCH = 3
4
+ PATCH = 4
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].join '.'
7
7
  end
@@ -0,0 +1,37 @@
1
+ require 'digest'
2
+
3
+ if defined?(Sidekiq)
4
+ module Sidekiq
5
+ module Middleware
6
+ module Client
7
+ class UniqueJobs
8
+ HASH_KEY_EXPIRATION = 30 * 60
9
+
10
+ def call(worker_class, item, queue)
11
+ enabled = worker_class.get_sidekiq_options['unique']
12
+ if enabled
13
+ payload_hash = Digest::MD5.hexdigest(Sidekiq.dump_json(item))
14
+ unique = false
15
+
16
+ Sidekiq.redis do |conn|
17
+ conn.watch(payload_hash)
18
+
19
+ if conn.get(payload_hash)
20
+ conn.unwatch
21
+ else
22
+ unique = conn.multi do
23
+ conn.setex(payload_hash, HASH_KEY_EXPIRATION, 1)
24
+ end
25
+ end
26
+ end
27
+ yield if unique
28
+ else
29
+ yield
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ if defined?(Sidekiq)
2
+ module Sidekiq
3
+ module Middleware
4
+ module Server
5
+ class UniqueJobs
6
+ def call(*args)
7
+ yield
8
+ ensure
9
+ json = Sidekiq.dump_json(args[1])
10
+ hash = Digest::MD5.hexdigest(json)
11
+ Sidekiq.redis {|conn| conn.del(hash) }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1 +1 @@
1
- require 'resque/tasks'
1
+ require 'resque/tasks' if defined?(Resque)
@@ -23,5 +23,19 @@ class DislikeSpec < MiniTest::Spec
23
23
  @user.dislike(movie).must_be_nil
24
24
  Recommendable::Dislike.count.must_equal 1
25
25
  end
26
+
27
+ it "should cache the number of dislikes" do
28
+ movie = Movie.create(:title => "Star Wars: Episode I - The Phantom Menace", :year => 1999)
29
+ @user2 = User.create(:username => "frank")
30
+
31
+ @user.dislike(movie)
32
+ movie.dislike_count.must_equal 1
33
+
34
+ @user2.dislike(movie)
35
+ movie.dislike_count.must_equal 2
36
+
37
+ @user.undislike(movie)
38
+ movie.dislike_count.must_equal 1
39
+ end
26
40
  end
27
41
  end
@@ -24,5 +24,19 @@ class LikeSpec < MiniTest::Spec
24
24
  @user.like(movie).must_be_nil
25
25
  Recommendable::Like.count.must_equal 1
26
26
  end
27
+
28
+ it "should cache the number of likes" do
29
+ movie = Movie.create(:title => "2001: A Space Odyssey", :year => 1968)
30
+ @user2 = User.create(:username => "frank")
31
+
32
+ @user.like(movie)
33
+ movie.like_count.must_equal 1
34
+
35
+ @user2.like(movie)
36
+ movie.like_count.must_equal 2
37
+
38
+ @user.unlike(movie)
39
+ movie.like_count.must_equal 1
40
+ end
27
41
  end
28
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recommendable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-20 00:00:00.000000000 Z
12
+ date: 2012-07-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sqlite3
@@ -195,6 +195,8 @@ files:
195
195
  - lib/recommendable/helpers.rb
196
196
  - lib/recommendable/railtie.rb
197
197
  - lib/recommendable/version.rb
198
+ - lib/sidekiq/middleware/client/unique_jobs.rb
199
+ - lib/sidekiq/middleware/server/unique_jobs.rb
198
200
  - lib/tasks/recommendable_tasks.rake
199
201
  - recommendable.gemspec
200
202
  - script/rails
@@ -276,7 +278,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
278
  version: '0'
277
279
  requirements: []
278
280
  rubyforge_project:
279
- rubygems_version: 1.8.22
281
+ rubygems_version: 1.8.23
280
282
  signing_key:
281
283
  specification_version: 3
282
284
  summary: Add like-based and/or dislike-based recommendations to your app.