ar-resque-counter-cache 2.1.0 → 3.0.0.rc1
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/ar-resque-counter-cache.gemspec +1 -1
- data/lib/ar-resque-counter-cache.rb +0 -2
- data/lib/ar-resque-counter-cache/active_record.rb +108 -50
- data/lib/ar-resque-counter-cache/increment_counters_worker.rb +3 -3
- data/lib/ar-resque-counter-cache/version.rb +2 -2
- data/spec/ar_resque_counter_cache/active_record_spec.rb +4 -4
- data/spec/integration_spec.rb +5 -5
- data/spec/spec_helper.rb +1 -1
- metadata +15 -15
@@ -4,7 +4,7 @@ require "ar-resque-counter-cache/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "ar-resque-counter-cache"
|
7
|
-
s.version =
|
7
|
+
s.version = ArResqueCounterCache::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Aaron Gibralter"]
|
10
10
|
s.email = ["aaron.gibralter@gmail.com"]
|
@@ -1,72 +1,130 @@
|
|
1
|
-
module
|
1
|
+
module ArResqueCounterCache
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
ORIG_BT = ::ActiveRecord::Associations::BelongsToAssociation
|
6
|
+
ORIG_BTP = ::ActiveRecord::Associations::BelongsToPolymorphicAssociation
|
7
|
+
|
8
|
+
module Associations
|
9
|
+
|
10
|
+
module AsyncUpdateCounters
|
11
|
+
def update_counters_with_async(record)
|
12
|
+
# TODO make async?
|
13
|
+
update_counters_without_async(record)
|
12
14
|
end
|
13
|
-
end
|
14
|
-
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
raise "Please don't use both async_counter_cache and counter_cache." if column && options[:counter_cache]
|
21
|
-
super(association_id, options)
|
22
|
-
if column
|
23
|
-
# Store the async_counter_cache column for the update_async_counters
|
24
|
-
# helper method.
|
25
|
-
self.async_counter_types[association_id] = column
|
26
|
-
# Fetch the reflection.
|
27
|
-
reflection = self.reflect_on_association(association_id)
|
28
|
-
parent_class = reflection.klass
|
29
|
-
# Let's make the column read-only like the normal belongs_to
|
30
|
-
# counter_cache.
|
31
|
-
parent_class.send(:attr_readonly, column.to_sym) if defined?(parent_class) && parent_class.respond_to?(:attr_readonly)
|
32
|
-
parent_id_column = reflection.foreign_key
|
33
|
-
add_callbacks(parent_class.to_s, parent_id_column, column)
|
16
|
+
def self.included(base)
|
17
|
+
base.class_eval do
|
18
|
+
alias_method_chain :update_counters, :async
|
19
|
+
end
|
34
20
|
end
|
35
21
|
end
|
36
22
|
|
37
|
-
|
38
|
-
|
23
|
+
class AsyncBelongsToAssociation < ORIG_BT
|
24
|
+
include AsyncUpdateCounters
|
39
25
|
end
|
40
26
|
|
41
|
-
|
27
|
+
class AsyncBelongsToPolymorphicAssociation < ORIG_BTP
|
28
|
+
include AsyncUpdateCounters
|
29
|
+
end
|
30
|
+
end
|
42
31
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
32
|
+
module AssociationReflectionTweaks
|
33
|
+
def association_class_with_async
|
34
|
+
if macro == :belongs_to && options[:async_counter_cache]
|
35
|
+
if options[:polymorphic]
|
36
|
+
Associations::AsyncBelongsToPolymorphicAssociation
|
37
|
+
else
|
38
|
+
Associations::AsyncBelongsToAssociation
|
50
39
|
end
|
40
|
+
else
|
41
|
+
association_class_without_async
|
51
42
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
43
|
+
end
|
44
|
+
|
45
|
+
def counter_cache_column_with_async
|
46
|
+
if options[:async_counter_cache] == true
|
47
|
+
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
48
|
+
elsif options[:async_counter_cache]
|
49
|
+
options[:async_counter_cache].to_s
|
50
|
+
else
|
51
|
+
counter_cache_column_without_async
|
59
52
|
end
|
60
|
-
after_commit(method_name, :on => :destroy)
|
61
53
|
end
|
62
54
|
|
63
|
-
def
|
64
|
-
|
55
|
+
def self.included(base)
|
56
|
+
base.class_eval do
|
57
|
+
alias_method_chain :association_class, :async
|
58
|
+
alias_method_chain :counter_cache_column, :async
|
59
|
+
end
|
65
60
|
end
|
66
61
|
end
|
67
62
|
|
68
|
-
|
69
|
-
|
63
|
+
module AsyncBuilderBehavior
|
64
|
+
def build_with_async
|
65
|
+
reflection = build_without_async
|
66
|
+
if options[:async_counter_cache] && options[:counter_cache]
|
67
|
+
raise 'Do not mix `:async_counter_cache` and `:counter_cache`.'
|
68
|
+
end
|
69
|
+
if options[:async_counter_cache]
|
70
|
+
add_async_counter_cache_callbacks(reflection)
|
71
|
+
end
|
72
|
+
reflection
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.included(base)
|
76
|
+
base.class_eval do
|
77
|
+
alias_method_chain :build, :async
|
78
|
+
self.valid_options += [:async_counter_cache]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def add_async_counter_cache_callbacks(reflection)
|
85
|
+
cache_column = reflection.counter_cache_column
|
86
|
+
name = self.name
|
87
|
+
|
88
|
+
method_name = "belongs_to_async_counter_cache_on_create_for_#{name}"
|
89
|
+
model.redefine_method(method_name) do
|
90
|
+
record = send(name)
|
91
|
+
ArResqueCounterCache::IncrementCountersWorker.cache_and_enqueue(
|
92
|
+
record.class.to_s,
|
93
|
+
record.id,
|
94
|
+
cache_column,
|
95
|
+
:increment
|
96
|
+
) unless record.nil?
|
97
|
+
end
|
98
|
+
model.after_commit(method_name, :on => :create)
|
99
|
+
|
100
|
+
method_name = "belongs_to_async_counter_cache_on_destroy_for_#{name}"
|
101
|
+
model.redefine_method(method_name) do
|
102
|
+
record = send(name)
|
103
|
+
ArResqueCounterCache::IncrementCountersWorker.cache_and_enqueue(
|
104
|
+
record.class.to_s,
|
105
|
+
record.id,
|
106
|
+
cache_column,
|
107
|
+
:decrement
|
108
|
+
) unless record.nil?
|
109
|
+
end
|
110
|
+
model.after_commit(method_name, :on => :destroy)
|
111
|
+
|
112
|
+
model.send(
|
113
|
+
:module_eval,
|
114
|
+
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)",
|
115
|
+
__FILE__,
|
116
|
+
__LINE__
|
117
|
+
)
|
118
|
+
end
|
70
119
|
end
|
71
120
|
end
|
72
121
|
end
|
122
|
+
|
123
|
+
ActiveRecord::Reflection::AssociationReflection.send(
|
124
|
+
:include,
|
125
|
+
ArResqueCounterCache::ActiveRecord::AssociationReflectionTweaks
|
126
|
+
)
|
127
|
+
ActiveRecord::Associations::Builder::BelongsTo.send(
|
128
|
+
:include,
|
129
|
+
ArResqueCounterCache::ActiveRecord::AsyncBuilderBehavior
|
130
|
+
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'resque'
|
2
2
|
require 'resque-lock-timeout'
|
3
3
|
|
4
|
-
module
|
4
|
+
module ArResqueCounterCache
|
5
5
|
|
6
6
|
# The default Resque queue is :counter_caches.
|
7
7
|
def self.resque_job_queue=(queue)
|
@@ -25,7 +25,7 @@ module ArAsyncCounterCache
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
#
|
28
|
+
# ArResqueCounterCache will very quickly increment a counter cache in Redis,
|
29
29
|
# which will then later be updated by a Resque job. Using
|
30
30
|
# require-lock-timeout, we can ensure that only one job per ___ is running
|
31
31
|
# at a time.
|
@@ -43,7 +43,7 @@ module ArAsyncCounterCache
|
|
43
43
|
elsif direction == :decrement
|
44
44
|
redis.decr(key)
|
45
45
|
else
|
46
|
-
raise ArgumentError, "Must call
|
46
|
+
raise ArgumentError, "Must call ArResqueCounterCache::IncrementCountersWorker with :increment or :decrement"
|
47
47
|
end
|
48
48
|
::Resque.enqueue(self, parent_class, id, column)
|
49
49
|
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION =
|
1
|
+
module ArResqueCounterCache
|
2
|
+
VERSION = '3.0.0.rc1'
|
3
3
|
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe ArResqueCounterCache::ActiveRecord do
|
4
4
|
|
5
5
|
context "callbacks" do
|
6
6
|
|
7
7
|
subject { User.create(:name => "Susan") }
|
8
8
|
|
9
9
|
it "should increment" do
|
10
|
-
|
10
|
+
ArResqueCounterCache::IncrementCountersWorker.should_receive(:cache_and_enqueue).with("User", subject.id, "posts_count", :increment)
|
11
11
|
subject.posts.create(:body => "I have a cat!")
|
12
12
|
end
|
13
13
|
|
14
14
|
it "should increment" do
|
15
|
-
|
15
|
+
ArResqueCounterCache::IncrementCountersWorker.stub(:cache_and_enqueue)
|
16
16
|
post = subject.posts.create(:body => "I have a cat!")
|
17
|
-
|
17
|
+
ArResqueCounterCache::IncrementCountersWorker.should_receive(:cache_and_enqueue).with("User", subject.id, "posts_count", :decrement)
|
18
18
|
post.destroy
|
19
19
|
end
|
20
20
|
end
|
data/spec/integration_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe "integration" do
|
|
28
28
|
# 3 for comments incrementing posts' comments counts
|
29
29
|
Resque.size(:testing).should == 8
|
30
30
|
|
31
|
-
# [
|
31
|
+
# [ArResqueCounterCache::IncrementCountersWorker, "User", 1, "posts_count"]
|
32
32
|
perform_next_job
|
33
33
|
@user1.reload.posts_count.should == 2
|
34
34
|
@user1.reload.comments_count.should == 0
|
@@ -37,7 +37,7 @@ describe "integration" do
|
|
37
37
|
@post1.reload.count_of_comments.should == 0
|
38
38
|
@post2.reload.count_of_comments.should == 0
|
39
39
|
|
40
|
-
# [
|
40
|
+
# [ArResqueCounterCache::IncrementCountersWorker, "Post", 1, "count_of_comments"]
|
41
41
|
perform_next_job
|
42
42
|
@user1.reload.posts_count.should == 2
|
43
43
|
@user1.reload.comments_count.should == 0
|
@@ -46,7 +46,7 @@ describe "integration" do
|
|
46
46
|
@post1.reload.count_of_comments.should == 2
|
47
47
|
@post2.reload.count_of_comments.should == 0
|
48
48
|
|
49
|
-
# [
|
49
|
+
# [ArResqueCounterCache::IncrementCountersWorker, "User", 2, "comments_count"]
|
50
50
|
perform_next_job
|
51
51
|
@user1.reload.posts_count.should == 2
|
52
52
|
@user1.reload.comments_count.should == 0
|
@@ -55,7 +55,7 @@ describe "integration" do
|
|
55
55
|
@post1.reload.count_of_comments.should == 2
|
56
56
|
@post2.reload.count_of_comments.should == 0
|
57
57
|
|
58
|
-
# [
|
58
|
+
# [ArResqueCounterCache::IncrementCountersWorker, "User", 1, "comments_count"]
|
59
59
|
perform_next_job
|
60
60
|
@user1.reload.posts_count.should == 2
|
61
61
|
@user1.reload.comments_count.should == 1
|
@@ -64,7 +64,7 @@ describe "integration" do
|
|
64
64
|
@post1.reload.count_of_comments.should == 2
|
65
65
|
@post2.reload.count_of_comments.should == 0
|
66
66
|
|
67
|
-
# [
|
67
|
+
# [ArResqueCounterCache::IncrementCountersWorker, "Post", 2, "count_of_comments"]
|
68
68
|
perform_next_job
|
69
69
|
@user1.reload.posts_count.should == 2
|
70
70
|
@user1.reload.comments_count.should == 1
|
data/spec/spec_helper.rb
CHANGED
@@ -32,7 +32,7 @@ Resque.redis = '127.0.0.1:9736'
|
|
32
32
|
|
33
33
|
RSpec.configure do |config|
|
34
34
|
config.before(:all) do
|
35
|
-
|
35
|
+
ArResqueCounterCache.resque_job_queue = :testing
|
36
36
|
end
|
37
37
|
config.before(:each) do
|
38
38
|
ActiveRecord::Base.silence { CreateModelsForTest.migrate(:up) }
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar-resque-counter-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 3.0.0.rc1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Aaron Gibralter
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70293402116900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.1'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70293402116900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: resque
|
27
|
-
requirement: &
|
27
|
+
requirement: &70293402116100 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70293402116100
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: resque-lock-timeout
|
38
|
-
requirement: &
|
38
|
+
requirement: &70293402115180 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.3.1
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70293402115180
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70293402114700 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 2.4.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70293402114700
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: sqlite3-ruby
|
60
|
-
requirement: &
|
60
|
+
requirement: &70293402114200 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: 1.3.3
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70293402114200
|
69
69
|
description: Increment ActiveRecord's counter cache column asynchronously using Resque
|
70
70
|
(and resque-lock-timeout).
|
71
71
|
email:
|
@@ -104,9 +104,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
|
-
- - ! '
|
107
|
+
- - ! '>'
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 1.3.1
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project: ar-resque-counter-cache
|
112
112
|
rubygems_version: 1.8.10
|