counter-cache 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +8 -8
- data/Appraisals +11 -0
- data/README.md +246 -2
- data/counter-cache.gemspec +1 -0
- data/doc/counter-cache-flow.png +0 -0
- data/gemfiles/activerecord_3.gemfile +7 -0
- data/gemfiles/activerecord_3.gemfile.lock +92 -0
- data/gemfiles/activerecord_4.0.gemfile +7 -0
- data/gemfiles/activerecord_4.0.gemfile.lock +98 -0
- data/gemfiles/activerecord_4.1.gemfile +7 -0
- data/gemfiles/activerecord_4.1.gemfile.lock +97 -0
- data/lib/counter/cache/counters/buffer_counter/relation_finder.rb +1 -1
- data/lib/counter/cache/version.rb +1 -1
- data/spec/features/counter_spec.rb +2 -2
- data/spec/lib/counter/cache/buffer_counter/relation_finder_spec.rb +1 -1
- metadata +25 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 005de7af8ad6de2ac4db333a51a0b69037b415da
|
4
|
+
data.tar.gz: 82e11a2ff62767df0275d60e12ef8f6208f76dc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a19850cde42e9e3d8413bc8d2b0f8ab134736c16df4171c788c2cc611363429d6830191d29027084bd93d2466d64dcda731949d2313d0f07311db87faa96dbce
|
7
|
+
data.tar.gz: e90d15bae6c2de8781dcb792e90f9b7f1e40fe4fdbefa7d83b9a5bc91515dbc01ced62ff68f3f3450716f0e23319e845a38767528a8e5b3bd221bcbd72da8460
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
language: ruby
|
2
1
|
rvm:
|
3
|
-
-
|
4
|
-
- 2.1.
|
5
|
-
|
6
|
-
-
|
2
|
+
- "1.9.3"
|
3
|
+
- "2.1.1"
|
4
|
+
gemfile:
|
5
|
+
- gemfiles/activerecord_3.gemfile
|
6
|
+
- gemfiles/activerecord_4.0.gemfile
|
7
|
+
- gemfiles/activerecord_4.1.gemfile
|
7
8
|
notifications:
|
8
|
-
email:
|
9
|
-
|
10
|
-
webhooks: "http://ci.wanelo.com/projects/2f4c3438-a0bc-434e-a9fb-30c0c335b432/status"
|
9
|
+
email: false
|
10
|
+
webhooks: "http://ci.wanelo.com/projects/d6108a07-a333-487a-97ed-0df78d8464c0/status"
|
data/Appraisals
ADDED
data/README.md
CHANGED
@@ -1,6 +1,82 @@
|
|
1
1
|
# Counter::Cache
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/wanelo/counter-cache)
|
4
|
+
|
5
|
+
Counting things is hard, counting them at scale is even harder, so control when things are counted.
|
6
|
+
|
7
|
+
[Rails Counter Caches](http://railscasts.com/episodes/23-counter-cache-column) are a convenient way to keep counters on
|
8
|
+
models that have many children. Without them, you always do live counts, which do not scale. But at high scale, Rails
|
9
|
+
counter caches create update contention on singe models, especially for social sites where any single model might become
|
10
|
+
extremely popular. Many web requests trying to update the same row creates database deadlocks and kills performance due
|
11
|
+
to locking and an uncontrollable increase in iops.
|
12
|
+
|
13
|
+
This library provides all the benefits of rails counter cache, without the penalty of the contention on updates,
|
14
|
+
by serializing, buffering, and delaying updates via a queue. Counts becoming slightly less realtime, but with a guarantee that
|
15
|
+
single models will never be updated more than once in certain time periods.
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
By default, a Buffer Counter is used which implements two modes of counting. The two modes are deferred and recalculation.
|
20
|
+
|
21
|
+
IMPORTANT: If Sidekiq is to be used as the delayed job framework, using `sidekiq-unique-jobs` is essential: https://github.com/mhenrixon/sidekiq-unique-jobs
|
22
|
+
|
23
|
+
### Mode: Deferred
|
24
|
+
|
25
|
+
Initial mode that is used to provide roughly realtime counters.
|
26
|
+
|
27
|
+
This mode is meant to provide very reasonably up to date counters using values buffered into Redis, without asking the database
|
28
|
+
for the count at all. An example of how this works is described:
|
29
|
+
|
30
|
+
Scenario: User has many posts. We want to keep track of the number of posts on the user model (posts_count column).
|
31
|
+
|
32
|
+
When a post is created:
|
33
|
+
|
34
|
+
1. Increment a key in Redis that corresponds to the field and user that relates to the post.
|
35
|
+
2. Enqueue a delayed job that will later reconcile the counter column based on the key in redis.
|
36
|
+
3. When the job runs, it picks up the value from redis (which can be zero or more) and adds the value to user.posts_count
|
37
|
+
column on the associated model.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
user = User.find_by_id(100)
|
41
|
+
user.posts_count # 10
|
42
|
+
user.posts.create(...) # => Job is enqueued
|
43
|
+
user.posts.create(...) # => Job is already enqueued
|
44
|
+
|
45
|
+
# come back later (after a delay)
|
46
|
+
user = User.find_by_id(100)
|
47
|
+
user.posts_count # 12
|
48
|
+
```
|
49
|
+
|
50
|
+
### Mode: Recalculation
|
51
|
+
|
52
|
+
Runs later and ensures values are completely up to date.
|
53
|
+
|
54
|
+
This mode is used to compensate for transient errors that may cause the deferred counters to drift from the actual
|
55
|
+
values. The exact reasons this happens are undefined, redis could hang, go away, the universe could skip ahead in time,
|
56
|
+
who knows.
|
57
|
+
|
58
|
+
Using the same scenario as above:
|
59
|
+
|
60
|
+
Scenario: User has many posts. We want to keep track of the number of posts on the user model (posts_count column).
|
61
|
+
|
62
|
+
1. Enqueue a job that is delayed by many hours (customizable)
|
63
|
+
2. When the job runs, run a full count query to find the true count from the database and save the value to the database.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
user = User.find_by_id(100)
|
67
|
+
user.posts_count # 10
|
68
|
+
user.posts.create(...)
|
69
|
+
user.posts.create(...)
|
70
|
+
|
71
|
+
# redis crashes, world explodes, etc.. we miss on deferred update.
|
72
|
+
|
73
|
+
user = User.find_by_id(100)
|
74
|
+
user.posts_count # 11, due to only one deferred update having run.
|
75
|
+
|
76
|
+
# come back later in a couple hours
|
77
|
+
user = User.find_by_id(100)
|
78
|
+
user.posts_count # 12
|
79
|
+
```
|
4
80
|
|
5
81
|
## Installation
|
6
82
|
|
@@ -18,7 +94,163 @@ Or install it yourself as:
|
|
18
94
|
|
19
95
|
## Usage
|
20
96
|
|
21
|
-
|
97
|
+
Counter caches are configured on the models from the perspective of the child model to the parent that contains the counter.
|
98
|
+
|
99
|
+
#### Basic Counter with recalculation:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class Post
|
103
|
+
include Counter::Cache
|
104
|
+
|
105
|
+
counter_cache_on column: :posts_count, # users.posts_count
|
106
|
+
relation: :user,
|
107
|
+
relation_class_name: "User",
|
108
|
+
method: :calculate_posts_count, # This is a method on the user.
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
#### To control when recalculation happens:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class Post
|
116
|
+
include Counter::Cache
|
117
|
+
|
118
|
+
counter_cache_on column: :posts_count, # users.posts_count
|
119
|
+
relation: :user,
|
120
|
+
relation_class_name: "User",
|
121
|
+
method: :calculate_posts_count, # This is a method on the user.
|
122
|
+
recalculation: true|false, # whether to ever recalculate this counter.
|
123
|
+
recalculation_delay: 10.seconds # Only a hard value that defines when to perform a full recalculation.
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
#### To control when the deferred job runs:
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class Post
|
131
|
+
include Counter::Cache
|
132
|
+
|
133
|
+
counter_cache_on column: :posts_count, # users.posts_count
|
134
|
+
relation: :user,
|
135
|
+
relation_class_name: "User",
|
136
|
+
method: :calculate_posts_count, # This is a method on the user.
|
137
|
+
wait: 10.seconds # This can be a hard value
|
138
|
+
|
139
|
+
counter_cache_on column: :posts_count, # users.posts_count
|
140
|
+
relation: :user,
|
141
|
+
relation_class_name: "User",
|
142
|
+
method: :calculate_posts_count, # This is a method on the user.
|
143
|
+
wait: ->(user) { user.posts_count * 10 } # .. or a proc, in this case, the more posts a user has, the less frequently it will be updated.
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
#### To control if an update should even happen:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class Post
|
151
|
+
include Counter::Cache
|
152
|
+
|
153
|
+
counter_cache_on column: :posts_count, # users.posts_count
|
154
|
+
relation: :user,
|
155
|
+
relation_class_name: "User",
|
156
|
+
method: :calculate_posts_count, # This is a method on the user.
|
157
|
+
if: ->(post) { post.public? ? false : true }
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
#### Polymorphism (because YAY)
|
162
|
+
|
163
|
+
Setting `polymorphic: true`, will ask ActiveRecord what the class is (User, Store), based on followee_type, and update
|
164
|
+
the appropriate model. So if a user is followed, then that users followers_count will increment.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
class User
|
168
|
+
attr_accessible :followers_count
|
169
|
+
end
|
170
|
+
|
171
|
+
class Store
|
172
|
+
attr_accessible :followers_count
|
173
|
+
end
|
174
|
+
|
175
|
+
class Follow
|
176
|
+
attr_accessible :user_id, :followee_id, :followee_type
|
177
|
+
|
178
|
+
belongs_to :followee, polymorphic: true
|
179
|
+
|
180
|
+
include Counter::Cache
|
181
|
+
|
182
|
+
counter_cache_on column: :followers_count,
|
183
|
+
relation: :followee,
|
184
|
+
polymorphic: true
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
## Configuration
|
189
|
+
|
190
|
+
In an initializer such as `config/initializers/counter_cache.rb`, write the configuration as:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
Counter::Cache.configure do |c|
|
194
|
+
c.default_worker_adapter = MyCustomWorkAdapter
|
195
|
+
c.recalculation_delay = 6.hours # Default delay for recalculations
|
196
|
+
c.redis_pool = Redis.new
|
197
|
+
c.counting_data_store = MyCustomDataStore # Default is Counter::Cache::Redis
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
### default_worker_adapter
|
202
|
+
|
203
|
+
The worker adapter allows you to control how jobs are delayed/enqueued for later execution. Three options are passed:
|
204
|
+
|
205
|
+
- delay: This is the delay in seconds that the execution should be delayed. Can be ignored or adjusted. We pass this to
|
206
|
+
sidekiq.
|
207
|
+
- base_class: This is the class name of the source object.
|
208
|
+
- options: This will be a hash of options that should be passed to the instance of the counter.
|
209
|
+
|
210
|
+
An example of a dummy adapter is like so:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
class TestWorkerAdapter
|
214
|
+
def enqueue(delay, base_class, options)
|
215
|
+
options[:source_object_class_name] = base_class.constantize
|
216
|
+
counter_class = options[:counter].constantize # options[:counter] is the class name of the counter that called the adapter.
|
217
|
+
counter = counter_class.new(nil, options)
|
218
|
+
counter.save!
|
219
|
+
end
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
An example of a dummy adapter that uses Sidekiq is like so:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
class CounterWorker
|
227
|
+
include Sidekiq::Worker
|
228
|
+
|
229
|
+
def perform(base_class, options)
|
230
|
+
options.symbolize_keys! # From ActiveSupport, Sidekiq looses symbol information from hashes.
|
231
|
+
options[:source_object_class_name] = base_class.constantize
|
232
|
+
counter_class = options[:counter].constantize # options[:counter] is the class name of the counter that called the adapter.
|
233
|
+
counter = counter_class.new(nil, options)
|
234
|
+
counter.save!
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.enqueue(delay, base_class, options)
|
238
|
+
perform_in(delay, base_class, options)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
### recalculation_delay
|
244
|
+
|
245
|
+
This should be set to the default delay for recalculations, in seconds.
|
246
|
+
|
247
|
+
### redis_pool
|
248
|
+
|
249
|
+
This can either be a single redis connection or a ConnectionPool instance (https://github.com/mperham/connection_pool).
|
250
|
+
|
251
|
+
### counting_data_store
|
252
|
+
|
253
|
+
This defaults to Counter::Cache::Redis but can be set to anything. The Redis store describes what the API would be.
|
22
254
|
|
23
255
|
## Contributing
|
24
256
|
|
@@ -27,3 +259,15 @@ TODO: Write usage instructions here
|
|
27
259
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
260
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
261
|
5. Create a new Pull Request
|
262
|
+
|
263
|
+
### Running specs:
|
264
|
+
|
265
|
+
Appraisal is used to test against multiple versions of activerecord. 3.2, 4.0, and 4.1 are currently supported.
|
266
|
+
|
267
|
+
To install dependencies:
|
268
|
+
|
269
|
+
$ bundle exec appraisal install
|
270
|
+
|
271
|
+
To run specs across versions:
|
272
|
+
|
273
|
+
$ bundle exec appraisal rspec
|
data/counter-cache.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "activerecord", ">= 3.0"
|
22
22
|
|
23
|
+
spec.add_development_dependency "appraisal"
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.6"
|
24
25
|
spec.add_development_dependency "rake"
|
25
26
|
spec.add_development_dependency "rspec", ">= 3.0"
|
Binary file
|
@@ -0,0 +1,92 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
counter-cache (0.0.2)
|
5
|
+
activerecord (>= 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (3.2.19)
|
11
|
+
activesupport (= 3.2.19)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
activerecord (3.2.19)
|
14
|
+
activemodel (= 3.2.19)
|
15
|
+
activesupport (= 3.2.19)
|
16
|
+
arel (~> 3.0.2)
|
17
|
+
tzinfo (~> 0.3.29)
|
18
|
+
activesupport (3.2.19)
|
19
|
+
i18n (~> 0.6, >= 0.6.4)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
appraisal (1.0.0)
|
22
|
+
bundler
|
23
|
+
rake
|
24
|
+
thor (>= 0.14.0)
|
25
|
+
arel (3.0.3)
|
26
|
+
builder (3.0.4)
|
27
|
+
celluloid (0.15.2)
|
28
|
+
timers (~> 1.1.0)
|
29
|
+
coderay (1.1.0)
|
30
|
+
diff-lcs (1.2.5)
|
31
|
+
fakeredis (0.5.0)
|
32
|
+
redis (~> 3.0)
|
33
|
+
ffi (1.9.3)
|
34
|
+
formatador (0.2.5)
|
35
|
+
guard (2.6.1)
|
36
|
+
formatador (>= 0.2.4)
|
37
|
+
listen (~> 2.7)
|
38
|
+
lumberjack (~> 1.0)
|
39
|
+
pry (>= 0.9.12)
|
40
|
+
thor (>= 0.18.1)
|
41
|
+
guard-rspec (4.3.1)
|
42
|
+
guard (~> 2.1)
|
43
|
+
rspec (>= 2.14, < 4.0)
|
44
|
+
i18n (0.6.11)
|
45
|
+
listen (2.7.9)
|
46
|
+
celluloid (>= 0.15.2)
|
47
|
+
rb-fsevent (>= 0.9.3)
|
48
|
+
rb-inotify (>= 0.9)
|
49
|
+
lumberjack (1.0.9)
|
50
|
+
method_source (0.8.2)
|
51
|
+
multi_json (1.10.1)
|
52
|
+
pry (0.10.0)
|
53
|
+
coderay (~> 1.1.0)
|
54
|
+
method_source (~> 0.8.1)
|
55
|
+
slop (~> 3.4)
|
56
|
+
rake (10.3.2)
|
57
|
+
rb-fsevent (0.9.4)
|
58
|
+
rb-inotify (0.9.5)
|
59
|
+
ffi (>= 0.5.0)
|
60
|
+
redis (3.1.0)
|
61
|
+
rspec (3.0.0)
|
62
|
+
rspec-core (~> 3.0.0)
|
63
|
+
rspec-expectations (~> 3.0.0)
|
64
|
+
rspec-mocks (~> 3.0.0)
|
65
|
+
rspec-core (3.0.3)
|
66
|
+
rspec-support (~> 3.0.0)
|
67
|
+
rspec-expectations (3.0.3)
|
68
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
69
|
+
rspec-support (~> 3.0.0)
|
70
|
+
rspec-mocks (3.0.3)
|
71
|
+
rspec-support (~> 3.0.0)
|
72
|
+
rspec-support (3.0.3)
|
73
|
+
slop (3.6.0)
|
74
|
+
sqlite3 (1.3.9)
|
75
|
+
thor (0.19.1)
|
76
|
+
timers (1.1.0)
|
77
|
+
tzinfo (0.3.39)
|
78
|
+
|
79
|
+
PLATFORMS
|
80
|
+
ruby
|
81
|
+
|
82
|
+
DEPENDENCIES
|
83
|
+
activerecord (~> 3.2.19)
|
84
|
+
appraisal
|
85
|
+
bundler (~> 1.6)
|
86
|
+
counter-cache!
|
87
|
+
fakeredis
|
88
|
+
guard
|
89
|
+
guard-rspec
|
90
|
+
rake
|
91
|
+
rspec (>= 3.0)
|
92
|
+
sqlite3
|
@@ -0,0 +1,98 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
counter-cache (0.0.2)
|
5
|
+
activerecord (>= 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.0.8)
|
11
|
+
activesupport (= 4.0.8)
|
12
|
+
builder (~> 3.1.0)
|
13
|
+
activerecord (4.0.8)
|
14
|
+
activemodel (= 4.0.8)
|
15
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
16
|
+
activesupport (= 4.0.8)
|
17
|
+
arel (~> 4.0.0)
|
18
|
+
activerecord-deprecated_finders (1.0.3)
|
19
|
+
activesupport (4.0.8)
|
20
|
+
i18n (~> 0.6, >= 0.6.9)
|
21
|
+
minitest (~> 4.2)
|
22
|
+
multi_json (~> 1.3)
|
23
|
+
thread_safe (~> 0.1)
|
24
|
+
tzinfo (~> 0.3.37)
|
25
|
+
appraisal (1.0.0)
|
26
|
+
bundler
|
27
|
+
rake
|
28
|
+
thor (>= 0.14.0)
|
29
|
+
arel (4.0.2)
|
30
|
+
builder (3.1.4)
|
31
|
+
celluloid (0.15.2)
|
32
|
+
timers (~> 1.1.0)
|
33
|
+
coderay (1.1.0)
|
34
|
+
diff-lcs (1.2.5)
|
35
|
+
fakeredis (0.5.0)
|
36
|
+
redis (~> 3.0)
|
37
|
+
ffi (1.9.3)
|
38
|
+
formatador (0.2.5)
|
39
|
+
guard (2.6.1)
|
40
|
+
formatador (>= 0.2.4)
|
41
|
+
listen (~> 2.7)
|
42
|
+
lumberjack (~> 1.0)
|
43
|
+
pry (>= 0.9.12)
|
44
|
+
thor (>= 0.18.1)
|
45
|
+
guard-rspec (4.3.1)
|
46
|
+
guard (~> 2.1)
|
47
|
+
rspec (>= 2.14, < 4.0)
|
48
|
+
i18n (0.6.11)
|
49
|
+
listen (2.7.9)
|
50
|
+
celluloid (>= 0.15.2)
|
51
|
+
rb-fsevent (>= 0.9.3)
|
52
|
+
rb-inotify (>= 0.9)
|
53
|
+
lumberjack (1.0.9)
|
54
|
+
method_source (0.8.2)
|
55
|
+
minitest (4.7.5)
|
56
|
+
multi_json (1.10.1)
|
57
|
+
pry (0.10.0)
|
58
|
+
coderay (~> 1.1.0)
|
59
|
+
method_source (~> 0.8.1)
|
60
|
+
slop (~> 3.4)
|
61
|
+
rake (10.3.2)
|
62
|
+
rb-fsevent (0.9.4)
|
63
|
+
rb-inotify (0.9.5)
|
64
|
+
ffi (>= 0.5.0)
|
65
|
+
redis (3.1.0)
|
66
|
+
rspec (3.0.0)
|
67
|
+
rspec-core (~> 3.0.0)
|
68
|
+
rspec-expectations (~> 3.0.0)
|
69
|
+
rspec-mocks (~> 3.0.0)
|
70
|
+
rspec-core (3.0.3)
|
71
|
+
rspec-support (~> 3.0.0)
|
72
|
+
rspec-expectations (3.0.3)
|
73
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
74
|
+
rspec-support (~> 3.0.0)
|
75
|
+
rspec-mocks (3.0.3)
|
76
|
+
rspec-support (~> 3.0.0)
|
77
|
+
rspec-support (3.0.3)
|
78
|
+
slop (3.6.0)
|
79
|
+
sqlite3 (1.3.9)
|
80
|
+
thor (0.19.1)
|
81
|
+
thread_safe (0.3.4)
|
82
|
+
timers (1.1.0)
|
83
|
+
tzinfo (0.3.40)
|
84
|
+
|
85
|
+
PLATFORMS
|
86
|
+
ruby
|
87
|
+
|
88
|
+
DEPENDENCIES
|
89
|
+
activerecord (~> 4.0.8)
|
90
|
+
appraisal
|
91
|
+
bundler (~> 1.6)
|
92
|
+
counter-cache!
|
93
|
+
fakeredis
|
94
|
+
guard
|
95
|
+
guard-rspec
|
96
|
+
rake
|
97
|
+
rspec (>= 3.0)
|
98
|
+
sqlite3
|
@@ -0,0 +1,97 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
counter-cache (0.0.2)
|
5
|
+
activerecord (>= 3.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activemodel (4.1.4)
|
11
|
+
activesupport (= 4.1.4)
|
12
|
+
builder (~> 3.1)
|
13
|
+
activerecord (4.1.4)
|
14
|
+
activemodel (= 4.1.4)
|
15
|
+
activesupport (= 4.1.4)
|
16
|
+
arel (~> 5.0.0)
|
17
|
+
activesupport (4.1.4)
|
18
|
+
i18n (~> 0.6, >= 0.6.9)
|
19
|
+
json (~> 1.7, >= 1.7.7)
|
20
|
+
minitest (~> 5.1)
|
21
|
+
thread_safe (~> 0.1)
|
22
|
+
tzinfo (~> 1.1)
|
23
|
+
appraisal (1.0.0)
|
24
|
+
bundler
|
25
|
+
rake
|
26
|
+
thor (>= 0.14.0)
|
27
|
+
arel (5.0.1.20140414130214)
|
28
|
+
builder (3.2.2)
|
29
|
+
celluloid (0.15.2)
|
30
|
+
timers (~> 1.1.0)
|
31
|
+
coderay (1.1.0)
|
32
|
+
diff-lcs (1.2.5)
|
33
|
+
fakeredis (0.5.0)
|
34
|
+
redis (~> 3.0)
|
35
|
+
ffi (1.9.3)
|
36
|
+
formatador (0.2.5)
|
37
|
+
guard (2.6.1)
|
38
|
+
formatador (>= 0.2.4)
|
39
|
+
listen (~> 2.7)
|
40
|
+
lumberjack (~> 1.0)
|
41
|
+
pry (>= 0.9.12)
|
42
|
+
thor (>= 0.18.1)
|
43
|
+
guard-rspec (4.3.1)
|
44
|
+
guard (~> 2.1)
|
45
|
+
rspec (>= 2.14, < 4.0)
|
46
|
+
i18n (0.6.11)
|
47
|
+
json (1.8.1)
|
48
|
+
listen (2.7.9)
|
49
|
+
celluloid (>= 0.15.2)
|
50
|
+
rb-fsevent (>= 0.9.3)
|
51
|
+
rb-inotify (>= 0.9)
|
52
|
+
lumberjack (1.0.9)
|
53
|
+
method_source (0.8.2)
|
54
|
+
minitest (5.4.0)
|
55
|
+
pry (0.10.0)
|
56
|
+
coderay (~> 1.1.0)
|
57
|
+
method_source (~> 0.8.1)
|
58
|
+
slop (~> 3.4)
|
59
|
+
rake (10.3.2)
|
60
|
+
rb-fsevent (0.9.4)
|
61
|
+
rb-inotify (0.9.5)
|
62
|
+
ffi (>= 0.5.0)
|
63
|
+
redis (3.1.0)
|
64
|
+
rspec (3.0.0)
|
65
|
+
rspec-core (~> 3.0.0)
|
66
|
+
rspec-expectations (~> 3.0.0)
|
67
|
+
rspec-mocks (~> 3.0.0)
|
68
|
+
rspec-core (3.0.3)
|
69
|
+
rspec-support (~> 3.0.0)
|
70
|
+
rspec-expectations (3.0.3)
|
71
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
72
|
+
rspec-support (~> 3.0.0)
|
73
|
+
rspec-mocks (3.0.3)
|
74
|
+
rspec-support (~> 3.0.0)
|
75
|
+
rspec-support (3.0.3)
|
76
|
+
slop (3.6.0)
|
77
|
+
sqlite3 (1.3.9)
|
78
|
+
thor (0.19.1)
|
79
|
+
thread_safe (0.3.4)
|
80
|
+
timers (1.1.0)
|
81
|
+
tzinfo (1.2.1)
|
82
|
+
thread_safe (~> 0.1)
|
83
|
+
|
84
|
+
PLATFORMS
|
85
|
+
ruby
|
86
|
+
|
87
|
+
DEPENDENCIES
|
88
|
+
activerecord (~> 4.1.4)
|
89
|
+
appraisal
|
90
|
+
bundler (~> 1.6)
|
91
|
+
counter-cache!
|
92
|
+
fakeredis
|
93
|
+
guard
|
94
|
+
guard-rspec
|
95
|
+
rake
|
96
|
+
rspec (>= 3.0)
|
97
|
+
sqlite3
|
@@ -20,7 +20,7 @@ module Counter
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def reflection_type
|
23
|
-
source_object.reflections[options.relation.to_sym].class_name.to_s.camelize # let AR give us the correct class name :)
|
23
|
+
source_object.class.reflections[options.relation.to_sym].class_name.to_s.camelize # let AR give us the correct class name :)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -6,7 +6,7 @@ require 'fakeredis'
|
|
6
6
|
RSpec.describe "Counting" do
|
7
7
|
|
8
8
|
before do
|
9
|
-
|
9
|
+
CreateModelsForTest.migrate(:up)
|
10
10
|
Counter::Cache.configure do |c|
|
11
11
|
c.redis_pool = Redis.new
|
12
12
|
c.default_worker_adapter = TestWorkerAdapter.new
|
@@ -14,7 +14,7 @@ RSpec.describe "Counting" do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
after do
|
17
|
-
|
17
|
+
CreateModelsForTest.migrate(:down)
|
18
18
|
end
|
19
19
|
|
20
20
|
let(:user) { User.create }
|
@@ -29,7 +29,7 @@ RSpec.describe Counter::Cache::Counters::BufferCounter::RelationFinder do
|
|
29
29
|
before do
|
30
30
|
reflection = double
|
31
31
|
expect(reflection).to receive_message_chain("class_name.to_s.camelize") { "Boo" }
|
32
|
-
expect(source_object).to receive(:reflections).and_return({:boo => reflection})
|
32
|
+
expect(source_object.class).to receive(:reflections).and_return({:boo => reflection})
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'asks active record for the class name' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: counter-cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Henry & Matt Camuto
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: appraisal
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -132,12 +146,20 @@ files:
|
|
132
146
|
- ".gitignore"
|
133
147
|
- ".rspec"
|
134
148
|
- ".travis.yml"
|
149
|
+
- Appraisals
|
135
150
|
- Gemfile
|
136
151
|
- Guardfile
|
137
152
|
- LICENSE.txt
|
138
153
|
- README.md
|
139
154
|
- Rakefile
|
140
155
|
- counter-cache.gemspec
|
156
|
+
- doc/counter-cache-flow.png
|
157
|
+
- gemfiles/activerecord_3.gemfile
|
158
|
+
- gemfiles/activerecord_3.gemfile.lock
|
159
|
+
- gemfiles/activerecord_4.0.gemfile
|
160
|
+
- gemfiles/activerecord_4.0.gemfile.lock
|
161
|
+
- gemfiles/activerecord_4.1.gemfile
|
162
|
+
- gemfiles/activerecord_4.1.gemfile.lock
|
141
163
|
- lib/counter/cache.rb
|
142
164
|
- lib/counter/cache/active_record_updater.rb
|
143
165
|
- lib/counter/cache/config.rb
|
@@ -185,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
185
207
|
version: '0'
|
186
208
|
requirements: []
|
187
209
|
rubyforge_project:
|
188
|
-
rubygems_version: 2.2.
|
210
|
+
rubygems_version: 2.2.0
|
189
211
|
signing_key:
|
190
212
|
specification_version: 4
|
191
213
|
summary: Counting is hard.
|