redis-objects 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -25,23 +25,14 @@ class Redis
25
25
  class << self
26
26
  def expiration_filter(*names)
27
27
  names.each do |name|
28
- if ['=', '?', '!'].include? name.to_s[-1]
29
- with_name = "#{name[0..-2]}_with_expiration#{name[-1]}".to_sym
30
- without_name = "#{name[0..-2]}_without_expiration#{name[-1]}".to_sym
31
- else
32
- with_name = "#{name}_with_expiration".to_sym
33
- without_name = "#{name}_without_expiration".to_sym
34
- end
35
-
36
- alias_method without_name, name
28
+ # http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html
29
+ bind_method = instance_method(name)
37
30
 
38
- define_method(with_name) do |*args|
39
- result = send(without_name, *args)
31
+ define_method(name) do |*args, &block|
32
+ result = bind_method.bind(self).call(*args, &block)
40
33
  set_expiration
41
34
  result
42
35
  end
43
-
44
- alias_method name, with_name
45
36
  end
46
37
  end
47
38
  end
data/lib/redis/counter.rb CHANGED
@@ -113,8 +113,10 @@ class Redis
113
113
  EndOverload
114
114
  end
115
115
 
116
- expiration_filter :increment, :decrement
117
-
116
+ expiration_filter :increment, :incr, :incrby, :incrbyfloat,
117
+ :decrement, :decr, :decrby, :decrbyfloat,
118
+ :value=, :set, :reset
119
+
118
120
  private
119
121
 
120
122
  # Implements atomic increment/decrement blocks
@@ -158,7 +158,7 @@ class Redis
158
158
  def incrbyfloat(field, by=1.0)
159
159
  ret = redis.hincrbyfloat(key, field, by)
160
160
  unless ret.is_a? Array
161
- ret.to_i
161
+ ret.to_f
162
162
  else
163
163
  nil
164
164
  end
@@ -169,7 +169,9 @@ class Redis
169
169
  incrbyfloat(field, -by)
170
170
  end
171
171
 
172
- expiration_filter :[]=, :store, :bulk_set, :fill, :incrby
172
+ expiration_filter :[]=, :store, :bulk_set, :fill,
173
+ :incrby, :incr, :incrbyfloat,
174
+ :decrby, :decr, :decrbyfloat
173
175
  end
174
176
  end
175
177
 
data/lib/redis/list.rb CHANGED
@@ -70,8 +70,7 @@ class Redis
70
70
  # a range of values using Redis: LRANGE.
71
71
  def [](index, length=nil)
72
72
  if index.is_a? Range
73
- last = index.exclude_end? ? (index.last - 1) : index.last
74
- range(index.first, last)
73
+ range(index.first, index.max)
75
74
  elsif length
76
75
  case length <=> 0
77
76
  when 1 then range(index, index + length - 1)
@@ -144,6 +143,6 @@ class Redis
144
143
  values.join(', ')
145
144
  end
146
145
 
147
- expiration_filter :[]=, :push, :insert, :unshift
146
+ expiration_filter :[]=, :push, :<<, :insert, :unshift
148
147
  end
149
148
  end
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  module Objects
3
- VERSION = "0.9.1"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -41,7 +41,7 @@ class Redis
41
41
  # a range of values using Redis: ZRANGE.
42
42
  def [](index, length=nil)
43
43
  if index.is_a? Range
44
- range(index.first, index.last)
44
+ range(index.first, index.max)
45
45
  elsif length
46
46
  case length <=> 0
47
47
  when 1 then range(index, index + length - 1)
@@ -206,7 +206,8 @@ class Redis
206
206
  # Calculate the intersection and store it in Redis as +name+. Returns the number
207
207
  # of elements in the stored intersection. Redis: SUNIONSTORE
208
208
  def interstore(name, *sets)
209
- redis.zinterstore(name, keys_from_objects([self] + sets))
209
+ opts = sets.last.is_a?(Hash) ? sets.pop : {}
210
+ redis.zinterstore(key_from_object(name), keys_from_objects([self] + sets), opts)
210
211
  end
211
212
 
212
213
  # Return the union with another set. Can pass it either another set
@@ -229,7 +230,8 @@ class Redis
229
230
  # Calculate the union and store it in Redis as +name+. Returns the number
230
231
  # of elements in the stored union. Redis: SUNIONSTORE
231
232
  def unionstore(name, *sets)
232
- redis.zunionstore(name, keys_from_objects([self] + sets))
233
+ opts = sets.last.is_a?(Hash) ? sets.pop : {}
234
+ redis.zunionstore(key_from_object(name), keys_from_objects([self] + sets), opts)
233
235
  end
234
236
 
235
237
  # Return the difference vs another set. Can pass it either another set
@@ -302,13 +304,18 @@ class Redis
302
304
  !redis.zscore(key, marshal(value)).nil?
303
305
  end
304
306
 
305
- expiration_filter :[]=, :add, :merge, :diffstore, :increment, :decrement, :intersection, :interstore, :unionstore
307
+ expiration_filter :[]=, :add, :merge, :delete,
308
+ :increment, :incr, :incrby, :decrement, :decr, :decrby,
309
+ :intersection, :interstore, :unionstore, :diffstore
306
310
 
307
311
  private
312
+ def key_from_object(set)
313
+ set.is_a?(Redis::SortedSet) ? set.key : set
314
+ end
308
315
 
309
316
  def keys_from_objects(sets)
310
317
  raise ArgumentError, "Must pass in one or more set names" if sets.empty?
311
- sets.collect{|set| set.is_a?(Redis::SortedSet) ? set.key : set}
318
+ sets.collect{|set| set.is_a?(Redis::SortedSet) || set.is_a?(Redis::Set) ? set.key : set}
312
319
  end
313
320
  end
314
321
  end
data/lib/redis/value.rb CHANGED
@@ -11,7 +11,7 @@ class Redis
11
11
  attr_reader :key, :options
12
12
  def initialize(key, *args)
13
13
  super(key, *args)
14
- redis.setnx(key, marshal(@options[:default])) if @options[:default]
14
+ redis.setnx(key, marshal(@options[:default])) if !@options[:default].nil?
15
15
  end
16
16
 
17
17
  def value=(val)
@@ -14,6 +14,11 @@ describe Redis::Value do
14
14
  @value.value.should == {:json => 'data'}
15
15
  end
16
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
+
17
22
  it "should handle simple values" do
18
23
  @value.should == nil
19
24
  @value.value = 'Trevor Hoffman'
@@ -157,7 +162,7 @@ describe Redis::List do
157
162
  end
158
163
  end
159
164
 
160
- describe "with basic operations" do
165
+ describe "basic operations" do
161
166
  before do
162
167
  @list = Redis::List.new('spec/list')
163
168
  @list.clear
@@ -260,23 +265,23 @@ describe Redis::List do
260
265
  list2.should == ["b"]
261
266
  end
262
267
 
263
- it "should handle insert" do
264
- @list << 'b' << 'd'
265
- @list.insert(:before,'b','a')
266
- @list.insert(:after,'b','c')
267
- @list.insert("before",'a','z')
268
- @list.insert("after",'d','e')
269
- @list.should == ['z','a','b','c','d','e']
270
- end
271
-
272
- it "should handle insert at a specific index" do
273
- @list << 'b' << 'd'
274
- @list.should == ['b','d']
275
- @list[0] = 'a'
276
- @list.should == ['a', 'd']
277
- @list[1] = 'b'
278
- @list.should == ['a', 'b']
279
- end
268
+ it "should handle insert" do
269
+ @list << 'b' << 'd'
270
+ @list.insert(:before,'b','a')
271
+ @list.insert(:after,'b','c')
272
+ @list.insert("before",'a','z')
273
+ @list.insert("after",'d','e')
274
+ @list.should == ['z','a','b','c','d','e']
275
+ end
276
+
277
+ it "should handle insert at a specific index" do
278
+ @list << 'b' << 'd'
279
+ @list.should == ['b','d']
280
+ @list[0] = 'a'
281
+ @list.should == ['a', 'd']
282
+ @list[1] = 'b'
283
+ @list.should == ['a', 'b']
284
+ end
280
285
 
281
286
  it "should handle lists of complex data types" do
282
287
  @list.options[:marshal] = true
@@ -329,24 +334,35 @@ describe Redis::List do
329
334
  @list.redis.del('spec/list2')
330
335
  end
331
336
 
332
- it 'should set time to live in seconds when expiration option assigned' do
333
- @list = Redis::List.new('spec/list', :expiration => 10)
334
- @list << 'val'
335
- @list.ttl.should > 0
336
- @list.ttl.should <= 10
337
+ after do
338
+ @list.clear
337
339
  end
340
+ end
338
341
 
339
- it 'should set expiration when expireat option assigned' do
340
- @list = Redis::List.new('spec/list', :expireat => Time.now + 10.seconds)
341
- @list << 'val'
342
- @list.ttl.should > 0
343
- @list.ttl.should <= 10
344
- end
342
+ describe 'with expiration' do
343
+ [:[]=, :push, :<<, :insert, :unshift].each do |meth|
344
+ describe meth do
345
+ it 'expiration: option' do
346
+ @list = Redis::List.new('spec/list_exp', :expiration => 10)
347
+ @list << 'val'
348
+ @list.ttl.should > 0
349
+ @list.ttl.should <= 10
350
+ end
351
+
352
+ it 'expireat: option' do
353
+ @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
354
+ @list << 'val'
355
+ @list.ttl.should > 0
356
+ @list.ttl.should <= 10
357
+ end
358
+ end
345
359
 
346
- after do
347
- @list.clear
360
+ after do
361
+ @list.clear
362
+ end
348
363
  end
349
364
  end
365
+
350
366
  end
351
367
 
352
368
  describe Redis::Counter do
@@ -399,18 +415,73 @@ describe Redis::Counter do
399
415
  @counter.clear
400
416
  end
401
417
 
402
- it 'should set time to live in seconds when expiration option assigned' do
403
- @counter = Redis::Counter.new('spec/counter', :expiration => 10)
404
- @counter.increment
405
- @counter.ttl.should > 0
406
- @counter.ttl.should <= 10
418
+ it "should support an atomic block" do
419
+ @counter = Redis::Counter.new("spec/block_counter")
420
+ @counter.should == 0
421
+ @counter.increment(1)
422
+ # The block is never executed.
423
+ @updated =
424
+ @counter.increment(1) do |updated|
425
+ if updated == 2
426
+ 'yep'
427
+ else
428
+ raise("test failed")
429
+ end
430
+ end
431
+ @updated.should == 'yep'
432
+ @counter.should == 2
407
433
  end
408
434
 
409
- it 'should set expiration when expireat option assigned' do
410
- @counter = Redis::Counter.new('spec/counter', :expireat => Time.now + 10.seconds)
411
- @counter.increment
412
- @counter.ttl.should > 0
413
- @counter.ttl.should <= 10
435
+ describe 'with expiration' do
436
+ it 'should set time to live in seconds' do
437
+ @counter = Redis::Counter.new('spec/counter', :expiration => 10)
438
+ @counter.increment
439
+ @counter.ttl.should > 0
440
+ @counter.ttl.should <= 10
441
+ end
442
+
443
+ [:increment, :incr, :incrby, :incrbyfloat,
444
+ :decrement, :decr, :decrby, :decrbyfloat, :reset].each do |meth|
445
+ describe meth do
446
+ it "expiration: option" do
447
+ @counter = Redis::Counter.new('spec/counter_exp', :expiration => 10)
448
+ @counter.send(meth)
449
+ @counter.ttl.should > 0
450
+ @counter.ttl.should <= 10
451
+ end
452
+ it "expireat: option" do
453
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
454
+ @counter.send(meth)
455
+ @counter.ttl.should > 0
456
+ @counter.ttl.should <= 10
457
+ end
458
+ after do
459
+ @counter.reset
460
+ end
461
+ end
462
+ end
463
+
464
+ [:set, :value=].each do |meth|
465
+ describe meth do
466
+ it "expiration: option" do
467
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
468
+ @counter.send(meth, 99)
469
+ @counter.should == 99
470
+ @counter.ttl.should > 0
471
+ @counter.ttl.should <= 10
472
+ end
473
+ it "expireat: option" do
474
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
475
+ @counter.send(meth, 99)
476
+ @counter.should == 99
477
+ @counter.ttl.should > 0
478
+ @counter.ttl.should <= 10
479
+ end
480
+ after do
481
+ @counter.reset
482
+ end
483
+ end
484
+ end
414
485
  end
415
486
 
416
487
  after do
@@ -647,7 +718,7 @@ describe Redis::HashKey do
647
718
  @hash['float'] = '5.0e3'
648
719
  @hash.incrbyfloat('float')
649
720
  @hash.incrbyfloat('float', '1.23e3')
650
- @hash.incrbyfloat('float', 45.3)
721
+ @hash.incrbyfloat('float', 45.3).should == 6276.3
651
722
  @hash.get('float').to_f.should == 6276.3
652
723
  end
653
724
 
@@ -704,10 +775,11 @@ describe Redis::HashKey do
704
775
  @hash['foo'].should == 'bar'
705
776
  @hash['abc'].should == '123'
706
777
  @hash['bang'].should == 'michael'
778
+ @hash.keys.sort.should == ['abc', 'bang', 'foo']
779
+ end
707
780
 
708
- it "raises an error if a non-Hash is passed to fill" do
709
- lambda { @hash.fill([]) }.should.raise(ArgumentError)
710
- end
781
+ it "raises an error if a non-Hash is passed to fill" do
782
+ lambda { @hash.fill([]) }.should.raise(ArgumentError)
711
783
  end
712
784
 
713
785
  it "should fetch default values" do
@@ -722,18 +794,28 @@ describe Redis::HashKey do
722
794
  block.should == "oops: missing_key"
723
795
  end
724
796
 
725
- it 'should set time to live in seconds when expiration option assigned' do
726
- @hash = Redis::HashKey.new('spec/hash_key', :expiration => 10)
727
- @hash['foo'] = 'bar'
728
- @hash.ttl.should > 0
729
- @hash.ttl.should <= 10
730
- end
731
-
732
- it 'should set expiration when expireat option assigned' do
733
- @hash = Redis::HashKey.new('spec/hash_key', :expireat => Time.now + 10.seconds)
734
- @hash['foo'] = 'bar'
735
- @hash.ttl.should > 0
736
- end
797
+ #[:[]=, :store, :bulk_set, :fill,
798
+ describe 'with expiration' do
799
+ [:incrby, :incr, :incrbyfloat, :decrby, :decr, :decrbyfloat].each do |meth|
800
+ describe meth do
801
+ it "expiration: option" do
802
+ @hash = Redis::HashKey.new('spec/hash_exp', :expiration => 10)
803
+ @hash.send(meth, 'somekey')
804
+ @hash.ttl.should > 0
805
+ @hash.ttl.should <= 10
806
+ end
807
+ it "expireat: option" do
808
+ @hash = Redis::HashKey.new('spec/hash_exp', :expireat => Time.now + 10.seconds)
809
+ @hash.send(meth, 'somekey')
810
+ @hash.ttl.should > 0
811
+ @hash.ttl.should <= 10
812
+ end
813
+ after do
814
+ @hash.clear
815
+ end
816
+ end
817
+ end
818
+ end
737
819
 
738
820
  after do
739
821
  @hash.clear
@@ -884,14 +966,14 @@ describe Redis::Set do
884
966
  @set_2.sort(SORT_LIMIT).should == %w(3 4)
885
967
 
886
968
  @set_3 << 'm_4' << 'm_5' << 'm_1' << 'm_3' << 'm_2'
887
- ### incorrect interpretation of what the :by parameter means
888
- ### :by will look up values of keys so it would try to find a value in
889
- ### redis of "m_m_1" which doesn't exist at this point, it is not a way to
890
- ### alter the value to sort by but rather use a different value for this value
891
- ### in the set (Kris Fox)
969
+ ### incorrect interpretation of what the :by parameter means
970
+ ### :by will look up values of keys so it would try to find a value in
971
+ ### redis of "m_m_1" which doesn't exist at this point, it is not a way to
972
+ ### alter the value to sort by but rather use a different value for this value
973
+ ### in the set (Kris Fox)
892
974
  # @set_3.sort(:by => 'm_*').should == %w(m_1 m_2 m_3 m_4 m_5)
893
- # below passes just fine
894
- @set_3.sort.should == %w(m_1 m_2 m_3 m_4 m_5)
975
+ # below passes just fine
976
+ @set_3.sort.should == %w(m_1 m_2 m_3 m_4 m_5)
895
977
 
896
978
  val1 = Redis::Value.new('spec/3/sorted')
897
979
  val2 = Redis::Value.new('spec/4/sorted')
@@ -938,11 +1020,13 @@ describe Redis::SortedSet do
938
1020
  @set_2 = Redis::SortedSet.new('spec/zset_2')
939
1021
  @set_3 = Redis::SortedSet.new('spec/zset_3')
940
1022
  @set_4 = Redis::SortedSet.new('spec/zset_3', :marshal => true)
1023
+ @set_5 = Redis::Set.new('spec/zset_4')
941
1024
  @set.clear
942
1025
  @set_1.clear
943
1026
  @set_2.clear
944
1027
  @set_3.clear
945
1028
  @set_4.clear
1029
+ @set_5.clear
946
1030
  end
947
1031
 
948
1032
  it "should handle sorted sets of simple values" do
@@ -960,6 +1044,7 @@ describe Redis::SortedSet do
960
1044
  a = @set.members
961
1045
  @set[0,-1].should == a[0,-1]
962
1046
  @set[0..2].should == a[0..2]
1047
+ @set[0...2].should == a[0...2]
963
1048
  @set.slice(0..2).should == a.slice(0..2)
964
1049
  @set[0, 2].should == a[0,2]
965
1050
  @set.slice(0, 2).should == a.slice(0, 2)
@@ -1088,6 +1173,74 @@ describe Redis::SortedSet do
1088
1173
  @set.redis.del('spec/zset2')
1089
1174
  end
1090
1175
 
1176
+ it "should handle unions" do
1177
+ @set_1.add('a', 1)
1178
+ @set_1.add('b', 4)
1179
+ @set_1.add('c', 3)
1180
+
1181
+ @set_2.add('b', 2)
1182
+ @set_2.add('c', 1)
1183
+ @set_2.add('d', 0)
1184
+
1185
+ @set_1.unionstore(@set.key, @set_2)
1186
+ # @set is now: [[d, 0], [a, 1], [c, 4], [b, 6]]
1187
+ @set.members.should == ['d', 'a', 'c', 'b']
1188
+
1189
+ @set_2.unionstore(@set, @set_1, :aggregate => :sum)
1190
+ # @set is now: [[d, 0], [a, 1], [c, 4], [b, 6]]
1191
+ @set.members.should == ['d', 'a', 'c', 'b']
1192
+
1193
+ @set_1.unionstore(@set, @set_2, :aggregate => :min)
1194
+ # @set is now: [[d, 0], [a, 1], [c, 1], [b, 2]]
1195
+ @set.members.should == ['d', 'a', 'c', 'b']
1196
+ @set['b'].should == 2
1197
+
1198
+ @set_1.unionstore(@set, @set_2, :aggregate => :max)
1199
+ # @set is now: [[d, 0], [a, 1], [c, 3], [b, 4]]
1200
+ @set.members.should == ['d', 'a', 'c', 'b']
1201
+ @set['b'].should == 4
1202
+
1203
+ @set_1.unionstore(@set, @set_2, :aggregate => :sum, :weights => [0, 1])
1204
+ # @set is now: [[a, 0], [d, 0], [c, 1], [b, 2]]
1205
+ @set.members.should == ['a', 'd', 'c', 'b']
1206
+ @set['b'].should == 2
1207
+
1208
+ @set_5 << ['a', 'e', 'f']
1209
+ @set_1.unionstore(@set, @set_5, :aggregate => :sum)
1210
+ # #set is now: [[e, 1], [f, 1], [a, 1], [c, 3], [b, 4]]
1211
+ @set.members.should == ['e', 'f', 'a', 'c', 'b']
1212
+ @set['e'].should == 1
1213
+ end
1214
+
1215
+ it "should handle intersections" do
1216
+ @set_1.add('a', 1)
1217
+ @set_1.add('b', 4)
1218
+ @set_1.add('c', 3)
1219
+
1220
+ @set_2.add('b', 2)
1221
+ @set_2.add('c', 1)
1222
+ @set_2.add('d', 0)
1223
+
1224
+ @set_1.interstore(@set.key, @set_2)
1225
+ # @set is now: [[c, 4], [b, 6]]
1226
+ @set.members.should == ['c', 'b']
1227
+
1228
+ @set_2.interstore(@set, @set_1, :aggregate => :sum)
1229
+ # @set is now: [[c, 4], [b, 6]]
1230
+ @set.members.should == ['c', 'b']
1231
+
1232
+ @set_1.interstore(@set, @set_2, :aggregate => :min)
1233
+ # @set is now: [[c, 1], [b, 2]]
1234
+ @set.members.should == ['c', 'b']
1235
+ @set['b'].should == 2
1236
+
1237
+ @set_5 << ['a', 'e', 'b']
1238
+ @set_1.interstore(@set, @set_5, :aggregate => :max)
1239
+ # @set is now: [[a, 1], [b, 4]]
1240
+ @set.members.should == ['a', 'b']
1241
+ @set['b'].should == 4
1242
+ end
1243
+
1091
1244
  it 'should set time to live in seconds when expiration option assigned' do
1092
1245
  @set = Redis::SortedSet.new('spec/zset', :expiration => 10)
1093
1246
  @set['val'] = 1
@@ -1102,11 +1255,34 @@ describe Redis::SortedSet do
1102
1255
  @set.ttl.should <= 10
1103
1256
  end
1104
1257
 
1258
+ describe "with expiration" do
1259
+ [:[]=, :add, :increment, :incr, :incrby, :decrement, :decr, :decrby].each do |meth|
1260
+ describe meth do
1261
+ it "expiration: option" do
1262
+ @hash = Redis::SortedSet.new('spec/zset_exp', :expiration => 10)
1263
+ @hash.send(meth, 'somekey', 12)
1264
+ @hash.ttl.should > 0
1265
+ @hash.ttl.should <= 10
1266
+ end
1267
+ it "expireat: option" do
1268
+ @hash = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
1269
+ @hash.send(meth, 'somekey', 12)
1270
+ @hash.ttl.should > 0
1271
+ @hash.ttl.should <= 10
1272
+ end
1273
+ after do
1274
+ @hash.clear
1275
+ end
1276
+ end
1277
+ end
1278
+ end
1279
+
1105
1280
  after do
1106
1281
  @set.clear
1107
1282
  @set_1.clear
1108
1283
  @set_2.clear
1109
1284
  @set_3.clear
1110
1285
  @set_4.clear
1286
+ @set_5.clear
1111
1287
  end
1112
1288
  end