redis-objects-legacy 1.6.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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +16 -0
- data/ATOMICITY.rdoc +154 -0
- data/CHANGELOG.rdoc +362 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README.md +600 -0
- data/Rakefile +14 -0
- data/lib/redis/base_object.rb +62 -0
- data/lib/redis/counter.rb +145 -0
- data/lib/redis/enumerable_object.rb +28 -0
- data/lib/redis/hash_key.rb +163 -0
- data/lib/redis/helpers/core_commands.rb +89 -0
- data/lib/redis/list.rb +160 -0
- data/lib/redis/lock.rb +89 -0
- data/lib/redis/objects/connection_pool_proxy.rb +31 -0
- data/lib/redis/objects/counters.rb +155 -0
- data/lib/redis/objects/hashes.rb +60 -0
- data/lib/redis/objects/lists.rb +58 -0
- data/lib/redis/objects/locks.rb +73 -0
- data/lib/redis/objects/sets.rb +58 -0
- data/lib/redis/objects/sorted_sets.rb +49 -0
- data/lib/redis/objects/values.rb +64 -0
- data/lib/redis/objects/version.rb +5 -0
- data/lib/redis/objects.rb +199 -0
- data/lib/redis/set.rb +182 -0
- data/lib/redis/sorted_set.rb +325 -0
- data/lib/redis/value.rb +65 -0
- data/lib/redis-objects-legacy.rb +1 -0
- data/spec/redis_autoload_objects_spec.rb +46 -0
- data/spec/redis_namespace_compat_spec.rb +24 -0
- data/spec/redis_objects_active_record_spec.rb +162 -0
- data/spec/redis_objects_conn_spec.rb +276 -0
- data/spec/redis_objects_custom_serializer.rb +198 -0
- data/spec/redis_objects_instance_spec.rb +1666 -0
- data/spec/redis_objects_model_spec.rb +1097 -0
- data/spec/spec_helper.rb +92 -0
- metadata +214 -0
@@ -0,0 +1,1666 @@
|
|
1
|
+
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
|
+
|
4
|
+
require 'redis/objects'
|
5
|
+
|
6
|
+
describe Redis::Value do
|
7
|
+
before do
|
8
|
+
@value = Redis::Value.new('spec/value')
|
9
|
+
@value.delete
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should marshal default value" do
|
13
|
+
@value = Redis::Value.new('spec/value', :default => {:json => 'data'}, :marshal => true)
|
14
|
+
@value.value.should == {:json => 'data'}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be able to set the default value to false" do
|
18
|
+
@value = Redis::Value.new('spec/value', :default => false, :marshal => true)
|
19
|
+
@value.value.should == false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should handle simple values" do
|
23
|
+
@value.should == nil
|
24
|
+
@value.value = 'Trevor Hoffman'
|
25
|
+
@value.should == 'Trevor Hoffman'
|
26
|
+
@value.get.should == 'Trevor Hoffman'
|
27
|
+
@value.exists?.should == true
|
28
|
+
@value.exists.should == 1
|
29
|
+
@value.del.should == 1
|
30
|
+
@value.should.be.nil
|
31
|
+
@value.exists?.should == false
|
32
|
+
@value.exists.should == 0
|
33
|
+
@value.value = 42
|
34
|
+
@value.value.should == '42'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should compress non marshaled values" do
|
38
|
+
@value = Redis::Value.new('spec/value', compress: true)
|
39
|
+
@value.value = 'Trevor Hoffman'
|
40
|
+
@value.value.should == 'Trevor Hoffman'
|
41
|
+
@value.redis.get(@value.key).should.not == 'Trevor Hoffman'
|
42
|
+
@value.value = nil
|
43
|
+
@value.value.should == nil
|
44
|
+
@value.value = ''
|
45
|
+
@value.value.should == ''
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should compress marshaled values" do
|
49
|
+
@value = Redis::Value.new('spec/value', marshal: true, compress: true)
|
50
|
+
@value.value = 'Trevor Hoffman'
|
51
|
+
@value.value.should == 'Trevor Hoffman'
|
52
|
+
@value.redis.get(@value.key).should.not == Marshal.dump('Trevor Hoffman')
|
53
|
+
@value.value = nil
|
54
|
+
@value.value.should == nil
|
55
|
+
@value.value = ''
|
56
|
+
@value.value.should == ''
|
57
|
+
@value.delete
|
58
|
+
@value.value.should.be.nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should handle complex marshaled values" do
|
62
|
+
@value.options[:marshal] = true
|
63
|
+
@value.should == nil
|
64
|
+
@value.value = {:json => 'data'}
|
65
|
+
@value.should == {:json => 'data'}
|
66
|
+
|
67
|
+
# no marshaling
|
68
|
+
@value.options[:marshal] = false
|
69
|
+
v = {:json => 'data'}
|
70
|
+
@value.value = v
|
71
|
+
@value.should == v.to_s
|
72
|
+
|
73
|
+
@value.options[:marshal] = true
|
74
|
+
@value.value = [[1,2], {:t3 => 4}]
|
75
|
+
@value.should == [[1,2], {:t3 => 4}]
|
76
|
+
@value.get.should == [[1,2], {:t3 => 4}]
|
77
|
+
@value.del.should == 1
|
78
|
+
@value.should.be.nil
|
79
|
+
@value.options[:marshal] = false
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should not erroneously unmarshall a string" do
|
83
|
+
json_string = {json: 'value'}
|
84
|
+
@value = Redis::Value.new('spec/value', :marshal => true)
|
85
|
+
@value.value = json_string
|
86
|
+
@value.value.should == json_string
|
87
|
+
@value.clear
|
88
|
+
|
89
|
+
default_json_string = {json: 'default'}
|
90
|
+
@value = Redis::Value.new('spec/default', :default => default_json_string, :marshal => true)
|
91
|
+
@value.value.should == default_json_string
|
92
|
+
@value.clear
|
93
|
+
|
94
|
+
marshalled_string = Marshal.dump({json: 'marshal'})
|
95
|
+
@value = Redis::Value.new('spec/marshal', :default => marshalled_string, :marshal => true)
|
96
|
+
@value.value.should == marshalled_string
|
97
|
+
@value.delete
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should support renaming values" do
|
101
|
+
@value.value = 'Peter Pan'
|
102
|
+
@value.key.should == 'spec/value'
|
103
|
+
@value.rename('spec/value2') # can't test result; switched from true to "OK"
|
104
|
+
@value.key.should == 'spec/value2'
|
105
|
+
@value.should == 'Peter Pan'
|
106
|
+
old = Redis::Value.new('spec/value')
|
107
|
+
old.should.be.nil
|
108
|
+
old.value = 'Tuff'
|
109
|
+
@value.renamenx('spec/value') # can't test result; switched from true to "OK"
|
110
|
+
@value.value.should == 'Peter Pan'
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should provide a readable inspect" do
|
114
|
+
@value.value = 'monkey'
|
115
|
+
@value.inspect.should == '#<Redis::Value "monkey">'
|
116
|
+
@value.value = 1234
|
117
|
+
@value.inspect.should == '#<Redis::Value "1234">'
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should delegate unrecognized methods to the value' do
|
121
|
+
@value.value = 'monkey'
|
122
|
+
@value.to_sym.should == :monkey
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should properly pass equality operations on to the value' do
|
126
|
+
@value.value = 'monkey'
|
127
|
+
@value.should == 'monkey'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should properly pass nil? on to the value' do
|
131
|
+
@value.delete
|
132
|
+
@value.nil?.should == true
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should equate setting the value to nil to deletion' do
|
136
|
+
@value.value = nil
|
137
|
+
@value.nil?.should == true
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "with expiration" do
|
141
|
+
[:value=, :set].each do |meth|
|
142
|
+
it "#{meth} should set time to live in seconds when expiration option assigned" do
|
143
|
+
@value = Redis::Value.new('spec/value', :expiration => 10)
|
144
|
+
@value.send(meth, 'monkey')
|
145
|
+
@value.ttl.should > 0
|
146
|
+
@value.ttl.should <= 10
|
147
|
+
end
|
148
|
+
|
149
|
+
it "#{meth} should set expiration when expireat option assigned" do
|
150
|
+
@value = Redis::Value.new('spec/value', :expireat => Time.now + 10.seconds)
|
151
|
+
@value.send(meth, 'monkey')
|
152
|
+
@value.ttl.should > 0
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
after do
|
158
|
+
@value.delete
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe Redis::List do
|
163
|
+
describe "as a bounded list" do
|
164
|
+
before do
|
165
|
+
@list = Redis::List.new('spec/bounded_list',
|
166
|
+
:maxlength => 10)
|
167
|
+
1.upto(10) do |i|
|
168
|
+
@list << i
|
169
|
+
end
|
170
|
+
|
171
|
+
# Make sure that adding < maxlength doesn't mess up.
|
172
|
+
1.upto(10) do |i|
|
173
|
+
@list.at(i - 1).should == i.to_s
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should push the first element out of the list" do
|
178
|
+
@list << '11'
|
179
|
+
@list.last.should == '11'
|
180
|
+
@list.first.should == '2'
|
181
|
+
@list.length.should == 10
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should push the last element out of the list for unshift" do
|
185
|
+
@list.unshift('0')
|
186
|
+
@list.last.should == '9'
|
187
|
+
@list.first.should == '0'
|
188
|
+
@list.length.should == 10
|
189
|
+
end
|
190
|
+
|
191
|
+
after do
|
192
|
+
@list.clear
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "basic operations" do
|
197
|
+
before do
|
198
|
+
@list = Redis::List.new('spec/list')
|
199
|
+
@list.clear
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should handle lists of simple values" do
|
203
|
+
@list.should.be.empty
|
204
|
+
@list << 'a'
|
205
|
+
@list.should == ['a']
|
206
|
+
@list.get.should == ['a']
|
207
|
+
@list.unshift 'b'
|
208
|
+
@list.to_s.should == 'b, a'
|
209
|
+
@list.should == ['b','a']
|
210
|
+
@list.get.should == ['b','a']
|
211
|
+
@list.push 'c'
|
212
|
+
@list.should == ['b','a','c']
|
213
|
+
@list.get.should == ['b','a','c']
|
214
|
+
@list.first.should == 'b'
|
215
|
+
@list.last.should == 'c'
|
216
|
+
@list << 'd'
|
217
|
+
@list.should == ['b','a','c','d']
|
218
|
+
@list[1].should == 'a'
|
219
|
+
@list[0].should == 'b'
|
220
|
+
@list[2].should == 'c'
|
221
|
+
@list[3].should == 'd'
|
222
|
+
@list.include?('c').should.be.true
|
223
|
+
@list.include?('no').should.be.false
|
224
|
+
@list.pop.should == 'd'
|
225
|
+
@list[0].should == @list.at(0)
|
226
|
+
@list[1].should == @list.at(1)
|
227
|
+
@list[2].should == @list.at(2)
|
228
|
+
@list.should == ['b','a','c']
|
229
|
+
@list.get.should == ['b','a','c']
|
230
|
+
@list.shift.should == 'b'
|
231
|
+
@list.should == ['a','c']
|
232
|
+
@list.get.should == ['a','c']
|
233
|
+
@list << 'e' << 'f' << 'e'
|
234
|
+
@list.should == ['a','c','e','f','e']
|
235
|
+
@list.get.should == ['a','c','e','f','e']
|
236
|
+
@list.delete('e').should == 2
|
237
|
+
@list.should == ['a','c','f']
|
238
|
+
@list.get.should == ['a','c','f']
|
239
|
+
@list << 'j'
|
240
|
+
@list.should == ['a','c','f','j']
|
241
|
+
@list.push 'h'
|
242
|
+
@list.push 'i', 'j'
|
243
|
+
@list.should == ['a','c','f','j','h','i','j']
|
244
|
+
# Test against similar Ruby functionality
|
245
|
+
a = @list.values
|
246
|
+
@list[0..2].should == a[0..2]
|
247
|
+
@list[0...2].should == a[0...2]
|
248
|
+
@list.slice(0..2).should == a.slice(0..2)
|
249
|
+
@list[0, 2].should == a[0, 2]
|
250
|
+
@list.range(0, 2).should == a[0..2] # range for Redis works like .. in Ruby
|
251
|
+
@list[0, 1].should == a[0, 1]
|
252
|
+
@list.range(0, 1).should == a[0..1] # range for Redis works like .. in Ruby
|
253
|
+
@list[1, 3].should == a[1, 3]
|
254
|
+
@list.slice(1, 3).should == a.slice(1, 3)
|
255
|
+
@list[0, 0].should == []
|
256
|
+
@list[0, -1].should == a[0, -1]
|
257
|
+
@list.length.should == 7
|
258
|
+
@list.should == a
|
259
|
+
@list.get.should == a
|
260
|
+
@list.pop # lose 'j'
|
261
|
+
@list.size.should == 6
|
262
|
+
|
263
|
+
i = -1
|
264
|
+
@list.each do |st|
|
265
|
+
st.should == @list[i += 1]
|
266
|
+
end
|
267
|
+
@list.should == ['a','c','f','j','h','i']
|
268
|
+
@list.get.should == ['a','c','f','j','h','i']
|
269
|
+
|
270
|
+
@list.each_with_index do |st,i|
|
271
|
+
st.should == @list[i]
|
272
|
+
end
|
273
|
+
@list.should == ['a','c','f','j','h','i']
|
274
|
+
@list.get.should == ['a','c','f','j','h','i']
|
275
|
+
|
276
|
+
coll = @list.collect{|st| st}
|
277
|
+
coll.should == ['a','c','f','j','h','i']
|
278
|
+
@list.should == ['a','c','f','j','h','i']
|
279
|
+
@list.get.should == ['a','c','f','j','h','i']
|
280
|
+
|
281
|
+
@list << 'a'
|
282
|
+
coll = @list.select{|st| st == 'a'}
|
283
|
+
coll.should == ['a','a']
|
284
|
+
@list.should == ['a','c','f','j','h','i','a']
|
285
|
+
@list.get.should == ['a','c','f','j','h','i','a']
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should support popping & shifting multiple values" do
|
289
|
+
@list.should.be.empty
|
290
|
+
|
291
|
+
@list << 'a' << 'b' << 'c'
|
292
|
+
@list.shift(2).should == ['a', 'b']
|
293
|
+
@list.shift(2).should == ['c']
|
294
|
+
@list.shift(2).should == []
|
295
|
+
|
296
|
+
@list << 'a' << 'b' << 'c'
|
297
|
+
@list.pop(2).should == ['b', 'c']
|
298
|
+
@list.pop(2).should == ['a']
|
299
|
+
@list.pop(2).should == []
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should handle rpoplpush" do
|
303
|
+
list2 = Redis::List.new("spec/list2")
|
304
|
+
list2.clear
|
305
|
+
|
306
|
+
@list << "a" << "b"
|
307
|
+
result = @list.rpoplpush(list2)
|
308
|
+
result.should == "b"
|
309
|
+
@list.should == ["a"]
|
310
|
+
list2.should == ["b"]
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should handle insert" do
|
314
|
+
@list << 'b' << 'd'
|
315
|
+
@list.insert(:before,'b','a')
|
316
|
+
@list.insert(:after,'b','c')
|
317
|
+
@list.insert("before",'a','z')
|
318
|
+
@list.insert("after",'d','e')
|
319
|
+
@list.should == ['z','a','b','c','d','e']
|
320
|
+
end
|
321
|
+
|
322
|
+
it "should handle insert at a specific index" do
|
323
|
+
@list << 'b' << 'd'
|
324
|
+
@list.should == ['b','d']
|
325
|
+
@list[0] = 'a'
|
326
|
+
@list.should == ['a', 'd']
|
327
|
+
@list[1] = 'b'
|
328
|
+
@list.should == ['a', 'b']
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should handle lists of complex data types" do
|
332
|
+
@list.options[:marshal] = true
|
333
|
+
v1 = {:json => 'data'}
|
334
|
+
v2 = {:json2 => 'data2'}
|
335
|
+
v3 = [1,2,3]
|
336
|
+
@list << v1
|
337
|
+
@list << v2
|
338
|
+
@list.first.should == v1
|
339
|
+
@list[0] = @list[0].tap{|d| d[:json] = 'data_4'}
|
340
|
+
@list.first.should == {:json => 'data_4'}
|
341
|
+
@list.last.should == v2
|
342
|
+
@list << [1,2,3,[4,5],6]
|
343
|
+
@list.last.should == [1,2,3,[4,5],6]
|
344
|
+
@list.shift.should == {:json => 'data_4'}
|
345
|
+
@list.size.should == 2
|
346
|
+
@list.delete(v2)
|
347
|
+
@list.size.should == 1
|
348
|
+
@list.push v1, v2
|
349
|
+
@list[1].should == v1
|
350
|
+
@list.last.should == v2
|
351
|
+
@list.size.should == 3
|
352
|
+
@list.unshift v2, v3
|
353
|
+
@list.size.should == 5
|
354
|
+
@list.first.should == v3
|
355
|
+
@list.options[:marshal] = false
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should support renaming lists" do
|
359
|
+
@list.should.be.empty
|
360
|
+
@list << 'a' << 'b' << 'a' << 3
|
361
|
+
@list.should == ['a','b','a','3']
|
362
|
+
@list.key.should == 'spec/list'
|
363
|
+
@list.rename('spec/list3', false) # can't test result; switched from true to "OK"
|
364
|
+
@list.key.should == 'spec/list'
|
365
|
+
@list.redis.del('spec/list3')
|
366
|
+
@list << 'a' << 'b' << 'a' << 3
|
367
|
+
@list.rename('spec/list2') # can't test result; switched from true to "OK"
|
368
|
+
@list.key.should == 'spec/list2'
|
369
|
+
@list.redis.lrange(@list.key, 0, 3).should == ['a','b','a','3']
|
370
|
+
old = Redis::List.new('spec/list')
|
371
|
+
old.should.be.empty
|
372
|
+
old << 'Tuff'
|
373
|
+
old.values.should == ['Tuff']
|
374
|
+
@list.renamenx('spec/list').should.be.false
|
375
|
+
@list.renamenx(old).should.be.false
|
376
|
+
@list.renamenx('spec/foo').should.be.true
|
377
|
+
old.values.should == ['Tuff']
|
378
|
+
@list.clear
|
379
|
+
@list.redis.del('spec/list2')
|
380
|
+
end
|
381
|
+
|
382
|
+
it "responds to #value" do
|
383
|
+
@list << 'a'
|
384
|
+
@list.value.should == @list.get
|
385
|
+
@list.value.should == ['a']
|
386
|
+
end
|
387
|
+
|
388
|
+
it "should support to_json" do
|
389
|
+
@list << 'a'
|
390
|
+
JSON.parse(@list.to_json)['value'].should == ['a']
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should support as_json" do
|
394
|
+
@list << 'a'
|
395
|
+
@list.as_json['value'].should == ['a']
|
396
|
+
end
|
397
|
+
|
398
|
+
after do
|
399
|
+
@list.clear
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe 'with expiration' do
|
404
|
+
[:push, :<<, :unshift].each do |meth, args|
|
405
|
+
it "#{meth} expiration: option" do
|
406
|
+
@list = Redis::List.new('spec/list_exp', :expiration => 10)
|
407
|
+
@list.clear
|
408
|
+
@list.send(meth, 'val')
|
409
|
+
@list.ttl.should > 0
|
410
|
+
@list.ttl.should <= 10
|
411
|
+
end
|
412
|
+
|
413
|
+
it "#{meth} expireat: option" do
|
414
|
+
@list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
|
415
|
+
@list.clear
|
416
|
+
@list.send(meth, 'val')
|
417
|
+
@list.ttl.should > 0
|
418
|
+
@list.ttl.should <= 10
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
it "[]= expiration: option" do
|
423
|
+
@list = Redis::List.new('spec/list_exp', :expiration => 10)
|
424
|
+
@list.clear
|
425
|
+
@list.redis.rpush(@list.key, 'hello')
|
426
|
+
@list[0] = 'world'
|
427
|
+
@list.ttl.should > 0
|
428
|
+
@list.ttl.should <= 10
|
429
|
+
end
|
430
|
+
|
431
|
+
it "[]= expireat: option" do
|
432
|
+
@list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
|
433
|
+
@list.clear
|
434
|
+
@list.redis.rpush(@list.key, 'hello')
|
435
|
+
@list[0] = 'world'
|
436
|
+
@list.ttl.should > 0
|
437
|
+
@list.ttl.should <= 10
|
438
|
+
end
|
439
|
+
|
440
|
+
it "insert expiration: option" do
|
441
|
+
@list = Redis::List.new('spec/list_exp', :expiration => 10)
|
442
|
+
@list.clear
|
443
|
+
@list.redis.rpush(@list.key, 'hello')
|
444
|
+
@list.insert 'BEFORE', 'hello', 'world'
|
445
|
+
@list.ttl.should > 0
|
446
|
+
@list.ttl.should <= 10
|
447
|
+
end
|
448
|
+
|
449
|
+
it "insert expireat: option" do
|
450
|
+
@list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
|
451
|
+
@list.clear
|
452
|
+
@list.redis.rpush(@list.key, 'hello')
|
453
|
+
@list.insert 'BEFORE', 'hello', 'world'
|
454
|
+
@list.ttl.should > 0
|
455
|
+
@list.ttl.should <= 10
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
describe Redis::Counter do
|
461
|
+
before do
|
462
|
+
@counter = Redis::Counter.new('spec/counter')
|
463
|
+
@counter2 = Redis::Counter.new('spec/counter')
|
464
|
+
@counter.reset
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should support increment/decrement of counters" do
|
468
|
+
@counter.key.should == 'spec/counter'
|
469
|
+
@counter.incr(10)
|
470
|
+
@counter.should == 10
|
471
|
+
|
472
|
+
# math proxy ops
|
473
|
+
(@counter == 10).should.be.true
|
474
|
+
(@counter <= 10).should.be.true
|
475
|
+
(@counter < 11).should.be.true
|
476
|
+
(@counter > 9).should.be.true
|
477
|
+
(@counter >= 10).should.be.true
|
478
|
+
"#{@counter}".should == "10"
|
479
|
+
|
480
|
+
@counter.increment.should == 11
|
481
|
+
@counter.increment.should == 12
|
482
|
+
@counter2.increment.should == 13
|
483
|
+
@counter2.increment(2).should == 15
|
484
|
+
@counter.decrement.should == 14
|
485
|
+
@counter2.decrement.should == 13
|
486
|
+
@counter.decrement.should == 12
|
487
|
+
@counter2.decrement(4).should == 8
|
488
|
+
@counter.should == 8
|
489
|
+
@counter.reset.should.be.true
|
490
|
+
@counter.should == 0
|
491
|
+
@counter.reset(15).should.be.true
|
492
|
+
@counter.should == 15
|
493
|
+
@counter.getset(111).should == 15
|
494
|
+
@counter.should == 111
|
495
|
+
end
|
496
|
+
|
497
|
+
it "should support increment/decrement by float" do
|
498
|
+
@counter = Redis::Counter.new('spec/floater')
|
499
|
+
@counter.set 10.5
|
500
|
+
@counter.incrbyfloat 1
|
501
|
+
@counter.incrbyfloat 0.01
|
502
|
+
@counter.to_f.should == 11.51
|
503
|
+
@counter.set '5.0e3'
|
504
|
+
@counter.decrbyfloat -14.31
|
505
|
+
@counter.incrbyfloat 2.0e2
|
506
|
+
@counter.to_f.should == 5214.31
|
507
|
+
@counter.clear
|
508
|
+
@counter.should.be.nil
|
509
|
+
end
|
510
|
+
|
511
|
+
it "should support an atomic block" do
|
512
|
+
@counter = Redis::Counter.new("spec/block_counter")
|
513
|
+
@counter.should == 0
|
514
|
+
@counter.increment(1)
|
515
|
+
|
516
|
+
# successfully increments
|
517
|
+
@updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
|
518
|
+
@updated.should == 'yep'
|
519
|
+
@counter.should == 2
|
520
|
+
|
521
|
+
# fails to increment
|
522
|
+
@updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
|
523
|
+
@updated.should == nil
|
524
|
+
@counter.should == 2
|
525
|
+
|
526
|
+
# successfully increments by float
|
527
|
+
@updated = @counter.incrbyfloat(1.5) { |updated| updated == 3.5 ? 'yep' : nil }
|
528
|
+
@updated.should == 'yep'
|
529
|
+
@counter.should == 3.5
|
530
|
+
|
531
|
+
# fails to increment by float
|
532
|
+
@updated = @counter.incrbyfloat(2.5) { |updated| updated == 5 ? 'yep' : nil }
|
533
|
+
@updated.should == nil
|
534
|
+
@counter.should == 3.5
|
535
|
+
|
536
|
+
# fails to decrement by float
|
537
|
+
@updated = @counter.decrbyfloat(0.5) { |updated| updated == 5 ? 'yep' : nil }
|
538
|
+
@updated.should == nil
|
539
|
+
@counter.should == 3.5
|
540
|
+
|
541
|
+
# successfully decrements by float
|
542
|
+
@updated = @counter.decrbyfloat(0.5) { |updated| updated == 3 ? 'yep' : nil }
|
543
|
+
@updated.should == 'yep'
|
544
|
+
@counter.should == 3
|
545
|
+
|
546
|
+
# fails to decrement
|
547
|
+
@updated = @counter.decrement(1) { |updated| updated == 3 ? 'yep' : nil }
|
548
|
+
@updated.should == nil
|
549
|
+
@counter.should == 3
|
550
|
+
|
551
|
+
# successfully decrements
|
552
|
+
@updated = @counter.decrement(1) { |updated| updated == 2 ? 'yep' : nil }
|
553
|
+
@updated.should == 'yep'
|
554
|
+
@counter.should == 2
|
555
|
+
|
556
|
+
@counter.value = nil
|
557
|
+
@counter.should == 0
|
558
|
+
end
|
559
|
+
|
560
|
+
it "should support #to_json" do
|
561
|
+
@counter.increment
|
562
|
+
JSON.parse(@counter.to_json)['value'].should == 1
|
563
|
+
end
|
564
|
+
|
565
|
+
it "should support #as_json" do
|
566
|
+
@counter.increment
|
567
|
+
@counter.as_json['value'].should == 1
|
568
|
+
end
|
569
|
+
|
570
|
+
describe 'with expiration' do
|
571
|
+
it 'should set time to live in seconds' do
|
572
|
+
@counter = Redis::Counter.new('spec/counter', :expiration => 10)
|
573
|
+
@counter.increment
|
574
|
+
@counter.ttl.should > 0
|
575
|
+
@counter.ttl.should <= 10
|
576
|
+
end
|
577
|
+
|
578
|
+
[:increment, :incr, :incrby, :incrbyfloat,
|
579
|
+
:decrement, :decr, :decrby, :decrbyfloat, :reset].each do |meth|
|
580
|
+
describe meth do
|
581
|
+
it "expiration: option" do
|
582
|
+
@counter = Redis::Counter.new('spec/counter_exp', :expiration => 10)
|
583
|
+
@counter.send(meth)
|
584
|
+
@counter.ttl.should > 0
|
585
|
+
@counter.ttl.should <= 10
|
586
|
+
end
|
587
|
+
it "expireat: option" do
|
588
|
+
@counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
|
589
|
+
@counter.send(meth)
|
590
|
+
@counter.ttl.should > 0
|
591
|
+
@counter.ttl.should <= 10
|
592
|
+
end
|
593
|
+
after do
|
594
|
+
@counter.reset
|
595
|
+
end
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
[:set, :value=].each do |meth|
|
600
|
+
describe meth do
|
601
|
+
it "expiration: option" do
|
602
|
+
@counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
|
603
|
+
@counter.send(meth, 99)
|
604
|
+
@counter.should == 99
|
605
|
+
@counter.ttl.should > 0
|
606
|
+
@counter.ttl.should <= 10
|
607
|
+
end
|
608
|
+
it "expireat: option" do
|
609
|
+
@counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
|
610
|
+
@counter.send(meth, 99)
|
611
|
+
@counter.should == 99
|
612
|
+
@counter.ttl.should > 0
|
613
|
+
@counter.ttl.should <= 10
|
614
|
+
end
|
615
|
+
after do
|
616
|
+
@counter.reset
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
after do
|
623
|
+
@counter.delete
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
describe Redis::Lock do
|
628
|
+
before do
|
629
|
+
REDIS_HANDLE.flushall
|
630
|
+
end
|
631
|
+
|
632
|
+
it "should ttl to the expiration" do
|
633
|
+
expiry = 15
|
634
|
+
lock = Redis::Lock.new(:test_lock, :expiration => expiry)
|
635
|
+
lock.lock do
|
636
|
+
expiration = REDIS_HANDLE.ttl("test_lock")
|
637
|
+
|
638
|
+
# The expiration stored in redis should be 15 seconds from when we started
|
639
|
+
# or a little more
|
640
|
+
expiration.should.be.close(expiration, 2.0)
|
641
|
+
end
|
642
|
+
|
643
|
+
# key should have been cleaned up
|
644
|
+
REDIS_HANDLE.get("test_lock").should.be.nil
|
645
|
+
end
|
646
|
+
|
647
|
+
it "should set value to 1 when no expiration is set" do
|
648
|
+
lock = Redis::Lock.new(:test_lock)
|
649
|
+
lock.lock do
|
650
|
+
REDIS_HANDLE.ttl('test_lock').should == 1
|
651
|
+
end
|
652
|
+
|
653
|
+
# key should have been cleaned up
|
654
|
+
REDIS_HANDLE.get("test_lock").should.be.nil
|
655
|
+
end
|
656
|
+
|
657
|
+
it "should not let blocks execute if they timeout" do
|
658
|
+
expiry = 15
|
659
|
+
lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
|
660
|
+
|
661
|
+
# create a fake lock
|
662
|
+
REDIS_HANDLE.set("test_lock", (Time.now + expiry).to_f)
|
663
|
+
|
664
|
+
gotit = false
|
665
|
+
error = nil
|
666
|
+
begin
|
667
|
+
lock.lock do
|
668
|
+
gotit = true
|
669
|
+
end
|
670
|
+
rescue => error
|
671
|
+
end
|
672
|
+
|
673
|
+
error.should.be.kind_of(Redis::Lock::LockTimeout)
|
674
|
+
|
675
|
+
# should not have the lock
|
676
|
+
gotit.should.not.be.true
|
677
|
+
|
678
|
+
# lock value should still be set
|
679
|
+
REDIS_HANDLE.get("test_lock").should.not.be.nil
|
680
|
+
end
|
681
|
+
|
682
|
+
it "should handle a timeout of 0" do
|
683
|
+
expiry = 15
|
684
|
+
lock = Redis::Lock.new(:test_lock, :timeout => 0)
|
685
|
+
|
686
|
+
# create a fake lock
|
687
|
+
REDIS_HANDLE.set("test_lock", (Time.now + expiry).to_f)
|
688
|
+
|
689
|
+
gotit = false
|
690
|
+
error = nil
|
691
|
+
begin
|
692
|
+
lock.lock do
|
693
|
+
gotit = true
|
694
|
+
end
|
695
|
+
rescue => error
|
696
|
+
end
|
697
|
+
|
698
|
+
error.should.be.kind_of(Redis::Lock::LockTimeout)
|
699
|
+
|
700
|
+
# should not have the lock
|
701
|
+
gotit.should.not.be.true
|
702
|
+
|
703
|
+
# lock value should still be set
|
704
|
+
REDIS_HANDLE.get("test_lock").should.not.be.nil
|
705
|
+
end
|
706
|
+
|
707
|
+
it "should properly keep the lock across threads" do
|
708
|
+
lock = Redis::Lock.new(:test_lock0)
|
709
|
+
|
710
|
+
t1 = Thread.new do
|
711
|
+
lock.lock do
|
712
|
+
lock.exists?.should.be.true if RUNNING_LOCALLY
|
713
|
+
REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
|
714
|
+
sleep 1.0 # hang onto the lock across other thread
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
t2 = Thread.new do
|
719
|
+
# check for the lock from another thread
|
720
|
+
lock.exists?.should.be.true if RUNNING_LOCALLY
|
721
|
+
REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
|
722
|
+
end
|
723
|
+
|
724
|
+
t1.join
|
725
|
+
t2.join
|
726
|
+
|
727
|
+
# lock value should not be set since the lock was held for more than the expiry
|
728
|
+
lock.exists?.should.be.false
|
729
|
+
REDIS_HANDLE.exists?("test_lock0").should.be.false
|
730
|
+
end
|
731
|
+
|
732
|
+
it "Redis should remove the key if lock is held past expiration" do
|
733
|
+
lock = Redis::Lock.new(:test_lock1, :expiration => 0.1)
|
734
|
+
|
735
|
+
lock.lock do
|
736
|
+
lock.exists?.should.be.true
|
737
|
+
REDIS_HANDLE.exists?("test_lock1").should.be.true
|
738
|
+
sleep 1.0 # hang onto the lock > expiry
|
739
|
+
end
|
740
|
+
|
741
|
+
# lock value should not be set since the lock was held for more than the expiry
|
742
|
+
lock.exists?.should.be.false
|
743
|
+
REDIS_HANDLE.exists?("test_lock1").should.be.false
|
744
|
+
end
|
745
|
+
|
746
|
+
|
747
|
+
it "should not manually delete a key with a 'lock' name if finished after expiration" do
|
748
|
+
lock = Redis::Lock.new(:test_lock2, :expiration => 0.1)
|
749
|
+
|
750
|
+
lock.lock do
|
751
|
+
REDIS_HANDLE.exists?("test_lock2").should.be.true
|
752
|
+
sleep 1.0 # expired, key is deleted
|
753
|
+
REDIS_HANDLE.exists?("test_lock2").should.be.false
|
754
|
+
REDIS_HANDLE.set("test_lock2", "foo") # this is no longer a lock key, name is a coincidence
|
755
|
+
end
|
756
|
+
|
757
|
+
REDIS_HANDLE.get("test_lock2").should == "foo"
|
758
|
+
end
|
759
|
+
|
760
|
+
it "should manually delete the key if finished before expiration" do
|
761
|
+
lock = Redis::Lock.new(:test_lock3, :expiration => 10.0)
|
762
|
+
|
763
|
+
lock.lock do
|
764
|
+
REDIS_HANDLE.exists?("test_lock3").should.be.true
|
765
|
+
sleep 0.1
|
766
|
+
REDIS_HANDLE.exists?("test_lock3").should.be.true
|
767
|
+
end
|
768
|
+
|
769
|
+
# should delete the key because the lock block is done, regardless of expiry
|
770
|
+
# for some strange reason, I have seen this test fail randomly, which is worrisome.
|
771
|
+
REDIS_HANDLE.exists?("test_lock3").should.be.false
|
772
|
+
end
|
773
|
+
|
774
|
+
|
775
|
+
it "should respond to #to_json" do
|
776
|
+
Redis::Lock.new(:test_lock).to_json.should.be.kind_of(String)
|
777
|
+
end
|
778
|
+
|
779
|
+
it "should respond to #as_json" do
|
780
|
+
Redis::Lock.new(:test_lock).as_json.should.be.kind_of(Hash)
|
781
|
+
end
|
782
|
+
|
783
|
+
it "should deal with old lock format" do
|
784
|
+
expiry = 15
|
785
|
+
lock = Redis::Lock.new(:test_lock, expiration: expiry, timeout: 0.1)
|
786
|
+
|
787
|
+
# create a fake lock in the past
|
788
|
+
REDIS_HANDLE.set("test_lock", (Time.now - expiry).to_f)
|
789
|
+
|
790
|
+
gotit = false
|
791
|
+
lock.lock do
|
792
|
+
gotit = true
|
793
|
+
end
|
794
|
+
|
795
|
+
# should have the lock
|
796
|
+
gotit.should.be.true
|
797
|
+
|
798
|
+
# lock value should be unset
|
799
|
+
REDIS_HANDLE.get("test_lock").should.be.nil
|
800
|
+
end
|
801
|
+
|
802
|
+
end
|
803
|
+
|
804
|
+
describe Redis::HashKey do
|
805
|
+
describe "With Marshal" do
|
806
|
+
before do
|
807
|
+
@hash = Redis::HashKey.new('test_hash', {:marshal_keys=>{'created_at'=>true}})
|
808
|
+
@hash.clear
|
809
|
+
end
|
810
|
+
|
811
|
+
it "should marshal specified keys" do
|
812
|
+
@hash['created_at'] = Time.now
|
813
|
+
@hash['created_at'].class.should == Time
|
814
|
+
end
|
815
|
+
|
816
|
+
it "should not marshal unless required" do
|
817
|
+
@hash['updated_at'] = Time.now
|
818
|
+
@hash['updated_at'].class.should == String
|
819
|
+
end
|
820
|
+
|
821
|
+
it "should marshall appropriate key with bulk set and get" do
|
822
|
+
@hash.bulk_set({'created_at'=>Time.now, 'updated_at'=>Time.now})
|
823
|
+
|
824
|
+
@hash['created_at'].class.should == Time
|
825
|
+
@hash['updated_at'].class.should == String
|
826
|
+
|
827
|
+
h = @hash.bulk_get('created_at', 'updated_at')
|
828
|
+
h['created_at'].class.should == Time
|
829
|
+
h['updated_at'].class.should == String
|
830
|
+
|
831
|
+
h = @hash.all
|
832
|
+
h['created_at'].class.should == Time
|
833
|
+
h['updated_at'].class.should == String
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
837
|
+
before do
|
838
|
+
@hash = Redis::HashKey.new('test_hash')
|
839
|
+
@hash.clear
|
840
|
+
end
|
841
|
+
|
842
|
+
it "should handle complex marshaled values" do
|
843
|
+
@hash.options[:marshal] = true
|
844
|
+
@hash['abc'].should == nil
|
845
|
+
@hash['abc'] = {:json => 'hash marshal'}
|
846
|
+
@hash['abc'].should == {:json => 'hash marshal'}
|
847
|
+
|
848
|
+
# no marshaling
|
849
|
+
@hash.options[:marshal] = false
|
850
|
+
v = {:json => 'data'}
|
851
|
+
@hash['abc'] = v
|
852
|
+
@hash['abc'].should == v.to_s
|
853
|
+
|
854
|
+
@hash.options[:marshal] = true
|
855
|
+
@hash['abc'] = [[1,2], {:t3 => 4}]
|
856
|
+
@hash['abc'].should == [[1,2], {:t3 => 4}]
|
857
|
+
@hash.fetch('abc').should == [[1,2], {:t3 => 4}]
|
858
|
+
@hash.delete('abc').should == 1
|
859
|
+
@hash.fetch('abc').should.be.nil
|
860
|
+
|
861
|
+
@hash.options[:marshal] = true
|
862
|
+
@hash.bulk_set('abc' => [[1,2], {:t3 => 4}], 'def' => [[6,8], {:t4 => 8}])
|
863
|
+
hsh = @hash.bulk_get('abc', 'def', 'foo')
|
864
|
+
hsh['abc'].should == [[1,2], {:t3 => 4}]
|
865
|
+
hsh['def'].should == [[6,8], {:t4 => 8}]
|
866
|
+
hsh['foo'].should.be.nil
|
867
|
+
|
868
|
+
hsh = @hash.all
|
869
|
+
hsh['abc'].should == [[1,2], {:t3 => 4}]
|
870
|
+
hsh['def'].should == [[6,8], {:t4 => 8}]
|
871
|
+
|
872
|
+
@hash.values.sort.should == [[[1,2], {:t3 => 4}], [[6,8], {:t4 => 8}]].sort
|
873
|
+
|
874
|
+
@hash.delete('def').should == 1
|
875
|
+
@hash.delete('abc').should == 1
|
876
|
+
|
877
|
+
@hash.options[:marshal] = false
|
878
|
+
end
|
879
|
+
|
880
|
+
it "should marshal nil correctly" do
|
881
|
+
@hash.options[:marshal] = true
|
882
|
+
|
883
|
+
@hash['test'].should.be.nil
|
884
|
+
@hash['test'] = nil
|
885
|
+
@hash['test'].should.be.nil
|
886
|
+
@hash.delete('test').should == 1
|
887
|
+
@hash['test'].should.be.nil
|
888
|
+
|
889
|
+
@hash.options[:marshal] = false
|
890
|
+
end
|
891
|
+
|
892
|
+
it "should get and set values" do
|
893
|
+
@hash['foo'] = 'bar'
|
894
|
+
@hash['foo'].should == 'bar'
|
895
|
+
end
|
896
|
+
|
897
|
+
it "should know what exists" do
|
898
|
+
@hash['foo'] = 'bar'
|
899
|
+
@hash.include?('foo').should == true
|
900
|
+
end
|
901
|
+
|
902
|
+
it "should delete values" do
|
903
|
+
@hash['abc'] = 'xyz'
|
904
|
+
@hash.delete('abc')
|
905
|
+
@hash['abc'].should == nil
|
906
|
+
end
|
907
|
+
|
908
|
+
it "should respond to each" do
|
909
|
+
@hash['foo'] = 'bar'
|
910
|
+
@hash.each do |key, val|
|
911
|
+
key.should == 'foo'
|
912
|
+
val.should == 'bar'
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
it "should have 1 item" do
|
917
|
+
@hash['foo'] = 'bar'
|
918
|
+
@hash.size.should == 1
|
919
|
+
end
|
920
|
+
|
921
|
+
it "should respond to each_key" do
|
922
|
+
@hash['foo'] = 'bar'
|
923
|
+
@hash.each_key do |key|
|
924
|
+
key.should == 'foo'
|
925
|
+
end
|
926
|
+
end
|
927
|
+
|
928
|
+
it "should handle increment/decrement" do
|
929
|
+
@hash['integer'] = 1
|
930
|
+
@hash.incrby('integer')
|
931
|
+
@hash.incrby('integer', 2)
|
932
|
+
@hash.get('integer').to_i.should == 4
|
933
|
+
|
934
|
+
@hash['integer'] = 9
|
935
|
+
@hash.decrby('integer')
|
936
|
+
@hash.decrby('integer', 6)
|
937
|
+
@hash.get('integer').to_i.should == 2
|
938
|
+
|
939
|
+
@hash['float'] = 12.34
|
940
|
+
@hash.decrbyfloat('float')
|
941
|
+
@hash.decrbyfloat('float', 6.3)
|
942
|
+
@hash.get('float').to_f.should == 5.04
|
943
|
+
|
944
|
+
@hash['float'] = '5.0e3'
|
945
|
+
@hash.incrbyfloat('float')
|
946
|
+
@hash.incrbyfloat('float', '1.23e3')
|
947
|
+
@hash.incrbyfloat('float', 45.3).should == 6276.3
|
948
|
+
@hash.get('float').to_f.should == 6276.3
|
949
|
+
end
|
950
|
+
|
951
|
+
it "should respond to each_value" do
|
952
|
+
@hash['foo'] = 'bar'
|
953
|
+
@hash.each_value do |val|
|
954
|
+
val.should == 'bar'
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
it "should respond to empty?" do
|
959
|
+
@empty = Redis::HashKey.new('test_empty_hash')
|
960
|
+
@empty.respond_to?(:empty?).should == true
|
961
|
+
end
|
962
|
+
|
963
|
+
it "should be empty after a clear" do
|
964
|
+
@hash['foo'] = 'bar'
|
965
|
+
@hash.all.should == {'foo' => 'bar'}
|
966
|
+
@hash.clear
|
967
|
+
@hash.should.be.empty
|
968
|
+
end
|
969
|
+
|
970
|
+
it "should respond to bulk_set" do
|
971
|
+
@hash.bulk_set({'abc' => 'xyz', 'bizz' => 'bazz'})
|
972
|
+
@hash['abc'].should == 'xyz'
|
973
|
+
@hash['bizz'].should == 'bazz'
|
974
|
+
|
975
|
+
@hash.bulk_set('abc' => '123', 'bang' => 'michael')
|
976
|
+
@hash['abc'].should == '123'
|
977
|
+
@hash['bang'].should == 'michael'
|
978
|
+
|
979
|
+
@hash.bulk_set(:sym1 => 'val1', :sym2 => 'val2')
|
980
|
+
@hash['sym1'].should == 'val1'
|
981
|
+
@hash['sym2'].should == 'val2'
|
982
|
+
end
|
983
|
+
|
984
|
+
it "should respond to bulk_get" do
|
985
|
+
@hash['foo'] = 'bar'
|
986
|
+
hsh = @hash.bulk_get('abc','foo')
|
987
|
+
hsh['abc'].should == nil
|
988
|
+
hsh['foo'].should == 'bar'
|
989
|
+
end
|
990
|
+
|
991
|
+
it "should return multiple items via bulk_values" do
|
992
|
+
@hash['taco'] = 42
|
993
|
+
@hash['burrito'] = 99
|
994
|
+
res = @hash.bulk_values('taco', 'burrito')
|
995
|
+
res.should == ['42', '99'] # hashes don't convert
|
996
|
+
end
|
997
|
+
|
998
|
+
it "should increment field" do
|
999
|
+
@hash.incr('counter')
|
1000
|
+
@hash.incr('counter')
|
1001
|
+
@hash['counter'].to_i.should == 2
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
it "should respond to fill" do
|
1005
|
+
@hash['foo'] = 'bar'
|
1006
|
+
|
1007
|
+
@hash.fill('abc' => '123', 'bang' => 'michael')
|
1008
|
+
@hash['foo'].should == 'bar'
|
1009
|
+
@hash['abc'].should == '123'
|
1010
|
+
@hash['bang'].should == 'michael'
|
1011
|
+
@hash.keys.sort.should == ['abc', 'bang', 'foo']
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
it "raises an error if a non-Hash is passed to fill" do
|
1015
|
+
lambda { @hash.fill([]) }.should.raise(ArgumentError)
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
it "should fetch default values" do
|
1019
|
+
@hash['abc'] = "123"
|
1020
|
+
|
1021
|
+
value = @hash.fetch('missing_key','default_value')
|
1022
|
+
block = @hash.fetch("missing_key") {|key| "oops: #{key}" }
|
1023
|
+
no_error = @hash.fetch("abc") rescue "error"
|
1024
|
+
|
1025
|
+
no_error.should == "123"
|
1026
|
+
value.should == "default_value"
|
1027
|
+
block.should == "oops: missing_key"
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
it "should respond to #value" do
|
1031
|
+
@hash['abc'] = "123"
|
1032
|
+
@hash.value.should == @hash.all
|
1033
|
+
@hash.value.should == { "abc" => "123" }
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
it "should respond to #to_json" do
|
1037
|
+
@hash['abc'] = "123"
|
1038
|
+
JSON.parse(@hash.to_json)['value'].should == { "abc" => "123" }
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
it "should respond to #as_json" do
|
1042
|
+
@hash['abc'] = "123"
|
1043
|
+
@hash.as_json['value'].should == { "abc" => "123" }
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
it "should return empty object with bulk_get and bulk_value" do
|
1047
|
+
h = @hash.bulk_get
|
1048
|
+
h.should == {}
|
1049
|
+
|
1050
|
+
v = @hash.bulk_values
|
1051
|
+
v.should == []
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
describe 'with expiration' do
|
1055
|
+
{
|
1056
|
+
:incrby => 'somekey',
|
1057
|
+
:incr => 'somekey',
|
1058
|
+
:incrbyfloat => 'somekey',
|
1059
|
+
:decrby => 'somekey',
|
1060
|
+
:decr => 'somekey',
|
1061
|
+
:decrbyfloat => 'somekey',
|
1062
|
+
:store => ['somekey', 'somevalue'],
|
1063
|
+
:[]= => ['somekey', 'somevalue'],
|
1064
|
+
:bulk_set => [{ 'somekey' => 'somevalue' }],
|
1065
|
+
:update => [{ 'somekey' => 'somevalue' }],
|
1066
|
+
:fill => [{ 'somekey' => 'somevalue' }]
|
1067
|
+
}.each do |meth, args|
|
1068
|
+
it "#{meth} expiration: option" do
|
1069
|
+
@hash = Redis::HashKey.new('spec/hash_expiration', :expiration => 10)
|
1070
|
+
@hash.clear
|
1071
|
+
@hash.send(meth, *args)
|
1072
|
+
@hash.ttl.should > 0
|
1073
|
+
@hash.ttl.should <= 10
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
it "#{meth} expireat: option" do
|
1077
|
+
@hash = Redis::HashKey.new('spec/hash_expireat', :expireat => Time.now + 10.seconds)
|
1078
|
+
@hash.clear
|
1079
|
+
@hash.send(meth, *args)
|
1080
|
+
@hash.ttl.should > 0
|
1081
|
+
@hash.ttl.should <= 10
|
1082
|
+
end
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
after do
|
1087
|
+
@hash.clear
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
describe Redis::Set do
|
1092
|
+
before do
|
1093
|
+
@set = Redis::Set.new('spec/set')
|
1094
|
+
@set_1 = Redis::Set.new('spec/set_1')
|
1095
|
+
@set_2 = Redis::Set.new('spec/set_2')
|
1096
|
+
@set_3 = Redis::Set.new('spec/set_3')
|
1097
|
+
@set.clear
|
1098
|
+
@set_1.clear
|
1099
|
+
@set_2.clear
|
1100
|
+
@set_3.clear
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
it "should handle sets of simple values" do
|
1104
|
+
@set.should.be.empty
|
1105
|
+
@set << 'a'
|
1106
|
+
@set.randmember.should == 'a'
|
1107
|
+
@set << 'a' << 'a'
|
1108
|
+
@set.should == ['a']
|
1109
|
+
@set.to_s.should == 'a'
|
1110
|
+
@set.get.should == ['a']
|
1111
|
+
@set << 'b' << 'b'
|
1112
|
+
@set.sort.should == ['a','b']
|
1113
|
+
@set.members.sort.should == ['a','b']
|
1114
|
+
@set.members.sort.reverse.should == ['b','a'] # common question
|
1115
|
+
@set.get.sort.should == ['a','b']
|
1116
|
+
@set << 'c'
|
1117
|
+
@set.sort.should == ['a','b','c']
|
1118
|
+
@set.get.sort.should == ['a','b','c']
|
1119
|
+
@set.delete('c')
|
1120
|
+
@set.sort.should == ['a','b']
|
1121
|
+
@set.get.sort.should == ['a','b']
|
1122
|
+
@set.length.should == 2
|
1123
|
+
@set.size.should == 2
|
1124
|
+
@set.delete('a')
|
1125
|
+
@set.pop.should == 'b'
|
1126
|
+
|
1127
|
+
@set.add('a')
|
1128
|
+
@set.add('b')
|
1129
|
+
|
1130
|
+
i = 0
|
1131
|
+
@set.each do |st|
|
1132
|
+
i += 1
|
1133
|
+
end
|
1134
|
+
i.should == @set.length
|
1135
|
+
|
1136
|
+
coll = @set.sort.collect{|st| st}
|
1137
|
+
coll.should == ['a','b']
|
1138
|
+
@set.sort.should == ['a','b']
|
1139
|
+
@set.get.sort.should == ['a','b']
|
1140
|
+
|
1141
|
+
@set << 'c'
|
1142
|
+
@set.member?('c').should.be.true
|
1143
|
+
@set.include?('c').should.be.true
|
1144
|
+
@set.member?('no').should.be.false
|
1145
|
+
coll = @set.select{|st| st == 'c'}
|
1146
|
+
coll.should == ['c']
|
1147
|
+
@set.sort.should == ['a','b','c']
|
1148
|
+
@set.randmember.should.not == 'd'
|
1149
|
+
@set.delete_if{|m| m == 'c'}
|
1150
|
+
@set.sort.should == ['a','b']
|
1151
|
+
|
1152
|
+
@set << nil
|
1153
|
+
@set.include?("").should.be.true
|
1154
|
+
@set.sort.should == ['','a','b']
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
it "should handle empty array adds" do
|
1158
|
+
should.not.raise(Redis::CommandError) { @set.add([]) }
|
1159
|
+
@set.should.be.empty
|
1160
|
+
|
1161
|
+
should.not.raise(Redis::CommandError) { @set << [] }
|
1162
|
+
@set.should.be.empty
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it "should handle set intersections, unions, and diffs" do
|
1166
|
+
@set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
|
1167
|
+
@set_2 << 'c' << 'd' << 'e' << 'f' << 'g'
|
1168
|
+
@set_3 << 'a' << 'd' << 'g' << 'l' << 'm'
|
1169
|
+
@set_1.sort.should == %w(a b c d e)
|
1170
|
+
@set_2.sort.should == %w(c d e f g)
|
1171
|
+
@set_3.sort.should == %w(a d g l m)
|
1172
|
+
(@set_1 & @set_2).sort.should == ['c','d','e']
|
1173
|
+
@set_1.intersection(@set_2).sort.should == ['c','d','e']
|
1174
|
+
@set_1.intersection(@set_2, @set_3).sort.should == ['d']
|
1175
|
+
@set_1.intersect(@set_2).sort.should == ['c','d','e']
|
1176
|
+
@set_1.inter(@set_2, @set_3).sort.should == ['d']
|
1177
|
+
@set_1.interstore(INTERSTORE_KEY, @set_2).should == 3
|
1178
|
+
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['c','d','e']
|
1179
|
+
@set_1.interstore(INTERSTORE_KEY, @set_2, @set_3).should == 1
|
1180
|
+
@set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['d']
|
1181
|
+
|
1182
|
+
(@set_1 | @set_2).sort.should == ['a','b','c','d','e','f','g']
|
1183
|
+
(@set_1 + @set_2).sort.should == ['a','b','c','d','e','f','g']
|
1184
|
+
@set_1.union(@set_2).sort.should == ['a','b','c','d','e','f','g']
|
1185
|
+
@set_1.union(@set_2, @set_3).sort.should == ['a','b','c','d','e','f','g','l','m']
|
1186
|
+
@set_1.unionstore(UNIONSTORE_KEY, @set_2).should == 7
|
1187
|
+
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g']
|
1188
|
+
@set_1.unionstore(UNIONSTORE_KEY, @set_2, @set_3).should == 9
|
1189
|
+
@set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
|
1190
|
+
|
1191
|
+
(@set_1 ^ @set_2).sort.should == ["a", "b"]
|
1192
|
+
(@set_1 - @set_2).sort.should == ["a", "b"]
|
1193
|
+
(@set_2 - @set_1).sort.should == ["f", "g"]
|
1194
|
+
@set_1.difference(@set_2).sort.should == ["a", "b"]
|
1195
|
+
@set_1.diff(@set_2).sort.should == ["a", "b"]
|
1196
|
+
@set_1.difference(@set_2, @set_3).sort.should == ['b']
|
1197
|
+
@set_1.diffstore(DIFFSTORE_KEY, @set_2).should == 2
|
1198
|
+
@set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['a','b']
|
1199
|
+
@set_1.diffstore(DIFFSTORE_KEY, @set_2, @set_3).should == 1
|
1200
|
+
@set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['b']
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
it "should support renaming sets" do
|
1204
|
+
@set.should.be.empty
|
1205
|
+
@set << 'a' << 'b' << 'a' << 3
|
1206
|
+
@set.sort.should == ['3','a','b']
|
1207
|
+
@set.key.should == 'spec/set'
|
1208
|
+
@set.rename('spec/set2') # can't test result; switched from true to "OK"
|
1209
|
+
@set.key.should == 'spec/set2'
|
1210
|
+
old = Redis::Set.new('spec/set')
|
1211
|
+
old.should.be.empty
|
1212
|
+
old << 'Tuff'
|
1213
|
+
@set.renamenx('spec/set').should.be.false
|
1214
|
+
@set.renamenx(old).should.be.false
|
1215
|
+
@set.renamenx('spec/foo').should.be.true
|
1216
|
+
@set.clear
|
1217
|
+
@set.redis.del('spec/set2')
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
it "should handle variadic sadd operations" do
|
1221
|
+
@set.should.be.empty
|
1222
|
+
@set << 'a'
|
1223
|
+
@set.merge('b', 'c')
|
1224
|
+
@set.members.sort.should == ['a', 'b', 'c']
|
1225
|
+
@set.merge(['d','c','e'])
|
1226
|
+
@set.members.sort.should == ['a', 'b', 'c', 'd', 'e']
|
1227
|
+
end
|
1228
|
+
|
1229
|
+
it "should support sorting" do
|
1230
|
+
@set_1 << 'c' << 'b' << 'a' << 'e' << 'd'
|
1231
|
+
@set_1.sort.should == %w(a b c d e)
|
1232
|
+
@set_1.sort(SORT_ORDER).should == %w(e d c b a)
|
1233
|
+
|
1234
|
+
@set_2 << 2 << 4 << 3 << 1 << 5
|
1235
|
+
@set_2.sort.should == %w(1 2 3 4 5)
|
1236
|
+
@set_2.sort(SORT_LIMIT).should == %w(3 4)
|
1237
|
+
|
1238
|
+
@set_3 << 'm_4' << 'm_5' << 'm_1' << 'm_3' << 'm_2'
|
1239
|
+
### incorrect interpretation of what the :by parameter means
|
1240
|
+
### :by will look up values of keys so it would try to find a value in
|
1241
|
+
### redis of "m_m_1" which doesn't exist at this point, it is not a way to
|
1242
|
+
### alter the value to sort by but rather use a different value for this value
|
1243
|
+
### in the set (Kris Fox)
|
1244
|
+
# @set_3.sort(:by => 'm_*').should == %w(m_1 m_2 m_3 m_4 m_5)
|
1245
|
+
# below passes just fine
|
1246
|
+
@set_3.sort.should == %w(m_1 m_2 m_3 m_4 m_5)
|
1247
|
+
|
1248
|
+
val1 = Redis::Value.new('spec/3/sorted')
|
1249
|
+
val2 = Redis::Value.new('spec/4/sorted')
|
1250
|
+
|
1251
|
+
val1.set('val3')
|
1252
|
+
val2.set('val4')
|
1253
|
+
|
1254
|
+
@set_2.sort(SORT_GET).should == ['val3', 'val4']
|
1255
|
+
@set_2.sort(SORT_STORE).should == 2
|
1256
|
+
@set_2.redis.type(SORT_STORE[:store]).should == 'list'
|
1257
|
+
@set_2.redis.lrange(SORT_STORE[:store], 0, -1).should == ['val3', 'val4']
|
1258
|
+
|
1259
|
+
@set_1.redis.del val1.key
|
1260
|
+
@set_1.redis.del val2.key
|
1261
|
+
@set_1.redis.del SORT_STORE[:store]
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
it "should respond to #value" do
|
1265
|
+
@set_1 << 'a'
|
1266
|
+
@set_1.value.should == @set_1.members
|
1267
|
+
@set_1.value.should == ['a']
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
it "should support moving between sets" do
|
1271
|
+
@set_1 << 'X' << 'Y' << 'Z'
|
1272
|
+
@set_1.move('X', @set_2)
|
1273
|
+
@set_2.should == ['X']
|
1274
|
+
end
|
1275
|
+
|
1276
|
+
it "should respond to #to_json" do
|
1277
|
+
@set_1 << 'a'
|
1278
|
+
JSON.parse(@set_1.to_json)['value'].should == ['a']
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
it "should respond to #as_json" do
|
1282
|
+
@set_1 << 'a'
|
1283
|
+
@set_1.as_json['value'].should == ['a']
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
describe "with expiration" do
|
1287
|
+
[:<<, :add, :merge].each do |meth|
|
1288
|
+
it "should set time to live in seconds when expiration option assigned" do
|
1289
|
+
@set = Redis::Set.new('spec/set', :expiration => 10)
|
1290
|
+
@set.send(meth, 'val')
|
1291
|
+
@set.ttl.should > 0
|
1292
|
+
@set.ttl.should <= 10
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
it "should set expiration when expireat option assigned" do
|
1296
|
+
@set = Redis::Set.new('spec/set', :expireat => Time.now + 10.seconds)
|
1297
|
+
@set.send(meth, 'val')
|
1298
|
+
@set.ttl.should > 0
|
1299
|
+
@set.ttl.should <= 10
|
1300
|
+
end
|
1301
|
+
end
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
after do
|
1305
|
+
@set.clear
|
1306
|
+
@set_1.clear
|
1307
|
+
@set_2.clear
|
1308
|
+
@set_3.clear
|
1309
|
+
end
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
describe Redis::SortedSet do
|
1313
|
+
before do
|
1314
|
+
@set = Redis::SortedSet.new('spec/zset')
|
1315
|
+
@set_1 = Redis::SortedSet.new('spec/zset_1')
|
1316
|
+
@set_2 = Redis::SortedSet.new('spec/zset_2')
|
1317
|
+
@set_3 = Redis::SortedSet.new('spec/zset_3')
|
1318
|
+
@set_4 = Redis::SortedSet.new('spec/zset_3', :marshal => true)
|
1319
|
+
@set_5 = Redis::Set.new('spec/zset_4')
|
1320
|
+
@set.clear
|
1321
|
+
@set_1.clear
|
1322
|
+
@set_2.clear
|
1323
|
+
@set_3.clear
|
1324
|
+
@set_4.clear
|
1325
|
+
@set_5.clear
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
it "should handle sorted sets of simple values" do
|
1329
|
+
@set.should.be.empty
|
1330
|
+
@set['a'] = 11
|
1331
|
+
@set['a'] = 21
|
1332
|
+
@set.add('a', 5)
|
1333
|
+
@set.score('a').should == 5
|
1334
|
+
@set['a'].should == 5
|
1335
|
+
@set['a'] = 3
|
1336
|
+
@set['b'] = 5.6
|
1337
|
+
@set['b'].should == 5.6
|
1338
|
+
@set['c'] = 4
|
1339
|
+
|
1340
|
+
a = @set.members
|
1341
|
+
@set[0,-1].should == a[0,-1]
|
1342
|
+
@set[0..2].should == a[0..2]
|
1343
|
+
@set[0...2].should == a[0...2]
|
1344
|
+
@set.slice(0..2).should == a.slice(0..2)
|
1345
|
+
@set[0, 2].should == a[0,2]
|
1346
|
+
@set.slice(0, 2).should == a.slice(0, 2)
|
1347
|
+
@set.range(0, 2).should == a[0..2]
|
1348
|
+
@set[0, 0].should == []
|
1349
|
+
@set.range(0,1,:withscores => true).should == [['a',3],['c',4]]
|
1350
|
+
@set.revrange(0,1,:withscores => true).should == [['b',5.6],['c',4]]
|
1351
|
+
@set.range(0,-1).should == a[0..-1]
|
1352
|
+
@set.revrange(0,-1).should == a[0..-1].reverse
|
1353
|
+
@set[0..1].should == a[0..1]
|
1354
|
+
@set[1].should == 0 # missing
|
1355
|
+
@set.at(1).should == 'c'
|
1356
|
+
@set.first.should == 'a'
|
1357
|
+
@set.last.should == 'b'
|
1358
|
+
|
1359
|
+
@set.members.should == ['a','c','b']
|
1360
|
+
@set.members.reverse.should == ['b','c','a']
|
1361
|
+
@set.members(:withscores => true).should == [['a',3],['c',4],['b',5.6]]
|
1362
|
+
@set.members(:with_scores => true).should == [['a',3],['c',4],['b',5.6]]
|
1363
|
+
@set.members(:withscores => true).reverse.should == [['b',5.6],['c',4],['a',3]]
|
1364
|
+
@set.members(:withscores => true).should == @set.range(0,-1,:withscores => true)
|
1365
|
+
@set.members(:withscores => true).reverse.should == @set.revrange(0,-1,:withscores => true)
|
1366
|
+
|
1367
|
+
@set['b'] = 5
|
1368
|
+
@set['b'] = 6
|
1369
|
+
@set.score('b').should == 6
|
1370
|
+
@set.score('f').should == nil
|
1371
|
+
@set.delete('c')
|
1372
|
+
@set.to_s.should == 'a, b'
|
1373
|
+
@set.should == ['a','b']
|
1374
|
+
@set.members.should == ['a','b']
|
1375
|
+
@set.member?('a').should == true
|
1376
|
+
@set.member?('b').should == true
|
1377
|
+
@set.member?('c').should == false
|
1378
|
+
@set['d'] = 0
|
1379
|
+
|
1380
|
+
@set.rangebyscore(0, 4).should == ['d','a']
|
1381
|
+
@set.rangebyscore(0, 4, :count => 1).should == ['d']
|
1382
|
+
@set.rangebyscore(0, 4, :count => 2).should == ['d','a']
|
1383
|
+
@set.rangebyscore(0, 4, :limit => 2).should == ['d','a']
|
1384
|
+
|
1385
|
+
@set.revrangebyscore(4, 0, :withscores => true).should == [['a', 3], ['d', 0]]
|
1386
|
+
@set.revrangebyscore(4, 0).should == ['a', 'd']
|
1387
|
+
@set.revrangebyscore(4, 0, :count => 2).should == ['a','d']
|
1388
|
+
@set.rank('b').should == 2
|
1389
|
+
@set.revrank('b').should == 0
|
1390
|
+
|
1391
|
+
# shouldn't report a rank for a key that doesn't exist
|
1392
|
+
@set.rank('foo').should.not == @set.rank(@set.first)
|
1393
|
+
@set.rank('foo').should == nil
|
1394
|
+
|
1395
|
+
# shouldn't report a rank for a key that doesn't exist
|
1396
|
+
@set.revrank('foo').should.not == @set.revrank(@set.first)
|
1397
|
+
@set.revrank('foo').should == nil
|
1398
|
+
|
1399
|
+
@set['f'] = 100
|
1400
|
+
@set['g'] = 110
|
1401
|
+
@set['h'] = 120
|
1402
|
+
@set['j'] = 130
|
1403
|
+
@set.incr('h', 20)
|
1404
|
+
@set.remrangebyscore(100, 120)
|
1405
|
+
@set.members.should == ['d','a','b','j','h']
|
1406
|
+
|
1407
|
+
# Redis 1.3.5
|
1408
|
+
# @set['h'] = 12
|
1409
|
+
# @set['j'] = 13
|
1410
|
+
# @set.remrangebyrank(4,-1)
|
1411
|
+
# @set.members.should == ['d','a','b']
|
1412
|
+
|
1413
|
+
@set.delete('d')
|
1414
|
+
@set['c'] = 200
|
1415
|
+
@set.members.should == ['a','b','j','h','c']
|
1416
|
+
@set.delete('c')
|
1417
|
+
@set.length.should == 4
|
1418
|
+
@set.size.should == 4
|
1419
|
+
@set.count.should == 4
|
1420
|
+
|
1421
|
+
@set.range_size(100, 120).should == 0
|
1422
|
+
@set.range_size(0, 100).should == 2
|
1423
|
+
@set.range_size('-inf', 'inf').should == 4
|
1424
|
+
|
1425
|
+
@set.delete_if{|m| m == 'b'}
|
1426
|
+
@set.size.should == 3
|
1427
|
+
|
1428
|
+
# this is destructive so must come last
|
1429
|
+
res = @set.remrangebyrank(0, 2)
|
1430
|
+
res.should == 3
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
it "should handle inserting multiple values at once" do
|
1434
|
+
@set.merge({ 'a' => 1, 'b' => 2 })
|
1435
|
+
@set.merge([['a', 4], ['c', 5]])
|
1436
|
+
@set.merge({d: 0, e: 9 })
|
1437
|
+
|
1438
|
+
@set.members.should == ["d", "b", "a", "c", "e"]
|
1439
|
+
|
1440
|
+
@set[:f] = 3
|
1441
|
+
@set.members.should == ["d", "b", "f", "a", "c", "e"]
|
1442
|
+
end
|
1443
|
+
|
1444
|
+
it "should support marshaling key names" do
|
1445
|
+
@set_4.members.should == []
|
1446
|
+
|
1447
|
+
@set_4[Object] = 1.20
|
1448
|
+
@set_4[Module] = 2.30
|
1449
|
+
@set_4[nil] = 3.40
|
1450
|
+
|
1451
|
+
@set_4.incr(Object, 0.5)
|
1452
|
+
@set_4.decr(Module, 0.5)
|
1453
|
+
@set_4.incr(nil, 0.5)
|
1454
|
+
|
1455
|
+
@set_4[Object].round(1).should == 1.7
|
1456
|
+
@set_4[Module].round(1).should == 1.8
|
1457
|
+
@set_4[nil].round(1).should == 3.9
|
1458
|
+
|
1459
|
+
@set_4.members.should == [Object, Module, nil]
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
it "should support renaming sorted sets" do
|
1463
|
+
@set.should.be.empty
|
1464
|
+
@set['zynga'] = 151
|
1465
|
+
@set['playfish'] = 202
|
1466
|
+
@set.members.should == ['zynga','playfish']
|
1467
|
+
@set.key.should == 'spec/zset'
|
1468
|
+
@set.rename('spec/zset2') # can't test result; switched from true to "OK"
|
1469
|
+
@set.key.should == 'spec/zset2'
|
1470
|
+
old = Redis::SortedSet.new('spec/zset')
|
1471
|
+
old.should.be.empty
|
1472
|
+
old['tuff'] = 54
|
1473
|
+
@set.renamenx('spec/zset').should.be.false
|
1474
|
+
@set.renamenx(old).should.be.false
|
1475
|
+
@set.renamenx('spec/zfoo').should.be.true
|
1476
|
+
@set.clear
|
1477
|
+
@set.redis.del('spec/zset2')
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
it "should handle unions" do
|
1481
|
+
@set_1.add('a', 1)
|
1482
|
+
@set_1.add('b', 4)
|
1483
|
+
@set_1.add('c', 3)
|
1484
|
+
|
1485
|
+
@set_2.add('b', 2)
|
1486
|
+
@set_2.add('c', 1)
|
1487
|
+
@set_2.add('d', 0)
|
1488
|
+
|
1489
|
+
@set_1.union(@set_2).should == ['d', 'a', 'c', 'b']
|
1490
|
+
|
1491
|
+
@set_1.unionstore(@set.key, @set_2)
|
1492
|
+
# @set is now: [[d, 0], [a, 1], [c, 4], [b, 6]]
|
1493
|
+
@set.members.should == ['d', 'a', 'c', 'b']
|
1494
|
+
|
1495
|
+
@set_2.unionstore(@set, @set_1, :aggregate => :sum)
|
1496
|
+
# @set is now: [[d, 0], [a, 1], [c, 4], [b, 6]]
|
1497
|
+
@set.members.should == ['d', 'a', 'c', 'b']
|
1498
|
+
|
1499
|
+
@set_1.unionstore(@set, @set_2, :aggregate => :min)
|
1500
|
+
# @set is now: [[d, 0], [a, 1], [c, 1], [b, 2]]
|
1501
|
+
@set.members.should == ['d', 'a', 'c', 'b']
|
1502
|
+
@set['b'].should == 2
|
1503
|
+
|
1504
|
+
@set_1.unionstore(@set, @set_2, :aggregate => :max)
|
1505
|
+
# @set is now: [[d, 0], [a, 1], [c, 3], [b, 4]]
|
1506
|
+
@set.members.should == ['d', 'a', 'c', 'b']
|
1507
|
+
@set['b'].should == 4
|
1508
|
+
|
1509
|
+
@set_1.unionstore(@set, @set_2, :aggregate => :sum, :weights => [0, 1])
|
1510
|
+
# @set is now: [[a, 0], [d, 0], [c, 1], [b, 2]]
|
1511
|
+
@set.members.should == ['a', 'd', 'c', 'b']
|
1512
|
+
@set['b'].should == 2
|
1513
|
+
|
1514
|
+
@set_5 << ['a', 'e', 'f']
|
1515
|
+
@set_1.unionstore(@set, @set_5, :aggregate => :sum)
|
1516
|
+
# #set is now: [[e, 1], [f, 1], [a, 1], [c, 3], [b, 4]]
|
1517
|
+
@set.members.should == ['e', 'f', 'a', 'c', 'b']
|
1518
|
+
@set['e'].should == 1
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
it "should handle intersections" do
|
1522
|
+
@set_1.add('a', 1)
|
1523
|
+
@set_1.add('b', 4)
|
1524
|
+
@set_1.add('c', 3)
|
1525
|
+
|
1526
|
+
@set_2.add('b', 2)
|
1527
|
+
@set_2.add('c', 1)
|
1528
|
+
@set_2.add('d', 0)
|
1529
|
+
|
1530
|
+
@set_1.intersection(@set_2).should == ['c', 'b']
|
1531
|
+
|
1532
|
+
@set_1.interstore(@set.key, @set_2)
|
1533
|
+
# @set is now: [[c, 4], [b, 6]]
|
1534
|
+
@set.members.should == ['c', 'b']
|
1535
|
+
|
1536
|
+
@set_2.interstore(@set, @set_1, :aggregate => :sum)
|
1537
|
+
# @set is now: [[c, 4], [b, 6]]
|
1538
|
+
@set.members.should == ['c', 'b']
|
1539
|
+
|
1540
|
+
@set_1.interstore(@set, @set_2, :aggregate => :min)
|
1541
|
+
# @set is now: [[c, 1], [b, 2]]
|
1542
|
+
@set.members.should == ['c', 'b']
|
1543
|
+
@set['b'].should == 2
|
1544
|
+
|
1545
|
+
@set_5 << ['a', 'e', 'b']
|
1546
|
+
@set_1.interstore(@set, @set_5, :aggregate => :max)
|
1547
|
+
# @set is now: [[a, 1], [b, 4]]
|
1548
|
+
@set.members.should == ['a', 'b']
|
1549
|
+
@set['b'].should == 4
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
it 'should set time to live in seconds when expiration option assigned' do
|
1553
|
+
@set = Redis::SortedSet.new('spec/zset', :expiration => 10)
|
1554
|
+
@set['val'] = 1
|
1555
|
+
@set.ttl.should > 0
|
1556
|
+
@set.ttl.should <= 10
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
it 'should set expiration when expireat option assigned' do
|
1560
|
+
@set = Redis::SortedSet.new('spec/zset', :expireat => Time.now + 10.seconds)
|
1561
|
+
@set['val'] = 1
|
1562
|
+
@set.ttl.should > 0
|
1563
|
+
@set.ttl.should <= 10
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
it "should respond to #value" do
|
1567
|
+
@set['val'] = 1
|
1568
|
+
@set.value.should == @set.members
|
1569
|
+
@set.value.should == ['val']
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
it "should respond to #to_json" do
|
1573
|
+
@set['val'] = 1
|
1574
|
+
JSON.parse(@set.to_json)['value'].should == ['val']
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
it "should respond to #as_json" do
|
1578
|
+
@set['val'] = 1
|
1579
|
+
@set.as_json['value'].should == ['val']
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
describe "with expiration" do
|
1583
|
+
[:[]=, :add, :increment, :incr, :incrby, :decrement, :decr, :decrby].each do |meth|
|
1584
|
+
it "#{meth} expiration: option" do
|
1585
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expiration => 10)
|
1586
|
+
@set.clear
|
1587
|
+
@set.send(meth, 'somekey', 12)
|
1588
|
+
@set.ttl.should > 0
|
1589
|
+
@set.ttl.should <= 10
|
1590
|
+
end
|
1591
|
+
it "#{meth} expireat: option" do
|
1592
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
|
1593
|
+
@set.clear
|
1594
|
+
@set.send(meth, 'somekey', 12)
|
1595
|
+
@set.ttl.should > 0
|
1596
|
+
@set.ttl.should <= 10
|
1597
|
+
end
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
[:merge, :add_all].each do |meth|
|
1601
|
+
it "#{meth} expiration: option" do
|
1602
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expiration => 10)
|
1603
|
+
@set.clear
|
1604
|
+
@set.send(meth, 'somekey' => 12)
|
1605
|
+
@set.ttl.should > 0
|
1606
|
+
@set.ttl.should <= 10
|
1607
|
+
end
|
1608
|
+
it "#{meth} expireat: option" do
|
1609
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
|
1610
|
+
@set.clear
|
1611
|
+
@set.send(meth, 'somekey' => 12)
|
1612
|
+
@set.ttl.should > 0
|
1613
|
+
@set.ttl.should <= 10
|
1614
|
+
end
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
[:unionstore, :interstore].each do |meth|
|
1618
|
+
it "#{meth} expiration: option" do
|
1619
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expiration => 10)
|
1620
|
+
@set.clear
|
1621
|
+
@set.redis.zadd(@set.key, 1, "1")
|
1622
|
+
@set.send(meth, 'sets', Redis::SortedSet.new('other'))
|
1623
|
+
@set.ttl.should > 0
|
1624
|
+
@set.ttl.should <= 10
|
1625
|
+
end
|
1626
|
+
|
1627
|
+
it "#{meth} expireat: option" do
|
1628
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
|
1629
|
+
@set.clear
|
1630
|
+
@set.redis.zadd(@set.key, 1, "1")
|
1631
|
+
@set.send(meth, 'sets', Redis::SortedSet.new('other'))
|
1632
|
+
@set.ttl.should > 0
|
1633
|
+
@set.ttl.should <= 10
|
1634
|
+
end
|
1635
|
+
end
|
1636
|
+
|
1637
|
+
it "delete expiration: option" do
|
1638
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expiration => 10)
|
1639
|
+
@set.clear
|
1640
|
+
@set.redis.zadd(@set.key, 1, "1")
|
1641
|
+
@set.redis.zadd(@set.key, 2, "2")
|
1642
|
+
@set.delete("2")
|
1643
|
+
@set.ttl.should > 0
|
1644
|
+
@set.ttl.should <= 10
|
1645
|
+
end
|
1646
|
+
|
1647
|
+
it "delete expireat: option" do
|
1648
|
+
@set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
|
1649
|
+
@set.clear
|
1650
|
+
@set.redis.zadd(@set.key, 1, "1")
|
1651
|
+
@set.redis.zadd(@set.key, 2, "2")
|
1652
|
+
@set.delete("2")
|
1653
|
+
@set.ttl.should > 0
|
1654
|
+
@set.ttl.should <= 10
|
1655
|
+
end
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
after do
|
1659
|
+
@set.clear
|
1660
|
+
@set_1.clear
|
1661
|
+
@set_2.clear
|
1662
|
+
@set_3.clear
|
1663
|
+
@set_4.clear
|
1664
|
+
@set_5.clear
|
1665
|
+
end
|
1666
|
+
end
|