redis_counters 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/CHANGELOG.md +38 -0
  4. data/Gemfile +3 -0
  5. data/Makefile +14 -0
  6. data/README.md +320 -0
  7. data/Rakefile +15 -0
  8. data/lib/redis_counters.rb +21 -0
  9. data/lib/redis_counters/base_counter.rb +84 -0
  10. data/lib/redis_counters/bucket.rb +59 -0
  11. data/lib/redis_counters/cluster.rb +22 -0
  12. data/lib/redis_counters/clusterize_and_partitionize.rb +194 -0
  13. data/lib/redis_counters/hash_counter.rb +70 -0
  14. data/lib/redis_counters/partition.rb +16 -0
  15. data/lib/redis_counters/unique_hash_counter.rb +51 -0
  16. data/lib/redis_counters/unique_values_lists/base.rb +57 -0
  17. data/lib/redis_counters/unique_values_lists/blocking.rb +167 -0
  18. data/lib/redis_counters/unique_values_lists/expirable.rb +155 -0
  19. data/lib/redis_counters/unique_values_lists/non_blocking.rb +91 -0
  20. data/lib/redis_counters/version.rb +3 -0
  21. data/redis_counters.gemspec +31 -0
  22. data/spec/redis_counters/base_spec.rb +29 -0
  23. data/spec/redis_counters/hash_counter_spec.rb +462 -0
  24. data/spec/redis_counters/unique_hash_counter_spec.rb +83 -0
  25. data/spec/redis_counters/unique_values_lists/blocking_spec.rb +94 -0
  26. data/spec/redis_counters/unique_values_lists/expirable_spec.rb +6 -0
  27. data/spec/redis_counters/unique_values_lists/non_blicking_spec.rb +6 -0
  28. data/spec/spec_helper.rb +24 -0
  29. data/spec/support/unique_values_lists/common.rb +563 -0
  30. data/spec/support/unique_values_lists/expirable.rb +162 -0
  31. data/spec/support/unique_values_lists/set.rb +119 -0
  32. data/tasks/audit.rake +6 -0
  33. data/tasks/cane.rake +12 -0
  34. data/tasks/changelog.rake +7 -0
  35. data/tasks/coverage.rake +21 -0
  36. data/tasks/support.rb +24 -0
  37. metadata +242 -0
@@ -0,0 +1,3 @@
1
+ module RedisCounters
2
+ VERSION = '1.3.0'
3
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redis_counters/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'redis_counters'
8
+ spec.version = RedisCounters::VERSION
9
+ spec.authors = ['Artem Napolskih']
10
+ spec.email = %w(napolskih@gmail.com)
11
+ spec.summary = %q{Redis Counters}
12
+ spec.homepage = 'https://github.com/abak-press/redis_counters'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_dependency 'activesupport', '>= 3.0'
20
+
21
+ spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec', '~> 2.14.0'
24
+ spec.add_development_dependency 'mock_redis'
25
+ spec.add_development_dependency 'timecop'
26
+ spec.add_development_dependency 'codeclimate-test-reporter', '>= 0.4.1'
27
+ spec.add_development_dependency 'simplecov'
28
+ spec.add_development_dependency 'cane', '>= 2.6.0'
29
+ spec.add_development_dependency 'bundler-audit'
30
+ spec.add_development_dependency 'apress-changelogger'
31
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisCounters::BaseCounter do
4
+ let(:redis) { MockRedis.new }
5
+
6
+ let(:options) { {
7
+ :counter_class => RedisCounters::HashCounter,
8
+ :counter_name => :counter_name,
9
+ :field_name => :field_name
10
+ } }
11
+
12
+ let(:counter) { described_class.new(redis, options) }
13
+
14
+ context '.create' do
15
+ it { expect(described_class.create(redis, options)).to be_a RedisCounters::HashCounter }
16
+ end
17
+
18
+ context '#process' do
19
+ it { expect(described_class.create(redis, options)).to respond_to :process }
20
+ end
21
+
22
+ context 'when counter_name not given' do
23
+ let(:options) { {
24
+ :field_name => :field_name
25
+ } }
26
+
27
+ it { expect { counter }.to raise_error KeyError }
28
+ end
29
+ end
@@ -0,0 +1,462 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisCounters::HashCounter do
4
+ let(:redis) { MockRedis.new }
5
+ let(:value) { rand(10) + 1 }
6
+ let(:options) { { :counter_name => :test_counter, :field_name => :test_field } }
7
+ let(:counter) { described_class.new(redis, options) }
8
+
9
+ context 'when field_name and group_keys not given' do
10
+ let(:options) { { :counter_name => :test_counter } }
11
+
12
+ it { expect { counter.process }.to raise_error KeyError }
13
+ end
14
+
15
+ context 'when only field_name given' do
16
+ let(:options) { {
17
+ :counter_name => :test_counter,
18
+ :field_name => :test_field
19
+ } }
20
+
21
+ before { value.times { counter.process } }
22
+
23
+ it { expect(redis.keys('*')).to have(1).key }
24
+ it { expect(redis.keys('*').first).to eq 'test_counter' }
25
+ it { expect(redis.hexists('test_counter', 'test_field')).to be_true }
26
+ it { expect(redis.hget('test_counter', 'test_field')).to eq value.to_s }
27
+ end
28
+
29
+ context 'when group_keys given' do
30
+ context 'when field_name not given' do
31
+ let(:options) { {
32
+ :counter_name => :test_counter,
33
+ :group_keys => [:param1, :param2]
34
+ } }
35
+
36
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
37
+ before { value.times { counter.process(:param1 => 11, :param2 => nil, :param3 => 33) } }
38
+
39
+ it { expect(redis.keys('*')).to have(1).key }
40
+ it { expect(redis.keys('*').first).to eq 'test_counter' }
41
+ it { expect(redis.hexists('test_counter', '11:22')).to be_true }
42
+ it { expect(redis.hget('test_counter', '11:22')).to eq value.to_s }
43
+ it { expect(redis.hexists('test_counter', '11:')).to be_true }
44
+ it { expect(redis.hget('test_counter', '11:')).to eq value.to_s }
45
+ end
46
+
47
+ context 'when exists group_keys given' do
48
+ let(:options) { {
49
+ :counter_name => :test_counter,
50
+ :field_name => :test_field,
51
+ :group_keys => [:param1, :param2]
52
+ } }
53
+
54
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
55
+ before { 2.times { counter.process(:param1 => 12, :param2 => 22, :param3 => 33) } }
56
+ before { 1.times { counter.process(:param1 => 12, :param2 => nil, :param3 => 33) } }
57
+ before { 1.times { counter.process(:param1 => 12, :param2 => '', :param3 => 33) } }
58
+
59
+ it { expect(redis.keys('*')).to have(1).key }
60
+ it { expect(redis.keys('*').first).to eq 'test_counter' }
61
+ it { expect(redis.hexists('test_counter', '11:22')).to be_true }
62
+ it { expect(redis.hget('test_counter', '11:22')).to eq value.to_s }
63
+ it { expect(redis.hexists('test_counter', '12:22')).to be_true }
64
+ it { expect(redis.hget('test_counter', '12:22')).to eq 2.to_s }
65
+ it { expect(redis.hexists('test_counter', '12:')).to be_true }
66
+ it { expect(redis.hget('test_counter', '12:')).to eq 2.to_s }
67
+ end
68
+
69
+ context 'when not exists group_keys given' do
70
+ let(:options) { {
71
+ :counter_name => :test_counter,
72
+ :field_name => :test_field,
73
+ :group_keys => [:param1, :param4]
74
+ } }
75
+
76
+ it { expect { counter.process }.to raise_error KeyError }
77
+ end
78
+ end
79
+
80
+ context 'when use partition' do
81
+ context 'when all partition_keys is Symbols' do
82
+ let(:options) { {
83
+ :counter_name => :test_counter,
84
+ :field_name => :test_field,
85
+ :partition_keys => [:param1, :param2]
86
+ } }
87
+
88
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
89
+ before { 3.times { counter.process(:param1 => 21, :param2 => 22, :param3 => 33) } }
90
+
91
+ it { expect(redis.keys('*')).to have(2).key }
92
+ it { expect(redis.keys('*').first).to eq 'test_counter:11:22' }
93
+ it { expect(redis.keys('*').last).to eq 'test_counter:21:22' }
94
+ it { expect(redis.hexists('test_counter:11:22', 'test_field')).to be_true }
95
+ it { expect(redis.hget('test_counter:11:22', 'test_field')).to eq value.to_s }
96
+ it { expect(redis.hexists('test_counter:21:22', 'test_field')).to be_true }
97
+ it { expect(redis.hget('test_counter:21:22', 'test_field')).to eq 3.to_s }
98
+ end
99
+
100
+ context 'when all partition_keys is Proc' do
101
+ let(:options) { {
102
+ :counter_name => :test_counter,
103
+ :field_name => :test_field,
104
+ :partition_keys => proc { |params| params[:param1].odd?.to_s }
105
+ } }
106
+
107
+ before { 2.times { counter.process(:param1 => 1, :param2 => 2) } }
108
+ before { 3.times { counter.process(:param1 => 2, :param2 => 2) } }
109
+
110
+ it { expect(redis.keys('*')).to have(2).key }
111
+ it { expect(redis.keys('*').first).to eq 'test_counter:true' }
112
+ it { expect(redis.keys('*').last).to eq 'test_counter:false' }
113
+ it { expect(redis.hexists('test_counter:true', 'test_field')).to be_true }
114
+ it { expect(redis.hget('test_counter:true', 'test_field')).to eq 2.to_s }
115
+ it { expect(redis.hexists('test_counter:false', 'test_field')).to be_true }
116
+ it { expect(redis.hget('test_counter:false', 'test_field')).to eq 3.to_s }
117
+ end
118
+
119
+ context 'when one partition_key is nil or empty string' do
120
+ let(:options) { {
121
+ :counter_name => :test_counter,
122
+ :field_name => :test_field,
123
+ :partition_keys => [:param1, :param2]
124
+ } }
125
+
126
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
127
+ before { 3.times { counter.process(:param1 => 21, :param2 => 22, :param3 => 33) } }
128
+ before { 1.times { counter.process(:param1 => 21, :param2 => nil, :param3 => 33) } }
129
+ before { 1.times { counter.process(:param1 => 21, :param2 => '', :param3 => 33) } }
130
+
131
+ it { expect(redis.keys('*')).to have(3).key }
132
+ it { expect(redis.keys('*').first).to eq 'test_counter:11:22' }
133
+ it { expect(redis.keys('*').second).to eq 'test_counter:21:22' }
134
+ it { expect(redis.keys('*').third).to eq 'test_counter:21:' }
135
+ it { expect(redis.hexists('test_counter:11:22', 'test_field')).to be_true }
136
+ it { expect(redis.hget('test_counter:11:22', 'test_field')).to eq value.to_s }
137
+ it { expect(redis.hexists('test_counter:21:22', 'test_field')).to be_true }
138
+ it { expect(redis.hget('test_counter:21:22', 'test_field')).to eq 3.to_s }
139
+ it { expect(redis.hexists('test_counter:21:', 'test_field')).to be_true }
140
+ it { expect(redis.hget('test_counter:21:', 'test_field')).to eq 2.to_s }
141
+ end
142
+
143
+ context 'when partition_keys consists of mixed types' do
144
+ let(:options) { {
145
+ :counter_name => :test_counter,
146
+ :field_name => :test_field,
147
+ :partition_keys => [:date, proc { |params| params[:param1].odd?.to_s }]
148
+ } }
149
+
150
+ before { 2.times { counter.process(:param1 => 1, :param2 => 2, :date => '2013-04-27') } }
151
+ before { 1.times { counter.process(:param1 => 3, :param2 => 2, :date => '2013-04-27') } }
152
+ before { 4.times { counter.process(:param1 => 2, :param2 => 2, :date => '2013-04-27') } }
153
+ before { 1.times { counter.process(:param1 => 2, :param2 => 2, :date => '2013-04-28') } }
154
+
155
+ it { expect(redis.keys('*')).to have(3).key }
156
+ it { expect(redis.keys('*').first).to eq 'test_counter:2013-04-27:true' }
157
+ it { expect(redis.keys('*').second).to eq 'test_counter:2013-04-27:false' }
158
+ it { expect(redis.keys('*').third).to eq 'test_counter:2013-04-28:false' }
159
+ it { expect(redis.hexists('test_counter:2013-04-27:true', 'test_field')).to be_true }
160
+ it { expect(redis.hget('test_counter:2013-04-27:true', 'test_field')).to eq 3.to_s }
161
+ it { expect(redis.hexists('test_counter:2013-04-27:false', 'test_field')).to be_true }
162
+ it { expect(redis.hget('test_counter:2013-04-27:false', 'test_field')).to eq 4.to_s }
163
+ it { expect(redis.hexists('test_counter:2013-04-27:false', 'test_field')).to be_true }
164
+ it { expect(redis.hget('test_counter:2013-04-28:false', 'test_field')).to eq 1.to_s }
165
+ end
166
+ end
167
+
168
+ context '#partitions' do
169
+ let(:options) { {
170
+ :counter_name => :test_counter,
171
+ :field_name => :test_field,
172
+ :partition_keys => [:param1, :param2, :param3]
173
+ } }
174
+
175
+ let(:partition_1) { {'param1' => '11', :param2 => '22', :param3 => '33'}.with_indifferent_access }
176
+ let(:partition_2) { {'param1' => '21', :param2 => '23', :param3 => '33'}.with_indifferent_access }
177
+ let(:partition_3) { {'param1' => '21', :param2 => '24', :param3 => '33'}.with_indifferent_access }
178
+
179
+ before { counter.process(partition_1) }
180
+ before { counter.process(partition_2) }
181
+ before { counter.process(partition_3) }
182
+
183
+ context 'when no partition params given' do
184
+ it { expect(counter.partitions).to have(3).partition }
185
+ it { expect(counter.partitions.first).to eq partition_1 }
186
+ it { expect(counter.partitions.second).to eq partition_2 }
187
+ it { expect(counter.partitions.third).to eq partition_3 }
188
+ end
189
+
190
+ context 'when one partition param given' do
191
+ it { expect(counter.partitions(:param1 => 11)).to have(1).partition }
192
+ it { expect(counter.partitions(:param1 => 11).first).to eq partition_1 }
193
+ it { expect(counter.partitions(:param1 => 21)).to have(2).partition }
194
+ it { expect(counter.partitions(:param1 => 21).first).to eq partition_2 }
195
+ it { expect(counter.partitions('param1' => 21).second).to eq partition_3 }
196
+ end
197
+
198
+ context 'when two partition params given' do
199
+ it { expect(counter.partitions(:param1 => 21, :param2 => 23)).to have(1).partition }
200
+ it { expect(counter.partitions(:param1 => 21, :param2 => 23).first).to eq partition_2 }
201
+ end
202
+
203
+ context 'when all partition params given' do
204
+ it { expect(counter.partitions(partition_2)).to have(1).partition }
205
+ it { expect(counter.partitions(partition_2).first).to eq partition_2 }
206
+ end
207
+
208
+ context 'when unknown partition params given' do
209
+ it { expect(counter.partitions(:param1 => 55)).to eq [] }
210
+ end
211
+
212
+ context 'when given an incorrect partition' do
213
+ it { expect { counter.partitions(:param2 => 55) }.to raise_error ArgumentError }
214
+ end
215
+ end
216
+
217
+ context '#data' do
218
+ context 'when no group_keys given' do
219
+ let(:options) { {
220
+ :counter_name => :pages_by_day,
221
+ :field_name => :pages,
222
+ :partition_keys => [:param1, :param2]
223
+ } }
224
+
225
+ let(:partitions) { {:param1 => 21} }
226
+
227
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
228
+ before { 3.times { counter.process(:param1 => 21, :param2 => 22, :param3 => 33) } }
229
+ before { 2.times { counter.process(:param1 => 21, :param2 => 23, :param3 => 31) } }
230
+ before { 1.times { counter.process(:param1 => 21, :param2 => nil, :param3 => 31) } }
231
+ before { 4.times { counter.process(:param1 => 21, :param2 => '', :param3 => 31) } }
232
+
233
+ it { expect(counter.data(partitions)).to have(3).row }
234
+ it { expect(counter.data(partitions).first[:value]).to eq 3 }
235
+ it { expect(counter.data(partitions).second[:value]).to eq 2 }
236
+ it { expect(counter.data(partitions).third[:value]).to eq 5 }
237
+ end
238
+
239
+ context 'when group_keys and one group key is nil' do
240
+ let(:options) { {
241
+ :counter_name => :test_counter,
242
+ :field_name => :test_field,
243
+ :partition_keys => [:param1, :param2],
244
+ :group_keys => [:param3, :param4]
245
+ } }
246
+
247
+ let(:partitions) { {:param1 => 21, 'param3' => 33, :param2 => nil} }
248
+
249
+ before { 1.times { counter.process(:param1 => 21, :param2 => nil, :param3 => 31, :param4 => 1) } }
250
+ before { 4.times { counter.process(:param1 => 21, :param2 => '', :param3 => 31, :param4 => nil) } }
251
+ before { 1.times { counter.process(:param1 => 21, :param2 => nil, :param3 => 31, :param4 => '') } }
252
+ before { 3.times { counter.process(:param1 => 21, :param2 => '', :param3 => 31, :param4 => 1) } }
253
+
254
+ it { expect(counter.data(partitions)).to have(2).row }
255
+ it { expect(counter.data(partitions).first[:value]).to eq 4 }
256
+ it { expect(counter.data(partitions).first[:param3]).to eq '31' }
257
+ it { expect(counter.data(partitions).first[:param4]).to eq '1' }
258
+ it { expect(counter.data(partitions).second[:value]).to eq 5 }
259
+ it { expect(counter.data(partitions).second[:param3]).to eq '31' }
260
+ it { expect(counter.data(partitions).second[:param4]).to eq '' }
261
+ end
262
+
263
+ context 'when group_keys given' do
264
+ let(:options) { {
265
+ :counter_name => :test_counter,
266
+ :field_name => :test_field,
267
+ :partition_keys => [:param1, :param2],
268
+ :group_keys => :param3
269
+ } }
270
+
271
+ before { value.times { counter.process(:param1 => 11, :param2 => 22, :param3 => 33) } }
272
+ before { 3.times { counter.process(:param1 => 21, :param2 => 22, :param3 => 33) } }
273
+ before { 2.times { counter.process(:param1 => 21, :param2 => 22, :param3 => 31) } }
274
+ before { 1.times { counter.process(:param1 => 21, :param2 => nil, :param3 => 31) } }
275
+ before { 4.times { counter.process(:param1 => 21, :param2 => '', :param3 => 31) } }
276
+
277
+ context 'when partition as Hash_given' do
278
+ let(:partitions) { {:param1 => 21, 'param3' => 33, :param2 => 22} }
279
+
280
+ it { expect(counter.data(partitions)).to be_a Array }
281
+ it { expect(counter.data(partitions)).to have(2).row }
282
+ it { expect(counter.data(partitions).first).to be_a HashWithIndifferentAccess }
283
+ it { expect(counter.data(partitions).first).to include('value') }
284
+ it { expect(counter.data(partitions).first).to include(:value) }
285
+ it { expect(counter.data(partitions).first).to include(:param3) }
286
+ it { expect(counter.data(partitions).first[:value]).to eq 3 }
287
+ it { expect(counter.data(partitions).first[:param3]).to eq '33' }
288
+ it { expect(counter.data(partitions).second[:value]).to eq 2 }
289
+ it { expect(counter.data(partitions).second[:param3]).to eq '31' }
290
+ end
291
+
292
+ context 'when partition param is empty string' do
293
+ let(:partitions) { {:param1 => 21, 'param3' => 33, :param2 => ''} }
294
+
295
+ it { expect(counter.data(partitions)).to have(1).row }
296
+ it { expect(counter.data(partitions).first[:value]).to eq 5 }
297
+ it { expect(counter.data(partitions).first[:param3]).to eq '31' }
298
+ end
299
+
300
+ context 'when partition param is empty nil' do
301
+ let(:partitions) { {:param1 => 21, 'param3' => 33, :param2 => nil} }
302
+
303
+ it { expect(counter.data(partitions)).to have(1).row }
304
+ it { expect(counter.data(partitions).first[:value]).to eq 5 }
305
+ it { expect(counter.data(partitions).first[:param3]).to eq '31' }
306
+ end
307
+
308
+ context 'when unknown partition_given' do
309
+ let(:partitions) { {:param1 => 22, :param3 => 33, 'param2' => '22'} }
310
+
311
+ it { expect(counter.data(partitions)).to have(0).row }
312
+ end
313
+
314
+ context 'when no data in storage' do
315
+ let(:partitions) { {:param1 => 21, 'param3' => 33, :param2 => 22} }
316
+
317
+ before { redis.flushdb }
318
+
319
+ it { expect(counter.data(partitions)).to be_a Array }
320
+ it { expect(counter.data(partitions)).to be_empty }
321
+ end
322
+
323
+
324
+ context 'when block given' do
325
+ let(:partitions) { {:param1 => 21, :param3 => 33, 'param2' => '22'} }
326
+
327
+ it { expect(counter.data(partitions) {}).to eq 2 }
328
+ it { expect { |b| counter.data(partitions, &b) }.to yield_control.once }
329
+
330
+ it do
331
+ expect { |b| counter.data(partitions, &b) }.to(
332
+ yield_successive_args([
333
+ {'param3'=>'33', 'value'=>3},
334
+ {'param3'=>'31', 'value'=>2}
335
+ ])
336
+ )
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ context 'when check deleting data methods' do
343
+ let(:options) { {
344
+ :counter_name => :test_counter,
345
+ :field_name => :test_field,
346
+ :partition_keys => [:param1, :param2, :param3]
347
+ } }
348
+
349
+ let(:partition_1) { {'param1' => '11', :param2 => '22', :param3 => '33'}.with_indifferent_access }
350
+ let(:partition_2) { {'param1' => '21', :param2 => '23', :param3 => '33'}.with_indifferent_access }
351
+ let(:partition_3) { {'param1' => '21', :param2 => '24', :param3 => '31'}.with_indifferent_access }
352
+
353
+ before { counter.process(partition_1) }
354
+ before { counter.process(partition_2) }
355
+ before { counter.process(partition_3) }
356
+
357
+ context '#delete_all!' do
358
+ before { counter.delete_all! }
359
+
360
+ it { expect(counter.partitions).to eq [] }
361
+ it { expect(counter.data).to eq [] }
362
+ it { expect(redis.keys).to eq [] }
363
+ end
364
+
365
+ context '#delete_partitions!' do
366
+ context 'when no params given' do
367
+ before { counter.delete_partitions! }
368
+
369
+ it { expect(counter.partitions).to eq [] }
370
+ it { expect(counter.data).to eq [] }
371
+ it { expect(redis.keys).to eq [] }
372
+ end
373
+
374
+ context 'when leaf partition given' do
375
+ before { counter.delete_partitions!(partition_2) }
376
+
377
+ it { expect(counter.partitions).to have(2).row }
378
+ it { expect(counter.partitions.first).to eq partition_1 }
379
+ it { expect(counter.partitions.last).to eq partition_3 }
380
+ end
381
+
382
+ context 'when not leaf partition given' do
383
+ before { counter.delete_partitions!(:param1 => 21) }
384
+
385
+ it { expect(counter.partitions).to have(1).row }
386
+ it { expect(counter.partitions.first).to eq partition_1 }
387
+ end
388
+
389
+ context 'when block given' do
390
+ it { expect { |b| counter.delete_partitions!(:param1 => 21, &b) }.to yield_control.once }
391
+ end
392
+
393
+ context 'if you pass a block in which the exception occurred' do
394
+ let(:error_proc) { Proc.new { raise '!' } }
395
+
396
+ before { counter.delete_partitions!(:param1 => 21, &error_proc) rescue nil }
397
+
398
+ it { expect(counter.partitions).to have(3).row }
399
+ it { expect(counter.partitions.first).to eq partition_1 }
400
+ it { expect(counter.partitions.second).to eq partition_2 }
401
+ it { expect(counter.partitions.last).to eq partition_3 }
402
+ end
403
+ end
404
+
405
+ context '#delete_partition_direct!' do
406
+ context 'when leaf partition given' do
407
+ before { counter.delete_partitions!(partition_2) }
408
+
409
+ it { expect(counter.partitions).to have(2).row }
410
+ it { expect(counter.partitions.first).to eq partition_1 }
411
+ it { expect(counter.partitions.last).to eq partition_3 }
412
+ end
413
+
414
+ context 'when not leaf partition given' do
415
+ it { expect { counter.delete_partition_direct!({}, :param1 => 21) }.to raise_error KeyError }
416
+ end
417
+ end
418
+ end
419
+
420
+ context 'when check delimiters' do
421
+ let(:options) { {
422
+ :counter_name => :test_counter,
423
+ :group_keys => [:param3],
424
+ :partition_keys => [:param1, :param2],
425
+ :key_delimiter => '&',
426
+ :value_delimiter => '|'
427
+ } }
428
+
429
+ let(:partition_1) { {:param1 => '11', :param2 => '22:35', :param3 => '11:64'} }
430
+ let(:partition_2) { {:param1 => '11', :param2 => '23:26', :param3 => '11:36'} }
431
+ let(:partition_3) { {:param1 => '11', :param2 => '24:26', :param3 => '21:54'} }
432
+
433
+ before { counter.process(partition_1) }
434
+ before { 2.times { counter.process(partition_2) } }
435
+ before { 3.times { counter.process(partition_3) } }
436
+
437
+ it { expect(counter.partitions.first[:param2]).to eq '22:35' }
438
+ it { expect(counter.partitions.second[:param2]).to eq '23:26' }
439
+ it { expect(counter.partitions.third[:param2]).to eq '24:26' }
440
+
441
+ it { expect(counter.data.first[:param3]).to eq '11:64' }
442
+ it { expect(counter.data.first[:value]).to eq 1 }
443
+ it { expect(counter.data.second[:param3]).to eq '11:36' }
444
+ it { expect(counter.data.second[:value]).to eq 2 }
445
+ it { expect(counter.data.third[:param3]).to eq '21:54' }
446
+ it { expect(counter.data.third[:value]).to eq 3 }
447
+ end
448
+
449
+ context 'when check custom increment' do
450
+ let(:options) { {
451
+ :counter_name => :test_counter,
452
+ :field_name => :test_field
453
+ } }
454
+
455
+ before { value.times { counter.process(:value => 0.2) } }
456
+
457
+ it { expect(redis.keys('*')).to have(1).key }
458
+ it { expect(redis.keys('*').first).to eq 'test_counter' }
459
+ it { expect(redis.hexists('test_counter', 'test_field')).to be_true }
460
+ it { expect(redis.hget('test_counter', 'test_field').to_f).to be_within(0.001).of value*0.2.to_f }
461
+ end
462
+ end