rediska 0.2.7 → 0.3.0

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 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