redis-bitops 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.
@@ -0,0 +1,16 @@
1
+ $: << "../../lib"
2
+
3
+ require 'redis/bitops'
4
+
5
+ require_relative './support/bitmap_examples'
6
+
7
+ require 'pry'
8
+ require 'awesome_print'
9
+
10
+ RSpec.configure do |config|
11
+ config.after(:each, redis_cleanup: true) do
12
+ key_prefix = example.metadata[:redis_key_prefix] or raise "Specify the key prefix using RSpec metadata (e.g. redis_key_prefix: 'rsb:')."
13
+ keys = redis.keys("#{key_prefix}*")
14
+ redis.del(*keys) unless keys.empty?
15
+ end
16
+ end
@@ -0,0 +1,313 @@
1
+ shared_examples_for "a bitmap" do |creation_method|
2
+ let(:redis) { Redis.new }
3
+ let(:a) { redis.send(creation_method, "rsb:a") }
4
+ let(:b) { redis.send(creation_method, "rsb:b") }
5
+ let(:c) { redis.send(creation_method, "rsb:c") }
6
+ let(:result) { redis.send(creation_method, "rsb:output") }
7
+
8
+ describe "#[]" do
9
+ it "sets individual bits" do
10
+ b[0] = true
11
+ b[99] = true
12
+ b[0].should be_true
13
+ b[99].should be_true
14
+ end
15
+
16
+ it "resizes the bitmap as necessary" do
17
+ expect { b[10_000_000] = 1 }.to_not raise_error
18
+ b[10_000_000].should be_true
19
+ end
20
+
21
+ it "doesn't zeroes unset bits" do
22
+ max_bit_pos = 5_000
23
+ approx_percent_bits_set = 0.2
24
+
25
+ # TODO: There are definitely faster ways to generate the data.
26
+ Inf = 1.0/0.0 unless Kernel.const_defined? "Inf"
27
+ set = Set.new((0..Inf).lazy.map {|i| (rand * max_bit_pos).to_i}.take(approx_percent_bits_set * max_bit_pos))
28
+
29
+ set.each do |pos|
30
+ b[pos] = true
31
+ end
32
+
33
+ set.each do |pos|
34
+ b[pos].should be_true
35
+ end
36
+
37
+ (0..Inf).lazy.take(max_bit_pos).each do |pos|
38
+ b[pos].should eq set.include?(pos)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#bitcount" do
44
+ it "returns the number of set bits" do
45
+ b.bitcount.should == 0
46
+ b[1] = true
47
+ b[2] = true
48
+ b[100] = true
49
+ b.bitcount.should == 3
50
+ end
51
+ end
52
+
53
+ describe "#operator &" do
54
+ it "returns a result query that can be materialized" do
55
+ a[0] = true
56
+ a[1] = true
57
+ a[2] = true
58
+ a[100] = true
59
+ a[110] = true
60
+
61
+ b[0] = true
62
+ b[100] = true
63
+
64
+ q = a & b
65
+
66
+ result << q
67
+ result.bitcount.should == 2
68
+ end
69
+
70
+ it "allows operator nesting" do
71
+ a[0] = true
72
+ a[1] = true
73
+ a[2] = true
74
+ a[100] = true
75
+ a[110] = true
76
+
77
+ b[0] = true
78
+ b[100] = true
79
+
80
+ c[0] = true
81
+
82
+ q = a & b & (c & a)
83
+
84
+ result << q
85
+ result.bitcount.should == 1
86
+ end
87
+ end
88
+
89
+ describe "#operator |" do
90
+ it "returns a result query that can be materialized" do
91
+ a[0] = true
92
+ a[1] = true
93
+ a[2] = true
94
+ a[100] = true
95
+
96
+ b[0] = true
97
+ b[100] = true
98
+ b[110] = true
99
+
100
+ q = a | b
101
+
102
+ result << q
103
+ result.bitcount.should == 5
104
+ end
105
+
106
+ it "allows operator nesting" do
107
+ a[0] = true
108
+ a[1] = true
109
+ a[2] = true
110
+ a[100] = true
111
+ a[110] = true
112
+
113
+ b[0] = true
114
+ b[100] = true
115
+
116
+ c[0] = true
117
+
118
+ q = a | b | (c | a)
119
+
120
+ result << q
121
+ result.bitcount.should == 5
122
+ end
123
+ end
124
+
125
+ describe "#operator ~" do
126
+ it "returns a result query that can be materialized" do
127
+ a[0] = true
128
+ a[1] = true
129
+ a[2] = true
130
+ a[100] = true
131
+
132
+ q = ~a
133
+
134
+ result << q
135
+ result[0].should be_false
136
+ result[1].should be_false
137
+ result[2].should be_false
138
+ result[3].should be_true
139
+ result[99].should be_true
140
+ result[100].should be_false
141
+ end
142
+
143
+ it "allows operator nesting" do
144
+ a[0] = true
145
+ a[1] = true
146
+ a[2] = true
147
+ a[100] = true
148
+ a[110] = true
149
+
150
+ b[0] = true
151
+ b[100] = true
152
+
153
+ c[0] = true
154
+
155
+ q = ~a | b & c
156
+
157
+ result << q
158
+ result[0].should be_true
159
+ result[1].should be_false
160
+ result[2].should be_false
161
+ result[3].should be_true
162
+ result[99].should be_true
163
+ result[100].should be_false
164
+ result[109].should be_true
165
+ result[110].should be_false
166
+ result[111].should be_true
167
+ end
168
+
169
+ # Commented out because this is how redis BITOP NOT works:
170
+ # it pads the results to the full byte thus messing up with
171
+ # the operation.
172
+
173
+ # it "returns result with the correct bitcount" do
174
+ # pending
175
+ # a[0] = true
176
+ # a[1] = true
177
+ # a[2] = true
178
+ # a[100] = true
179
+ #
180
+ # q = ~a
181
+ #
182
+ # result << q
183
+ # result.bitcount.should == 96
184
+ # end
185
+ end
186
+
187
+ describe "#operator ^" do
188
+ it "returns a result query that can be materialized" do
189
+ a[0] = true
190
+ a[1] = true
191
+ a[2] = true
192
+ a[100] = true
193
+
194
+ b[0] = true
195
+ b[100] = true
196
+ b[110] = true
197
+
198
+ q = a ^ b
199
+
200
+ result << q
201
+ result.bitcount.should == 3
202
+ end
203
+
204
+ it "allows operator nesting" do
205
+ a[0] = true
206
+ a[1] = true
207
+ a[2] = true
208
+ a[100] = true
209
+ a[110] = true
210
+
211
+ b[0] = true
212
+ b[100] = true
213
+
214
+ c[0] = true
215
+
216
+ q = a ^ (b ^ c)
217
+
218
+ result << q
219
+ result.bitcount.should == 4
220
+ end
221
+ end
222
+
223
+ describe "#delete!" do
224
+ it "removes all bitmap's keys" do
225
+ a[0] = true
226
+ a[10_000] = true
227
+ a.delete!
228
+ redis.keys("rsb:*").should be_empty
229
+ end
230
+
231
+ it "effectively sets all bitmap's keys to zero" do
232
+ a[0] = true
233
+ a[10_000] = true
234
+ a.delete!
235
+ a[0].should be_false
236
+ a[10_000].should be_false
237
+ b[0] = true
238
+ result << (a & b)
239
+ result.bitcount.should == 0
240
+ end
241
+ end
242
+
243
+ describe "#<<" do
244
+ before do
245
+ a[0] = true
246
+ a[1] = true
247
+ a[2] = true
248
+ a[3] = true
249
+
250
+ b[0] = true
251
+ b[1] = true
252
+ b[2] = true
253
+
254
+ c[0] = true
255
+ c[1] = true
256
+ end
257
+
258
+ after do
259
+ @temp.delete! if @temp
260
+ end
261
+
262
+ it "materializes an arbitrarily-complicated expression" do
263
+ result << (a & (a & b) | c & b & a)
264
+ result.bitcount.should == 3
265
+ end
266
+
267
+ it "is lazy-invoked when expression is evaluated" do
268
+ result = (a & (a & b) | c & b & a)
269
+ result.should be_a Redis::Bitops::Queries::BinaryOperator
270
+ @temp = result
271
+ result.bitcount.should == 3
272
+ end
273
+
274
+ it "takes into account modifications made to the result" do
275
+ output = (a & (a & b) | c & b & a)
276
+ output[100] = true
277
+ @temp = output
278
+ result << output
279
+ result.bitcount.should == 4
280
+ end
281
+ end
282
+
283
+ describe "#copy_to" do
284
+ it "overrides the target bitmap" do
285
+ # Fix expression with bits set using [] after evaluation doesn't materialize the newly set bits.
286
+ result[1000] = true
287
+ a[0] = true
288
+ a[1] = true
289
+ a.copy_to(result)
290
+ result.bitcount.should == a.bitcount
291
+ result[1000].should be_false
292
+ end
293
+ end
294
+ end
295
+
296
+ shared_examples_for "a bitmap factory method" do |creation_method, bitmap_class|
297
+ let(:redis) { Redis.new }
298
+
299
+ after do
300
+ @bitmap.delete! if @bitmap
301
+ end
302
+
303
+ it "creates a new bitmap" do
304
+ @bitmap = redis.send(creation_method, "rsb:xxx")
305
+ @bitmap.should be_a bitmap_class
306
+ end
307
+
308
+ it "doesn't add keys until the bitmap is modified" do
309
+ @bitmap = redis.send(creation_method, "rsb:xxx")
310
+ expect { @bitmap }.to_not change { redis.keys.size }
311
+ expect { @bitmap[1] = true }.to change { redis.keys.size }
312
+ end
313
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-bitops
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Martin Bilski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hiredis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Optimized operations on Redis bitmaps using built-in Ruby operators.
126
+ Supports sparse bitmaps to preserve storage.
127
+ email: gyamtso@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - lib/redis/bitops.rb
135
+ - lib/redis/bitops/bitmap.rb
136
+ - lib/redis/bitops/configuration.rb
137
+ - lib/redis/bitops/queries/binary_operator.rb
138
+ - lib/redis/bitops/queries/lazy_evaluation.rb
139
+ - lib/redis/bitops/queries/materialization_helpers.rb
140
+ - lib/redis/bitops/queries/tree_building_helpers.rb
141
+ - lib/redis/bitops/queries/unary_operator.rb
142
+ - lib/redis/bitops/sparse_bitmap.rb
143
+ - spec/redis/bitops/bitmap_spec.rb
144
+ - spec/redis/bitops/queries/binary_operator_spec.rb
145
+ - spec/redis/bitops/queries/unary_operator_spec.rb
146
+ - spec/redis/bitops/sparse_bitmap_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/support/bitmap_examples.rb
149
+ homepage: http://github.com/bilus/redis-bitops
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.2.2
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Bitmap and sparse bitmap operations for Redis.
173
+ test_files: []