redis-objects 0.2.1 → 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.
@@ -3,8 +3,10 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
 
4
4
  require 'redis/counter'
5
5
  require 'redis/list'
6
- require 'redis/set'
7
6
  require 'redis/value'
7
+ require 'redis/lock'
8
+ require 'redis/set'
9
+ require 'redis/sorted_set'
8
10
 
9
11
  describe Redis::Value do
10
12
  before :all do
@@ -25,15 +27,37 @@ describe Redis::Value do
25
27
  end
26
28
 
27
29
  it "should handle complex marshaled values" do
30
+ @value.options[:marshal] = true
28
31
  @value.should == nil
29
32
  @value.value = {:json => 'data'}
30
33
  @value.should == {:json => 'data'}
31
- @value.get.should == {:json => 'data'}
34
+
35
+ # no marshaling
36
+ @value.options[:marshal] = false
37
+ v = {:json => 'data'}
38
+ @value.value = v
39
+ @value.should == v.to_s
40
+
41
+ @value.options[:marshal] = true
32
42
  @value.value = [[1,2], {:t3 => 4}]
33
43
  @value.should == [[1,2], {:t3 => 4}]
34
44
  @value.get.should == [[1,2], {:t3 => 4}]
35
45
  @value.del.should be_true
36
46
  @value.should be_nil
47
+ @value.options[:marshal] = false
48
+ end
49
+
50
+ it "should support renaming values" do
51
+ @value.value = 'Peter Pan'
52
+ @value.key.should == 'spec/value'
53
+ @value.rename('spec/value2').should be_true
54
+ @value.key.should == 'spec/value2'
55
+ @value.should == 'Peter Pan'
56
+ old = Redis::Value.new('spec/value')
57
+ old.should be_nil
58
+ old.value = 'Tuff'
59
+ @value.renamenx('spec/value').should be_false
60
+ @value.value.should == 'Peter Pan'
37
61
  end
38
62
 
39
63
  after :all do
@@ -91,6 +115,7 @@ describe Redis::List do
91
115
  @list << 'j'
92
116
  @list.should == ['a','c','f','j']
93
117
  @list[0..2].should == ['a','c','f']
118
+ @list[0, 2].should == ['a','c','f'] # consistent with Redis, not Ruby
94
119
  @list[1, 3].should == ['c','f','j']
95
120
  @list.length.should == 4
96
121
  @list.size.should == 4
@@ -123,13 +148,41 @@ describe Redis::List do
123
148
  end
124
149
 
125
150
  it "should handle lists of complex data types" do
126
- @list << {:json => 'data'}
127
- @list << {:json2 => 'data2'}
128
- @list.first.should == {:json => 'data'}
129
- @list.last.should == {:json2 => 'data2'}
151
+ @list.options[:marshal] = true
152
+ v1 = {:json => 'data'}
153
+ v2 = {:json2 => 'data2'}
154
+ @list << v1
155
+ @list << v2
156
+ @list.first.should == v1
157
+ @list.last.should == v2
130
158
  @list << [1,2,3,[4,5]]
131
159
  @list.last.should == [1,2,3,[4,5]]
132
160
  @list.shift.should == {:json => 'data'}
161
+ @list.options[:marshal] = false
162
+ end
163
+
164
+ it "should support renaming lists" do
165
+ @list.should be_empty
166
+ @list << 'a' << 'b' << 'a' << 3
167
+ @list.should == ['a','b','a','3']
168
+ @list.key.should == 'spec/list'
169
+ @list.rename('spec/list3', false).should be_true
170
+ @list.key.should == 'spec/list'
171
+ @list.redis.del('spec/list3')
172
+ @list << 'a' << 'b' << 'a' << 3
173
+ @list.rename('spec/list2').should be_true
174
+ @list.key.should == 'spec/list2'
175
+ @list.redis.lrange(@list.key, 0, 3).should == ['a','b','a','3']
176
+ old = Redis::List.new('spec/list')
177
+ old.should be_empty
178
+ old << 'Tuff'
179
+ old.values.should == ['Tuff']
180
+ @list.renamenx('spec/list').should be_false
181
+ @list.renamenx(old).should be_false
182
+ @list.renamenx('spec/foo').should be_true
183
+ old.values.should == ['Tuff']
184
+ @list.clear
185
+ @list.redis.del('spec/list2')
133
186
  end
134
187
 
135
188
  after :all do
@@ -137,6 +190,134 @@ describe Redis::List do
137
190
  end
138
191
  end
139
192
 
193
+ describe Redis::Counter do
194
+ before :all do
195
+ @counter = Redis::Counter.new('spec/counter')
196
+ @counter2 = Redis::Counter.new('spec/counter')
197
+ end
198
+
199
+ before :each do
200
+ @counter.reset
201
+ end
202
+
203
+ it "should support increment/decrement of counters" do
204
+ @counter.key.should == 'spec/counter'
205
+ @counter.incr(10)
206
+ @counter.should == 10
207
+
208
+ # math proxy ops
209
+ (@counter == 10).should be_true
210
+ (@counter <= 10).should be_true
211
+ (@counter < 11).should be_true
212
+ (@counter > 9).should be_true
213
+ (@counter >= 10).should be_true
214
+ "#{@counter}".should == "10"
215
+
216
+ @counter.increment.should == 11
217
+ @counter.increment.should == 12
218
+ @counter2.increment.should == 13
219
+ @counter2.increment(2).should == 15
220
+ @counter.decrement.should == 14
221
+ @counter2.decrement.should == 13
222
+ @counter.decrement.should == 12
223
+ @counter2.decrement(4).should == 8
224
+ @counter.should == 8
225
+ @counter.reset.should be_true
226
+ @counter.should == 0
227
+ @counter.reset(15).should be_true
228
+ @counter.should == 15
229
+ end
230
+
231
+ after :all do
232
+ @counter.delete
233
+ end
234
+ end
235
+
236
+ describe Redis::Lock do
237
+ before :each do
238
+ $redis.flushall
239
+ end
240
+
241
+ it "should set the value to the expiration" do
242
+ start = Time.now
243
+ expiry = 15
244
+ lock = Redis::Lock.new(:test_lock, :expiration => expiry)
245
+ lock.lock do
246
+ expiration = $redis.get("test_lock").to_f
247
+
248
+ # The expiration stored in redis should be 15 seconds from when we started
249
+ # or a little more
250
+ expiration.should be_close((start + expiry).to_f, 2.0)
251
+ end
252
+
253
+ # key should have been cleaned up
254
+ $redis.get("test_lock").should be_nil
255
+ end
256
+
257
+ it "should set value to 1 when no expiration is set" do
258
+ lock = Redis::Lock.new(:test_lock)
259
+ lock.lock do
260
+ $redis.get('test_lock').should == '1'
261
+ end
262
+
263
+ # key should have been cleaned up
264
+ $redis.get("test_lock").should be_nil
265
+ end
266
+
267
+ it "should let lock be gettable when lock is expired" do
268
+ expiry = 15
269
+ lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
270
+
271
+ # create a fake lock in the past
272
+ $redis.set("test_lock", Time.now-(expiry + 60))
273
+
274
+ gotit = false
275
+ lock.lock do
276
+ gotit = true
277
+ end
278
+
279
+ # should get the lock because it has expired
280
+ gotit.should be_true
281
+ $redis.get("test_lock").should be_nil
282
+ end
283
+
284
+ it "should not let non-expired locks be gettable" do
285
+ expiry = 15
286
+ lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
287
+
288
+ # create a fake lock
289
+ $redis.set("test_lock", (Time.now + expiry).to_f)
290
+
291
+ gotit = false
292
+ error = nil
293
+ begin
294
+ lock.lock do
295
+ gotit = true
296
+ end
297
+ rescue => error
298
+ end
299
+
300
+ error.should be_kind_of(Redis::Lock::LockTimeout)
301
+
302
+ # should not have the lock
303
+ gotit.should_not be_true
304
+
305
+ # lock value should still be set
306
+ $redis.get("test_lock").should_not be_nil
307
+ end
308
+
309
+ it "should not remove the key if lock is held past expiration" do
310
+ lock = Redis::Lock.new(:test_lock, :expiration => 0.0)
311
+
312
+ lock.lock do
313
+ sleep 1.1
314
+ end
315
+
316
+ # lock value should still be set since the lock was held for more than the expiry
317
+ $redis.get("test_lock").should_not be_nil
318
+ end
319
+ end
320
+
140
321
  describe Redis::Set do
141
322
  before :all do
142
323
  @set = Redis::Set.new('spec/set')
@@ -191,7 +372,7 @@ describe Redis::Set do
191
372
  @set.sort.should == ['a','b','c']
192
373
  end
193
374
 
194
- it "should handle set intersections and unions" do
375
+ it "should handle set intersections, unions, and diffs" do
195
376
  @set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
196
377
  @set_2 << 'c' << 'd' << 'e' << 'f' << 'g'
197
378
  @set_3 << 'a' << 'd' << 'g' << 'l' << 'm'
@@ -216,6 +397,34 @@ describe Redis::Set do
216
397
  @set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g']
217
398
  @set_1.unionstore(UNIONSTORE_KEY, @set_2, @set_3).should == 9
218
399
  @set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
400
+
401
+ (@set_1 ^ @set_2).sort.should == ["a", "b"]
402
+ (@set_1 - @set_2).sort.should == ["a", "b"]
403
+ (@set_2 - @set_1).sort.should == ["f", "g"]
404
+ @set_1.difference(@set_2).sort.should == ["a", "b"]
405
+ @set_1.diff(@set_2).sort.should == ["a", "b"]
406
+ @set_1.difference(@set_2, @set_3).sort.should == ['b']
407
+ @set_1.diffstore(DIFFSTORE_KEY, @set_2).should == 2
408
+ @set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['a','b']
409
+ @set_1.diffstore(DIFFSTORE_KEY, @set_2, @set_3).should == 1
410
+ @set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['b']
411
+ end
412
+
413
+ it "should support renaming sets" do
414
+ @set.should be_empty
415
+ @set << 'a' << 'b' << 'a' << 3
416
+ @set.sort.should == ['3','a','b']
417
+ @set.key.should == 'spec/set'
418
+ @set.rename('spec/set2').should be_true
419
+ @set.key.should == 'spec/set2'
420
+ old = Redis::Set.new('spec/set')
421
+ old.should be_empty
422
+ old << 'Tuff'
423
+ @set.renamenx('spec/set').should be_false
424
+ @set.renamenx(old).should be_false
425
+ @set.renamenx('spec/foo').should be_true
426
+ @set.clear
427
+ @set.redis.del('spec/set2')
219
428
  end
220
429
 
221
430
  after :all do
@@ -226,45 +435,162 @@ describe Redis::Set do
226
435
  end
227
436
  end
228
437
 
229
- describe Redis::Counter do
438
+ describe Redis::SortedSet do
230
439
  before :all do
231
- @counter = Redis::Counter.new('spec/counter')
232
- @counter2 = Redis::Counter.new('spec/counter')
440
+ @set = Redis::SortedSet.new('spec/zset')
441
+ @set_1 = Redis::SortedSet.new('spec/zset_1')
442
+ @set_2 = Redis::SortedSet.new('spec/zset_2')
443
+ @set_3 = Redis::SortedSet.new('spec/zset_3')
233
444
  end
234
445
 
235
446
  before :each do
236
- @counter.reset
447
+ @set.clear
448
+ @set_1.clear
449
+ @set_2.clear
450
+ @set_3.clear
237
451
  end
238
452
 
239
- it "should support increment/decrement of counters" do
240
- @counter.key.should == 'spec/counter'
241
- @counter.incr(10)
242
- @counter.should == 10
453
+ it "should handle sets of simple values" do
454
+ @set.should be_empty
455
+ @set['a'] = 11
456
+ @set['a'] = 21
457
+ @set.add('a', 5)
458
+ @set.score('a').should == 5
459
+ @set['a'].should == 5
460
+ @set['a'] = 3
461
+ @set['b'] = 5
462
+ @set['b'].should == 5
463
+ @set['c'] = 4
464
+
465
+ @set[0,-1].should == ['a','c','b']
466
+ @set[0..2].should == ['a','c','b']
467
+ @set[0,2].should == ['a','c','b'] # consistent with Redis, not Ruby
468
+ @set.range(0,1,:withscores => true).should == [['a',3],['c',4]]
469
+ @set.range(0,-1).should == ['a','c','b']
470
+ @set.revrange(0,-1).should == ['b','c','a']
471
+ @set[0..1].should == ['a','c']
472
+ @set[1].should == 0 # missing
473
+ @set.at(1).should == 'c'
474
+ @set.first.should == 'a'
475
+ @set.last.should == 'b'
476
+
477
+ @set.members.should == ['a','c','b']
478
+ @set.members(:withscores => true).should == [['a',3],['c',4],['b',5]]
479
+
480
+ @set['b'] = 5
481
+ @set['b'] = 6
482
+ @set.score('b').should == 6
483
+ @set.delete('c')
484
+ @set.to_s.should == 'a, b'
485
+ @set.should == ['a','b']
486
+ @set.members.should == ['a','b']
487
+ @set['d'] = 0
243
488
 
244
- # math proxy ops
245
- (@counter == 10).should be_true
246
- (@counter <= 10).should be_true
247
- (@counter < 11).should be_true
248
- (@counter > 9).should be_true
249
- (@counter >= 10).should be_true
250
- "#{@counter}".should == "10"
489
+ @set.rangebyscore(0, 4).should == ['d','a']
490
+ @set.rangebyscore(0, 4, :count => 1).should == ['d']
491
+ @set.rangebyscore(0, 4, :count => 2).should == ['d','a']
492
+ @set.rangebyscore(0, 4, :limit => 2).should == ['d','a']
251
493
 
252
- @counter.increment.should == 11
253
- @counter.increment.should == 12
254
- @counter2.increment.should == 13
255
- @counter2.increment(2).should == 15
256
- @counter.decrement.should == 14
257
- @counter2.decrement.should == 13
258
- @counter.decrement.should == 12
259
- @counter2.decrement(4).should == 8
260
- @counter.should == 8
261
- @counter.reset.should be_true
262
- @counter.should == 0
263
- @counter.reset(15).should be_true
264
- @counter.should == 15
494
+ # Redis 1.3.5
495
+ # @set.rangebyscore(0,4, :withscores => true).should == [['d',4],['a',3]]
496
+ # @set.revrangebyscore(0,4).should == ['d','a']
497
+ # @set.revrangebyscore(0,4, :count => 2).should == ['a','d']
498
+ # @set.rank('b').should == 2
499
+ # @set.revrank('b').should == 3
500
+
501
+ @set['f'] = 100
502
+ @set['g'] = 110
503
+ @set['h'] = 120
504
+ @set['j'] = 130
505
+ @set.incr('h', 20)
506
+ @set.remrangebyscore(100, 120)
507
+ @set.members.should == ['d','a','b','j','h']
508
+
509
+ # Redis 1.3.5
510
+ # @set['h'] = 12
511
+ # @set['j'] = 13
512
+ # @set.remrangebyrank(4,-1)
513
+ # @set.members.should == ['d','a','b']
514
+
515
+ @set.delete('d')
516
+ @set['c'] = 200
517
+ @set.members.should == ['a','b','j','h','c']
518
+ @set.delete('c')
519
+ @set.length.should == 4
520
+ @set.size.should == 4
521
+ end
522
+
523
+ # Not until Redis 1.3.5 with hashes
524
+ xit "Redis 1.3.5: should handle set intersections, unions, and diffs" do
525
+ @set_1['a'] = 5
526
+ @set_2['b'] = 18
527
+ @set_2['c'] = 12
528
+
529
+ @set_2['a'] = 10
530
+ @set_2['b'] = 15
531
+ @set_2['c'] = 15
532
+
533
+ (@set_1 & @set_2).sort.should == ['c','d','e']
534
+
535
+ @set_1 << 'a' << 'b' << 'c' << 'd' << 'e'
536
+ @set_2 << 'c' << 'd' << 'e' << 'f' << 'g'
537
+ @set_3 << 'a' << 'd' << 'g' << 'l' << 'm'
538
+ @set_1.sort.should == %w(a b c d e)
539
+ @set_2.sort.should == %w(c d e f g)
540
+ @set_3.sort.should == %w(a d g l m)
541
+ (@set_1 & @set_2).sort.should == ['c','d','e']
542
+ @set_1.intersection(@set_2).sort.should == ['c','d','e']
543
+ @set_1.intersection(@set_2, @set_3).sort.should == ['d']
544
+ @set_1.intersect(@set_2).sort.should == ['c','d','e']
545
+ @set_1.inter(@set_2, @set_3).sort.should == ['d']
546
+ @set_1.interstore(INTERSTORE_KEY, @set_2).should == 3
547
+ @set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['c','d','e']
548
+ @set_1.interstore(INTERSTORE_KEY, @set_2, @set_3).should == 1
549
+ @set_1.redis.smembers(INTERSTORE_KEY).sort.should == ['d']
550
+
551
+ (@set_1 | @set_2).sort.should == ['a','b','c','d','e','f','g']
552
+ (@set_1 + @set_2).sort.should == ['a','b','c','d','e','f','g']
553
+ @set_1.union(@set_2).sort.should == ['a','b','c','d','e','f','g']
554
+ @set_1.union(@set_2, @set_3).sort.should == ['a','b','c','d','e','f','g','l','m']
555
+ @set_1.unionstore(UNIONSTORE_KEY, @set_2).should == 7
556
+ @set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g']
557
+ @set_1.unionstore(UNIONSTORE_KEY, @set_2, @set_3).should == 9
558
+ @set_1.redis.smembers(UNIONSTORE_KEY).sort.should == ['a','b','c','d','e','f','g','l','m']
559
+
560
+ (@set_1 ^ @set_2).sort.should == ["a", "b"]
561
+ (@set_1 - @set_2).sort.should == ["a", "b"]
562
+ (@set_2 - @set_1).sort.should == ["f", "g"]
563
+ @set_1.difference(@set_2).sort.should == ["a", "b"]
564
+ @set_1.diff(@set_2).sort.should == ["a", "b"]
565
+ @set_1.difference(@set_2, @set_3).sort.should == ['b']
566
+ @set_1.diffstore(DIFFSTORE_KEY, @set_2).should == 2
567
+ @set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['a','b']
568
+ @set_1.diffstore(DIFFSTORE_KEY, @set_2, @set_3).should == 1
569
+ @set_1.redis.smembers(DIFFSTORE_KEY).sort.should == ['b']
570
+ end
571
+
572
+ it "should support renaming sets" do
573
+ @set.should be_empty
574
+ @set['zynga'] = 151
575
+ @set['playfish'] = 202
576
+ @set.members.should == ['zynga','playfish']
577
+ @set.key.should == 'spec/zset'
578
+ @set.rename('spec/zset2').should be_true
579
+ @set.key.should == 'spec/zset2'
580
+ old = Redis::SortedSet.new('spec/zset')
581
+ old.should be_empty
582
+ old['tuff'] = 54
583
+ @set.renamenx('spec/zset').should be_false
584
+ @set.renamenx(old).should be_false
585
+ @set.renamenx('spec/zfoo').should be_true
586
+ @set.clear
587
+ @set.redis.del('spec/zset2')
265
588
  end
266
589
 
267
590
  after :all do
268
- @counter.delete
591
+ @set.clear
592
+ @set_1.clear
593
+ @set_2.clear
594
+ @set_3.clear
269
595
  end
270
- end
596
+ end