rediska 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b301b8b105df4ba9c093329e391077669b638b2
4
- data.tar.gz: 431df3d4c271b26c2948caaf935d899995b1d08b
3
+ metadata.gz: ff4d9d58f4f006fea6fbfc2f1eac33122ef9aa12
4
+ data.tar.gz: 8bfbf422868e1e4bb2d9bf94d6418149ce8db266
5
5
  SHA512:
6
- metadata.gz: 6a59ee0d6021e31f320878d096bec22a9e1684ada257d0684f6526b8ebb29fb29e9370819afbcbb3eab41c2b3c2642db0709d83158a6a157cf98fb7374d5a127
7
- data.tar.gz: 00eb3278e5015604341c4f69836db7524ab8e5d1b509ee4c99bda1e070999a01469da528b8f62e5b30e9a126430edd97f48eca5bcdfcc97115dbdb2b60503774
6
+ metadata.gz: a343ab5b453ace36f23d5fc451b3a99ac49db6a287c2c6cd1a8e2b82bf1cba937a19a07e0995ddfd65876da96ffdd9076524b3eb8f7e78c82e2d0cb0de31c937
7
+ data.tar.gz: 3e7ff21587df7a01876ec277e17c90e75cfd0723e13a154e1108e7359ed98e54a18792345a4c314e566913d450b85e520eed6e035e84add2b6dd15546e9985e9
data/README.md CHANGED
@@ -73,6 +73,9 @@ end
73
73
  * BITCOUNT
74
74
  * BGSAVE
75
75
  * BGREWRITEAOF
76
+ * BLPOP
77
+ * BRPOP
78
+ * BRPOPLPUSH
76
79
  * DBSIZE
77
80
  * DECR
78
81
  * DECRBY
@@ -0,0 +1,56 @@
1
+ module Rediska
2
+ module Bitop
3
+ BIT_OPERATORS = {
4
+ 'or' => :|,
5
+ 'and' => :&,
6
+ 'xor' => :'^',
7
+ 'not' => :~,
8
+ }
9
+
10
+ def bitop(operation, destkey, *keys)
11
+ if result = apply(operator(operation), keys)
12
+ set(destkey, result)
13
+ result.length
14
+ else
15
+ 0
16
+ end
17
+ rescue ArgumentError => _
18
+ raise_argument_error('bitop')
19
+ end
20
+
21
+ private
22
+
23
+ def operator(operation)
24
+ BIT_OPERATORS[operation.to_s.downcase]
25
+ end
26
+
27
+ def apply(operator, keys)
28
+ case operator
29
+ when :~
30
+ raise ArgumentError if keys.count != 1
31
+ bitwise_not(keys.first)
32
+ when :&, :|, :'^'
33
+ raise ArgumentError if keys.empty?
34
+ bitwise_operation(operator, keys)
35
+ else
36
+ raise ArgumentError
37
+ end
38
+ end
39
+
40
+ def bitwise_not(key)
41
+ if value = get(keys.first)
42
+ value.bytes.map { |byte| ~ byte }.pack('c*')
43
+ end
44
+ end
45
+
46
+ def bitwise_operation(operation, keys)
47
+ apply_onto, *values = keys.map { |key| get(key) }.reject(&:nil?)
48
+ values.reduce(apply_onto) do |memo, value|
49
+ shorter, longer = [memo, value].sort_by(&:length).map(&:bytes).map(&:to_a)
50
+ longer.each_with_index.map do |byte, index|
51
+ byte.send(operation, shorter[index] || 0)
52
+ end.pack('c*')
53
+ end
54
+ end
55
+ end
56
+ end
@@ -4,6 +4,7 @@ require 'rediska/sort_method'
4
4
  require 'rediska/sorted_set_argument_handler'
5
5
  require 'rediska/sorted_set_store'
6
6
  require 'rediska/zset'
7
+ require 'rediska/bitop'
7
8
  require 'rediska/driver'
8
9
  require 'rediska/command_executor'
9
10
  require 'rediska/transaction_commands'
@@ -12,6 +13,7 @@ module Rediska
12
13
  class Connection
13
14
  include Driver
14
15
  include SortMethod
16
+ include Bitop
15
17
  include TransactionCommands
16
18
  include CommandExecutor
17
19
 
@@ -25,9 +25,6 @@ module Rediska
25
25
  end
26
26
 
27
27
  # NOT IMPLEMENTED:
28
- # * blpop
29
- # * brpop
30
- # * brpoplpush
31
28
  # * subscribe
32
29
  # * psubscribe
33
30
  # * publish
@@ -301,6 +298,8 @@ module Rediska
301
298
  end
302
299
 
303
300
  def rpush(key, value)
301
+ raise_argument_error('rpush') if value.respond_to?(:each) && value.empty?
302
+
304
303
  data_type_check(key, Array)
305
304
  data[key] ||= []
306
305
  [value].flatten.each do |val|
@@ -310,12 +309,16 @@ module Rediska
310
309
  end
311
310
 
312
311
  def rpushx(key, value)
312
+ raise_argument_error('rpushx') if value.respond_to?(:each) && value.empty?
313
+
313
314
  data_type_check(key, Array)
314
315
  return unless data[key]
315
316
  rpush(key, value)
316
317
  end
317
318
 
318
319
  def lpush(key, value)
320
+ raise_argument_error('lpush') if value.respond_to?(:each) && value.empty?
321
+
319
322
  data_type_check(key, Array)
320
323
  data[key] ||= []
321
324
  [value].flatten.each do |val|
@@ -325,6 +328,8 @@ module Rediska
325
328
  end
326
329
 
327
330
  def lpushx(key, value)
331
+ raise_argument_error('lpushx') if value.respond_to?(:each) && value.empty?
332
+
328
333
  data_type_check(key, Array)
329
334
  return unless data[key]
330
335
  lpush(key, value)
@@ -336,6 +341,17 @@ module Rediska
336
341
  data[key].pop
337
342
  end
338
343
 
344
+ def brpop(keys, timeout = 0)
345
+ keys = Array(keys)
346
+ keys.each do |key|
347
+ if data[key] && data[key].size > 0
348
+ return [key, data[key].pop]
349
+ end
350
+ end
351
+ sleep(timeout.to_f)
352
+ nil
353
+ end
354
+
339
355
  def rpoplpush(key1, key2)
340
356
  data_type_check(key1, Array)
341
357
  rpop(key1).tap do |elem|
@@ -343,12 +359,30 @@ module Rediska
343
359
  end
344
360
  end
345
361
 
362
+ def brpoplpush(key1, key2, opts = {})
363
+ data_type_check(key1, Array)
364
+ brpop(key1).tap do |elem|
365
+ lpush(key2, elem) unless elem.nil?
366
+ end
367
+ end
368
+
346
369
  def lpop(key)
347
370
  data_type_check(key, Array)
348
371
  return unless data[key]
349
372
  data[key].shift
350
373
  end
351
374
 
375
+ def blpop(keys, timeout = 0)
376
+ keys = Array(keys)
377
+ keys.each do |key|
378
+ if data[key] && data[key].size > 0
379
+ return [key, data[key].shift]
380
+ end
381
+ end
382
+ sleep(timeout.to_f)
383
+ nil
384
+ end
385
+
352
386
  def smembers(key)
353
387
  data_type_check(key, ::Set)
354
388
  return [] unless data[key]
@@ -1,3 +1,3 @@
1
1
  module Rediska
2
- VERSION = '0.2.7'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -12,6 +12,7 @@ shared_examples 'redis' do
12
12
  it_behaves_like 'transactions'
13
13
  it_behaves_like 'sorted sets'
14
14
  it_behaves_like 'upcase method names'
15
+ it_behaves_like 'bitop'
15
16
 
16
17
  it_behaves_like 'driver'
17
18
  it_behaves_like 'sidekiq'
@@ -0,0 +1,267 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'a bitwise operation' do |operator|
4
+ it 'raises an argument error when not passed any source keys' do
5
+ expect {
6
+ subject.bitop(operator, 'destkey')
7
+ }.to raise_error(Redis::CommandError)
8
+ end
9
+
10
+ it 'should not create destination key if nothing found' do
11
+ expect(subject.bitop(operator, 'dest1', 'nothing_here1')).to eq(0)
12
+ expect(subject.exists('dest1')).to be_falsy
13
+ end
14
+
15
+ it 'should accept operator as a case-insensitive symbol' do
16
+ subject.set('key1', 'foobar')
17
+ subject.bitop(operator.to_s.downcase.to_sym, 'dest1', 'key1')
18
+ subject.bitop(operator.to_s.upcase.to_sym, 'dest2', 'key1')
19
+
20
+ expect(subject.get('dest1')).to eq('foobar')
21
+ expect(subject.get('dest2')).to eq('foobar')
22
+ end
23
+
24
+ it 'should accept operator as a case-insensitive string' do
25
+ subject.set('key1', 'foobar')
26
+ subject.bitop(operator.to_s.downcase, 'dest1', 'key1')
27
+ subject.bitop(operator.to_s.upcase, 'dest2', 'key1')
28
+
29
+ expect(subject.get('dest1')).to eq('foobar')
30
+ expect(subject.get('dest2')).to eq('foobar')
31
+ end
32
+
33
+ it 'should copy original string for single key' do
34
+ subject.set('key1', 'foobar')
35
+ subject.bitop(operator, 'dest1', 'key1')
36
+
37
+ expect(subject.get('dest1')).to eq('foobar')
38
+ end
39
+
40
+ it 'should copy original string for single key' do
41
+ subject.set('key1', 'foobar')
42
+ subject.bitop(operator, 'dest1', 'key1')
43
+
44
+ expect(subject.get('dest1')).to eq('foobar')
45
+ end
46
+
47
+ it 'should return length of the string stored in the destination key' do
48
+ subject.set('key1', 'foobar')
49
+ subject.set('key2', 'baz')
50
+
51
+ expect(subject.bitop(operator, 'dest1', 'key1')).to eq(6)
52
+ expect(subject.bitop(operator, 'dest2', 'key2')).to eq(3)
53
+ end
54
+
55
+ it 'should overwrite previous value with new one' do
56
+ subject.set('key1', 'foobar')
57
+ subject.set('key2', 'baz')
58
+ subject.bitop(operator, 'dest1', 'key1')
59
+ subject.bitop(operator, 'dest1', 'key2')
60
+
61
+ expect(subject.get('dest1')).to eq('baz')
62
+ end
63
+ end
64
+
65
+ shared_examples 'bitop' do
66
+ it 'raises an argument error when passed unsupported operation' do
67
+ expect {
68
+ subject.bitop('meh', 'dest1', 'key1')
69
+ }.to raise_error(Redis::CommandError)
70
+ end
71
+
72
+ describe 'or' do
73
+ it_should_behave_like 'a bitwise operation', 'or'
74
+
75
+ it 'should apply bitwise or operation' do
76
+ subject.setbit('key1', 0, 0)
77
+ subject.setbit('key1', 1, 1)
78
+ subject.setbit('key1', 2, 1)
79
+ subject.setbit('key1', 3, 0)
80
+
81
+ subject.setbit('key2', 0, 1)
82
+ subject.setbit('key2', 1, 1)
83
+ subject.setbit('key2', 2, 0)
84
+ subject.setbit('key2', 3, 0)
85
+
86
+ expect(subject.bitop('or', 'dest1', 'key1', 'key2')).to eq(1)
87
+ expect(subject.bitcount('dest1')).to eq(3)
88
+ expect(subject.getbit('dest1', 0)).to eq(1)
89
+ expect(subject.getbit('dest1', 1)).to eq(1)
90
+ expect(subject.getbit('dest1', 2)).to eq(1)
91
+ expect(subject.getbit('dest1', 3)).to eq(0)
92
+ end
93
+
94
+ it 'should apply bitwise or operation with empty values' do
95
+ subject.setbit('key1', 1, 1)
96
+
97
+ expect(subject.bitop('or', 'dest1', 'key1', 'nothing_here1', 'nothing_here2')).to eq(1)
98
+ expect(subject.bitcount('dest1')).to eq(1)
99
+ expect(subject.getbit('dest1', 0)).to eq(0)
100
+ expect(subject.getbit('dest1', 1)).to eq(1)
101
+ expect(subject.getbit('dest1', 2)).to eq(0)
102
+ end
103
+
104
+ it 'should apply bitwise or operation with multiple keys' do
105
+ subject.setbit('key1', 1, 1)
106
+ subject.setbit('key1', 3, 1)
107
+
108
+ subject.setbit('key2', 5, 1)
109
+ subject.setbit('key2', 10, 1)
110
+
111
+ subject.setbit('key3', 13, 1)
112
+ subject.setbit('key3', 15, 1)
113
+
114
+ expect(subject.bitop('or', 'dest1', 'key1', 'key2', 'key3')).to eq(2)
115
+ expect(subject.bitcount('dest1')).to eq(6)
116
+ expect(subject.getbit('dest1', 1)).to eq(1)
117
+ expect(subject.getbit('dest1', 3)).to eq(1)
118
+ expect(subject.getbit('dest1', 5)).to eq(1)
119
+ expect(subject.getbit('dest1', 10)).to eq(1)
120
+ expect(subject.getbit('dest1', 13)).to eq(1)
121
+ expect(subject.getbit('dest1', 15)).to eq(1)
122
+ expect(subject.getbit('dest1', 2)).to eq(0)
123
+ expect(subject.getbit('dest1', 12)).to eq(0)
124
+ end
125
+ end
126
+
127
+ describe 'and' do
128
+ it_should_behave_like 'a bitwise operation', 'and'
129
+
130
+ it 'should apply bitwise and operation' do
131
+ subject.setbit('key1', 0, 1)
132
+ subject.setbit('key1', 1, 1)
133
+ subject.setbit('key1', 2, 0)
134
+
135
+ subject.setbit('key2', 0, 0)
136
+ subject.setbit('key2', 1, 1)
137
+ subject.setbit('key2', 2, 1)
138
+
139
+ expect(subject.bitop('and', 'dest1', 'key1', 'key2')).to eq(1)
140
+ expect(subject.bitcount('dest1')).to eq(1)
141
+ expect(subject.getbit('dest1', 0)).to eq(0)
142
+ expect(subject.getbit('dest1', 1)).to eq(1)
143
+ expect(subject.getbit('dest1', 2)).to eq(0)
144
+ end
145
+
146
+ it 'should apply bitwise and operation with empty values' do
147
+ subject.setbit('key1', 1, 1)
148
+
149
+ expect(subject.bitop('and', 'dest1', 'key1', 'nothing_here')).to eq(1)
150
+ expect(subject.bitcount('dest1')).to eq(1)
151
+ expect(subject.getbit('dest1', 0)).to eq(0)
152
+ expect(subject.getbit('dest1', 1)).to eq(1)
153
+ expect(subject.getbit('dest1', 2)).to eq(0)
154
+ end
155
+
156
+ it 'should apply bitwise and operation with multiple keys' do
157
+ subject.setbit('key1', 1, 1)
158
+ subject.setbit('key1', 2, 1)
159
+ subject.setbit('key1', 3, 1)
160
+ subject.setbit('key1', 4, 1)
161
+
162
+ subject.setbit('key2', 2, 1)
163
+ subject.setbit('key2', 3, 1)
164
+ subject.setbit('key2', 4, 1)
165
+ subject.setbit('key2', 5, 1)
166
+
167
+ subject.setbit('key3', 2, 1)
168
+ subject.setbit('key3', 4, 1)
169
+ subject.setbit('key3', 5, 1)
170
+ subject.setbit('key3', 6, 1)
171
+
172
+ expect(subject.bitop('and', 'dest1', 'key1', 'key2', 'key3')).to eq(1)
173
+ expect(subject.bitcount('dest1')).to eq(2)
174
+ expect(subject.getbit('dest1', 1)).to eq(0)
175
+ expect(subject.getbit('dest1', 2)).to eq(1)
176
+ expect(subject.getbit('dest1', 3)).to eq(0)
177
+ expect(subject.getbit('dest1', 4)).to eq(1)
178
+ expect(subject.getbit('dest1', 5)).to eq(0)
179
+ expect(subject.getbit('dest1', 6)).to eq(0)
180
+ end
181
+ end
182
+
183
+ describe 'xor' do
184
+ it_should_behave_like 'a bitwise operation', 'xor'
185
+
186
+ it 'should apply bitwise xor operation' do
187
+ subject.setbit('key1', 0, 0)
188
+ subject.setbit('key1', 1, 1)
189
+ subject.setbit('key1', 2, 0)
190
+ subject.setbit('key1', 3, 0)
191
+
192
+ subject.setbit('key2', 0, 1)
193
+ subject.setbit('key2', 1, 1)
194
+ subject.setbit('key2', 2, 1)
195
+ subject.setbit('key2', 3, 0)
196
+
197
+ expect(subject.bitop('xor', 'dest1', 'key1', 'key2')).to eq(1)
198
+ expect(subject.bitcount('dest1')).to eq(2)
199
+ expect(subject.getbit('dest1', 0)).to eq(1)
200
+ expect(subject.getbit('dest1', 1)).to eq(0)
201
+ expect(subject.getbit('dest1', 2)).to eq(1)
202
+ expect(subject.getbit('dest1', 3)).to eq(0)
203
+ end
204
+
205
+ it 'should apply bitwise xor operation with empty values' do
206
+ subject.setbit('key1', 1, 1)
207
+
208
+ expect(subject.bitop('xor', 'dest1', 'key1', 'nothing_here1', 'nothing_here2')).to eq(1)
209
+ expect(subject.bitcount('dest1')).to eq(1)
210
+ expect(subject.getbit('dest1', 0)).to eq(0)
211
+ expect(subject.getbit('dest1', 1)).to eq(1)
212
+ expect(subject.getbit('dest1', 2)).to eq(0)
213
+ end
214
+
215
+ it 'should apply bitwise xor operation with multiple keys' do
216
+ subject.setbit('key1', 1, 1)
217
+ subject.setbit('key1', 3, 1)
218
+ subject.setbit('key1', 5, 1)
219
+ subject.setbit('key1', 6, 1)
220
+
221
+ subject.setbit('key2', 2, 1)
222
+ subject.setbit('key2', 3, 1)
223
+ subject.setbit('key2', 4, 1)
224
+ subject.setbit('key2', 6, 1)
225
+
226
+ expect(subject.bitop('xor', 'dest1', 'key1', 'key2')).to eq(1)
227
+ expect(subject.bitcount('dest1')).to eq(4)
228
+ expect(subject.getbit('dest1', 1)).to eq(1)
229
+ expect(subject.getbit('dest1', 2)).to eq(1)
230
+ expect(subject.getbit('dest1', 3)).to eq(0)
231
+ expect(subject.getbit('dest1', 4)).to eq(1)
232
+ expect(subject.getbit('dest1', 5)).to eq(1)
233
+ expect(subject.getbit('dest1', 6)).to eq(0)
234
+ end
235
+ end
236
+
237
+ describe 'not' do
238
+ it 'raises an argument error when not passed any keys' do
239
+ expect {
240
+ subject.bitop('not', 'destkey')
241
+ }.to raise_error(Redis::CommandError)
242
+ end
243
+
244
+ it 'raises an argument error when not passed too many keys' do
245
+ expect {
246
+ subject.bitop('not', 'destkey', 'key1', 'key2')
247
+ }.to raise_error(Redis::CommandError)
248
+ end
249
+
250
+ it 'should apply bitwise negation operation' do
251
+ subject.setbit('key1', 1, 1)
252
+ subject.setbit('key1', 3, 1)
253
+ subject.setbit('key1', 5, 1)
254
+
255
+ expect(subject.bitop('not', 'dest1', 'key1')).to eq(1)
256
+ expect(subject.bitcount('dest1')).to eq(5)
257
+ expect(subject.getbit('dest1', 0)).to eq(1)
258
+ expect(subject.getbit('dest1', 1)).to eq(0)
259
+ expect(subject.getbit('dest1', 2)).to eq(1)
260
+ expect(subject.getbit('dest1', 3)).to eq(0)
261
+ expect(subject.getbit('dest1', 4)).to eq(1)
262
+ expect(subject.getbit('dest1', 5)).to eq(0)
263
+ expect(subject.getbit('dest1', 6)).to eq(1)
264
+ expect(subject.getbit('dest1', 7)).to eq(1)
265
+ end
266
+ end
267
+ end
@@ -200,4 +200,24 @@ shared_examples 'lists' do
200
200
  expect(subject.lrange('key1', 0, -1)).to eq(['one', 'two'])
201
201
  expect(subject.lrange('key2', 0, -1)).to be_empty
202
202
  end
203
+
204
+ it 'should not allow pushing empty list of objects' do
205
+ expect {
206
+ subject.lpush('key1', [])
207
+ }.to raise_error(Redis::CommandError, /lpush[^x]/)
208
+
209
+ expect {
210
+ subject.lpush('key1', 1)
211
+ subject.lpushx('key1', [])
212
+ }.to raise_error(Redis::CommandError, /lpushx/)
213
+
214
+ expect {
215
+ subject.rpush('key1', [])
216
+ }.to raise_error(Redis::CommandError, /rpush[^x]/)
217
+
218
+ expect {
219
+ subject.rpush('key1', 1)
220
+ subject.rpushx('key1', [])
221
+ }.to raise_error(Redis::CommandError, /rpushx/)
222
+ end
203
223
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rediska
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leonid Beder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-28 00:00:00.000000000 Z
11
+ date: 2015-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -97,6 +97,7 @@ files:
97
97
  - README.md
98
98
  - Rakefile
99
99
  - lib/rediska.rb
100
+ - lib/rediska/bitop.rb
100
101
  - lib/rediska/command_executor.rb
101
102
  - lib/rediska/configuration.rb
102
103
  - lib/rediska/connection.rb
@@ -114,6 +115,7 @@ files:
114
115
  - rediska.gemspec
115
116
  - spec/redis_spec.rb
116
117
  - spec/spec_helper.rb
118
+ - spec/support/bitop.rb
117
119
  - spec/support/compatibility.rb
118
120
  - spec/support/connection.rb
119
121
  - spec/support/driver.rb
@@ -155,6 +157,7 @@ summary: A light-weighted redis driver for testing, development, and minimal env
155
157
  test_files:
156
158
  - spec/redis_spec.rb
157
159
  - spec/spec_helper.rb
160
+ - spec/support/bitop.rb
158
161
  - spec/support/compatibility.rb
159
162
  - spec/support/connection.rb
160
163
  - spec/support/driver.rb