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,162 @@
1
+ # coding: utf-8
2
+ shared_examples_for 'unique_values_lists/expirable' do
3
+ let(:redis) { MockRedis.new }
4
+ let(:counter) { described_class.new(redis, options) }
5
+
6
+ after do
7
+ Timecop.return
8
+ end
9
+
10
+ context 'when auto expire enabled' do
11
+ context 'when expire given in counter options' do
12
+ let(:options) { {
13
+ :counter_name => :test_counter,
14
+ :value_keys => [:value],
15
+ :partition_keys => [:part],
16
+ :expire => 10.seconds
17
+ } }
18
+
19
+ before do
20
+ counter.add(:value => 1, :part => :part1)
21
+ counter.add(:value => 2, :part => :part2)
22
+ counter.add(:value => 3, :part => :part1, :expire => :never)
23
+ counter.add(:value => 4, :part => :part1, :expire => 20.seconds)
24
+ end
25
+
26
+ context 'before time has expired' do
27
+ before { Timecop.freeze(5.seconds.since) }
28
+
29
+ it { expect(counter.has_value?(:value => 1)).to be_true }
30
+ it { expect(counter.has_value?(:value => 2)).to be_true }
31
+ it { expect(counter.has_value?(:value => 3)).to be_true }
32
+ it { expect(counter.has_value?(:value => 4)).to be_true }
33
+
34
+ it { expect(counter.partitions).to include('part' => 'part1') }
35
+ it { expect(counter.partitions).to include('part' => 'part2') }
36
+
37
+ it { expect(counter.data).to include('value' => '1') }
38
+ it { expect(counter.data).to include('value' => '2') }
39
+ it { expect(counter.data).to include('value' => '3') }
40
+ it { expect(counter.data).to include('value' => '4') }
41
+ end
42
+
43
+ context 'after time has expired' do
44
+ before { Timecop.freeze(10.seconds.since) }
45
+
46
+ it { expect(counter.has_value?(:value => 1)).to be_false }
47
+ it { expect(counter.has_value?(:value => 2)).to be_false }
48
+ it { expect(counter.has_value?(:value => 3)).to be_true }
49
+
50
+ it { expect(counter.partitions).to include('part' => 'part1') }
51
+ it { expect(counter.partitions).to_not include('part' => 'part2') }
52
+
53
+ it { expect(counter.data).to_not include('value' => '1') }
54
+ it { expect(counter.data).to_not include('value' => '2') }
55
+ it { expect(counter.data).to include('value' => '3') }
56
+ it { expect(counter.data).to include('value' => '4') }
57
+ end
58
+ end
59
+
60
+ context 'when expire not given in counter options' do
61
+ let(:options) { {
62
+ :counter_name => :test_counter,
63
+ :value_keys => [:value],
64
+ :partition_keys => [:part]
65
+ } }
66
+
67
+ before do
68
+ counter.add(:value => 1, :part => :part1, :expire => 10.seconds)
69
+ counter.add(:value => 2, :part => :part2, :expire => 10.seconds)
70
+ counter.add(:value => 3, :part => :part1)
71
+ counter.add(:value => 4, :part => :part1, :expire => 20.seconds)
72
+ end
73
+
74
+ context 'before time has expired' do
75
+ before { Timecop.freeze(5.seconds.since) }
76
+
77
+ it { expect(counter.has_value?(:value => 1)).to be_true }
78
+ it { expect(counter.has_value?(:value => 2)).to be_true }
79
+ it { expect(counter.has_value?(:value => 3)).to be_true }
80
+ it { expect(counter.has_value?(:value => 4)).to be_true }
81
+
82
+ it { expect(counter.partitions).to include('part' => 'part1') }
83
+ it { expect(counter.partitions).to include('part' => 'part2') }
84
+
85
+ it { expect(counter.data).to include('value' => '1') }
86
+ it { expect(counter.data).to include('value' => '2') }
87
+ it { expect(counter.data).to include('value' => '3') }
88
+ it { expect(counter.data).to include('value' => '4') }
89
+ end
90
+
91
+ context 'after time has expired' do
92
+ before { Timecop.freeze(10.seconds.since) }
93
+
94
+ it { expect(counter.has_value?(:value => 1)).to be_false }
95
+ it { expect(counter.has_value?(:value => 2)).to be_false }
96
+ it { expect(counter.has_value?(:value => 3)).to be_true }
97
+ it { expect(counter.has_value?(:value => 4)).to be_true }
98
+
99
+ it { expect(counter.partitions).to include('part' => 'part1') }
100
+ it { expect(counter.partitions).to_not include('part' => 'part2') }
101
+
102
+ it { expect(counter.data).to_not include('value' => '1') }
103
+ it { expect(counter.data).to_not include('value' => '2') }
104
+ it { expect(counter.data).to include('value' => '3') }
105
+ it { expect(counter.data).to include('value' => '4') }
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'when auto expire disabled' do
111
+ context 'when expire given in counter options' do
112
+ let(:options) { {
113
+ :counter_name => :test_counter,
114
+ :value_keys => [:value],
115
+ :partition_keys => [:part],
116
+ :expire => 10.seconds,
117
+ :clean_expired => false
118
+ } }
119
+
120
+ before do
121
+ counter.add(:value => 1, :part => :part1)
122
+ counter.add(:value => 2, :part => :part2)
123
+ counter.add(:value => 3, :part => :part1, :expire => :never)
124
+ counter.add(:value => 4, :part => :part1, :expire => 20.seconds)
125
+
126
+ Timecop.freeze(10.seconds.since)
127
+ end
128
+
129
+ context 'after time has expired' do
130
+ it { expect(counter.has_value?(:value => 1)).to be_true }
131
+ it { expect(counter.has_value?(:value => 2)).to be_true }
132
+ it { expect(counter.has_value?(:value => 3)).to be_true }
133
+ it { expect(counter.has_value?(:value => 4)).to be_true }
134
+
135
+ it { expect(counter.partitions).to include('part' => 'part1') }
136
+ it { expect(counter.partitions).to include('part' => 'part2') }
137
+
138
+ it { expect(counter.data).to include('value' => '1') }
139
+ it { expect(counter.data).to include('value' => '2') }
140
+ it { expect(counter.data).to include('value' => '3') }
141
+ it { expect(counter.data).to include('value' => '4') }
142
+ end
143
+
144
+ context 'when clean_expired call' do
145
+ before { counter.clean_expired }
146
+
147
+ it { expect(counter.has_value?(:value => 1)).to be_false }
148
+ it { expect(counter.has_value?(:value => 2)).to be_false }
149
+ it { expect(counter.has_value?(:value => 3)).to be_true }
150
+ it { expect(counter.has_value?(:value => 4)).to be_true }
151
+
152
+ it { expect(counter.partitions).to include('part' => 'part1') }
153
+ it { expect(counter.partitions).to_not include('part' => 'part2') }
154
+
155
+ it { expect(counter.data).to_not include('value' => '1') }
156
+ it { expect(counter.data).to_not include('value' => '2') }
157
+ it { expect(counter.data).to include('value' => '3') }
158
+ it { expect(counter.data).to include('value' => '4') }
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,119 @@
1
+ # coding: utf-8
2
+ shared_examples_for 'unique_values_lists/set' do
3
+ let(:redis) { MockRedis.new }
4
+ let(:values) { rand(10) + 1 }
5
+
6
+ let(:counter) { described_class.new(redis, options) }
7
+
8
+ context '#add' do
9
+ context 'when cluster and partition keys given' do
10
+ let(:options) { {
11
+ :counter_name => :test_counter,
12
+ :value_keys => [:param0, :param1],
13
+ :cluster_keys => [:param2],
14
+ :partition_keys => [:param3, :param4]
15
+ } }
16
+
17
+ before do
18
+ values.times { counter.add(:param0 => 1, :param1 => 2, :param2 => :cluster1, :param3 => :part1, :param4 => :part2) }
19
+ values.times { counter.add(:param0 => 2, :param1 => 1, :param2 => :cluster1, :param3 => :part1, :param4 => :part2) }
20
+ values.times { counter.add(:param0 => 3, :param1 => 2, :param2 => :cluster1, :param3 => :part2, :param4 => :part2) }
21
+ values.times { counter.add(:param0 => 4, :param1 => 5, :param2 => :cluster2, :param3 => :part1, :param4 => :part2) }
22
+ end
23
+
24
+ it { expect(redis.keys('*')).to have(5).key }
25
+
26
+ it { expect(redis.exists("test_counter:cluster1:part1:part2")).to be_true }
27
+ it { expect(redis.exists("test_counter:cluster1:part2:part2")).to be_true }
28
+ it { expect(redis.exists("test_counter:cluster2:part1:part2")).to be_true }
29
+
30
+ it { expect(redis.smembers("test_counter:cluster1:part1:part2")).to have(2).keys }
31
+ it { expect(redis.smembers("test_counter:cluster1:part2:part2")).to have(1).keys }
32
+ it { expect(redis.smembers("test_counter:cluster2:part1:part2")).to have(1).keys }
33
+
34
+ it { expect(redis.smembers("test_counter:cluster1:part1:part2")).to include '1:2' }
35
+ it { expect(redis.smembers("test_counter:cluster1:part1:part2")).to include '2:1' }
36
+ it { expect(redis.smembers("test_counter:cluster1:part2:part2")).to include '3:2' }
37
+ it { expect(redis.smembers("test_counter:cluster2:part1:part2")).to include '4:5' }
38
+ end
39
+
40
+ context 'when cluster and partition keys no given' do
41
+ let(:options) { {
42
+ :counter_name => :test_counter,
43
+ :value_keys => [:param0, :param1]
44
+ } }
45
+
46
+ before do
47
+ values.times { counter.add(:param0 => 1, :param1 => 2) }
48
+ values.times { counter.add(:param0 => 1, :param1 => 2) }
49
+ values.times { counter.add(:param0 => 2, :param1 => 1) }
50
+ values.times { counter.add(:param0 => 3, :param1 => 2) }
51
+ end
52
+
53
+ it { expect(redis.keys('*')).to have(1).key }
54
+
55
+ it { expect(redis.exists("test_counter")).to be_true }
56
+ it { expect(redis.smembers("test_counter")).to have(3).keys }
57
+
58
+ it { expect(redis.smembers("test_counter")).to include '1:2' }
59
+ it { expect(redis.smembers("test_counter")).to include '2:1' }
60
+ it { expect(redis.smembers("test_counter")).to include '3:2' }
61
+ end
62
+
63
+ context 'when no cluster keys given, but partition keys given' do
64
+ let(:options) { {
65
+ :counter_name => :test_counter,
66
+ :value_keys => [:param0, :param1],
67
+ :partition_keys => [:param3, :param4]
68
+ } }
69
+
70
+ before do
71
+ values.times { counter.add(:param0 => 1, :param1 => 2, :param3 => :part1, :param4 => :part2) }
72
+ values.times { counter.add(:param0 => 2, :param1 => 1, :param3 => :part1, :param4 => :part2) }
73
+ values.times { counter.add(:param0 => 3, :param1 => 2, :param3 => :part2, :param4 => :part2) }
74
+ values.times { counter.add(:param0 => 4, :param1 => 5, :param3 => :part1, :param4 => :part2) }
75
+ end
76
+
77
+ it { expect(redis.keys('*')).to have(3).key }
78
+
79
+ it { expect(redis.exists("test_counter:part1:part2")).to be_true }
80
+ it { expect(redis.exists("test_counter:part2:part2")).to be_true }
81
+
82
+ it { expect(redis.smembers("test_counter:part1:part2")).to have(3).keys }
83
+ it { expect(redis.smembers("test_counter:part2:part2")).to have(1).keys }
84
+
85
+ it { expect(redis.smembers("test_counter:part1:part2")).to include '1:2' }
86
+ it { expect(redis.smembers("test_counter:part1:part2")).to include '2:1' }
87
+ it { expect(redis.smembers("test_counter:part2:part2")).to include '3:2' }
88
+ it { expect(redis.smembers("test_counter:part1:part2")).to include '4:5' }
89
+ end
90
+
91
+ context 'when cluster keys given, but partition keys not given' do
92
+ let(:options) { {
93
+ :counter_name => :test_counter,
94
+ :value_keys => [:param0, :param1],
95
+ :cluster_keys => [:param2]
96
+ } }
97
+
98
+ before do
99
+ values.times { counter.add(:param0 => 1, :param1 => 2, :param2 => :cluster1) }
100
+ values.times { counter.add(:param0 => 2, :param1 => 1, :param2 => :cluster1) }
101
+ values.times { counter.add(:param0 => 3, :param1 => 2, :param2 => :cluster1) }
102
+ values.times { counter.add(:param0 => 4, :param1 => 5, :param2 => :cluster2) }
103
+ end
104
+
105
+ it { expect(redis.keys('*')).to have(2).key }
106
+
107
+ it { expect(redis.exists("test_counter:cluster1")).to be_true }
108
+ it { expect(redis.exists("test_counter:cluster2")).to be_true }
109
+
110
+ it { expect(redis.smembers("test_counter:cluster1")).to have(3).keys }
111
+ it { expect(redis.smembers("test_counter:cluster2")).to have(1).keys }
112
+
113
+ it { expect(redis.smembers("test_counter:cluster1")).to include '1:2' }
114
+ it { expect(redis.smembers("test_counter:cluster1")).to include '2:1' }
115
+ it { expect(redis.smembers("test_counter:cluster1")).to include '3:2' }
116
+ it { expect(redis.smembers("test_counter:cluster2")).to include '4:5' }
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ desc 'Audit current gemset'
4
+ task :audit do
5
+ spawn 'bundle exec bundle-audit'
6
+ end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+
3
+ require 'cane/rake_task'
4
+
5
+ desc 'Run cane to check quality metrics'
6
+ Cane::RakeTask.new(:quality) do |cane|
7
+ cane.abc_max = 15
8
+ cane.abc_glob = cane.style_glob = cane.doc_glob = '*/{lib,bin}/**/*.rb'
9
+ cane.style_measure = 120
10
+ cane.parallel = false
11
+ cane.no_doc = true
12
+ end
@@ -0,0 +1,7 @@
1
+ desc 'Generate CHANGELOG.md'
2
+ task :changelog do
3
+ require 'apress/changelogger'
4
+ Apress::ChangeLogger.new.log_changes
5
+ spawn 'git add CHANGELOG.md'
6
+ spawn 'git commit -m "Update CHANGELOG.md"'
7
+ end
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+
3
+ MINIMUM_COVERAGE = 85
4
+
5
+ desc "Check if test coverage is equal or greater than %.2f%%" % MINIMUM_COVERAGE
6
+ task :coverage => :spec do
7
+ require 'simplecov'
8
+ require 'simplecov/exit_codes'
9
+
10
+ covered_percent = SimpleCov.result.covered_percent.round(2)
11
+ if covered_percent < MINIMUM_COVERAGE
12
+ $stderr.puts "Coverage (%.2f%%) is below the expected minimum coverage (%.2f%%)." % \
13
+ [covered_percent, MINIMUM_COVERAGE]
14
+
15
+ exit(SimpleCov::ExitCodes::MINIMUM_COVERAGE)
16
+ end
17
+ end
18
+
19
+ task :clean do
20
+ FileUtils.rm_rf 'coverage'
21
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ # Helpers for root Rakefile
3
+
4
+ require 'pty'
5
+
6
+ ROOT = File.expand_path(File.join('..', '..'), __FILE__)
7
+
8
+ # run +cmd+ in subprocess, redirect its stdout to parent's stdout
9
+ def spawn(cmd)
10
+ puts ">> #{cmd}"
11
+
12
+ cmd += ' 2>&1'
13
+ PTY.spawn cmd do |r, w, pid|
14
+ begin
15
+ r.sync
16
+ r.each_char { |chr| STDOUT.write(chr) }
17
+ rescue Errno::EIO => e
18
+ # simply ignoring this
19
+ ensure
20
+ ::Process.wait pid
21
+ end
22
+ end
23
+ abort "#{cmd} failed" unless $? && $?.exitstatus == 0
24
+ end
metadata ADDED
@@ -0,0 +1,242 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis_counters
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Artem Napolskih
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ prerelease: false
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - '>='
23
+ - !ruby/object:Gem::Version
24
+ version: '3.0'
25
+ type: :runtime
26
+ name: activesupport
27
+ - !ruby/object:Gem::Dependency
28
+ prerelease: false
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ name: bundler
41
+ - !ruby/object:Gem::Dependency
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ type: :development
54
+ name: rake
55
+ - !ruby/object:Gem::Dependency
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.14.0
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 2.14.0
67
+ type: :development
68
+ name: rspec
69
+ - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :development
82
+ name: mock_redis
83
+ - !ruby/object:Gem::Dependency
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :development
96
+ name: timecop
97
+ - !ruby/object:Gem::Dependency
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.4.1
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: 0.4.1
109
+ type: :development
110
+ name: codeclimate-test-reporter
111
+ - !ruby/object:Gem::Dependency
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ type: :development
124
+ name: simplecov
125
+ - !ruby/object:Gem::Dependency
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: 2.6.0
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: 2.6.0
137
+ type: :development
138
+ name: cane
139
+ - !ruby/object:Gem::Dependency
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ type: :development
152
+ name: bundler-audit
153
+ - !ruby/object:Gem::Dependency
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ type: :development
166
+ name: apress-changelogger
167
+ description:
168
+ email:
169
+ - napolskih@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - .gitignore
175
+ - CHANGELOG.md
176
+ - Gemfile
177
+ - Makefile
178
+ - README.md
179
+ - Rakefile
180
+ - lib/redis_counters.rb
181
+ - lib/redis_counters/base_counter.rb
182
+ - lib/redis_counters/bucket.rb
183
+ - lib/redis_counters/cluster.rb
184
+ - lib/redis_counters/clusterize_and_partitionize.rb
185
+ - lib/redis_counters/hash_counter.rb
186
+ - lib/redis_counters/partition.rb
187
+ - lib/redis_counters/unique_hash_counter.rb
188
+ - lib/redis_counters/unique_values_lists/base.rb
189
+ - lib/redis_counters/unique_values_lists/blocking.rb
190
+ - lib/redis_counters/unique_values_lists/expirable.rb
191
+ - lib/redis_counters/unique_values_lists/non_blocking.rb
192
+ - lib/redis_counters/version.rb
193
+ - redis_counters.gemspec
194
+ - spec/redis_counters/base_spec.rb
195
+ - spec/redis_counters/hash_counter_spec.rb
196
+ - spec/redis_counters/unique_hash_counter_spec.rb
197
+ - spec/redis_counters/unique_values_lists/blocking_spec.rb
198
+ - spec/redis_counters/unique_values_lists/expirable_spec.rb
199
+ - spec/redis_counters/unique_values_lists/non_blicking_spec.rb
200
+ - spec/spec_helper.rb
201
+ - spec/support/unique_values_lists/common.rb
202
+ - spec/support/unique_values_lists/expirable.rb
203
+ - spec/support/unique_values_lists/set.rb
204
+ - tasks/audit.rake
205
+ - tasks/cane.rake
206
+ - tasks/changelog.rake
207
+ - tasks/coverage.rake
208
+ - tasks/support.rb
209
+ homepage: https://github.com/abak-press/redis_counters
210
+ licenses: []
211
+ metadata: {}
212
+ post_install_message:
213
+ rdoc_options: []
214
+ require_paths:
215
+ - lib
216
+ required_ruby_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - '>='
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ required_rubygems_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - '>='
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ requirements: []
227
+ rubyforge_project:
228
+ rubygems_version: 2.4.2
229
+ signing_key:
230
+ specification_version: 4
231
+ summary: Redis Counters
232
+ test_files:
233
+ - spec/redis_counters/base_spec.rb
234
+ - spec/redis_counters/hash_counter_spec.rb
235
+ - spec/redis_counters/unique_hash_counter_spec.rb
236
+ - spec/redis_counters/unique_values_lists/blocking_spec.rb
237
+ - spec/redis_counters/unique_values_lists/expirable_spec.rb
238
+ - spec/redis_counters/unique_values_lists/non_blicking_spec.rb
239
+ - spec/spec_helper.rb
240
+ - spec/support/unique_values_lists/common.rb
241
+ - spec/support/unique_values_lists/expirable.rb
242
+ - spec/support/unique_values_lists/set.rb