redis-objects 1.4.3 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/redis/value.rb CHANGED
@@ -6,11 +6,6 @@ class Redis
6
6
  # Class representing a simple value. You can use standard Ruby operations on it.
7
7
  #
8
8
  class Value < BaseObject
9
- require 'redis/helpers/core_commands'
10
- include Redis::Helpers::CoreCommands
11
-
12
- attr_reader :key, :options
13
-
14
9
  def value=(val)
15
10
  allow_expiration do
16
11
  if val.nil?
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  # Only fix this one version or else tests break
22
- spec.add_dependency "redis", "~> 4.0"
22
+ spec.add_dependency "redis", "< 4.6"
23
23
 
24
24
  # Ignore gemspec warnings on these. Trying to fix them to versions breaks TravisCI
25
25
  spec.add_development_dependency "bundler"
@@ -29,6 +29,10 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  # Compatibility testing
31
31
  spec.add_development_dependency "redis-namespace"
32
+ spec.add_development_dependency "activesupport"
32
33
  spec.add_development_dependency "activerecord"
33
34
  spec.add_development_dependency "sqlite3"
35
+
36
+ # Code coverage
37
+ spec.add_development_dependency "simplecov-cobertura"
34
38
  end
@@ -4,7 +4,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
4
  # tests whether autoload functionality works correctly; had issues previously
5
5
 
6
6
  require 'redis/objects'
7
- # $redis used automatically
7
+ Redis::Objects.redis = REDIS_HANDLE
8
8
 
9
9
  describe 'Redis::Objects' do
10
10
  it "should autoload everything" do
@@ -5,6 +5,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
5
5
 
6
6
  require 'redis/objects'
7
7
  require 'connection_pool'
8
+ Redis::Objects.redis = REDIS_HANDLE
8
9
 
9
10
  BAD_REDIS = "totally bad bogus redis handle"
10
11
 
@@ -95,13 +96,13 @@ describe 'Connection tests' do
95
96
  end
96
97
 
97
98
  it "should support local handles with a vanilla redis connection" do
98
- Redis.current = nil # reset from other tests
99
+ # Redis.current = nil # reset from other tests
99
100
  Redis::Objects.redis = nil
100
101
  @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
101
102
 
102
103
  # Redis.current is lazily auto-populated to touch 6379
103
104
  # This why we choose the weird 9212 port to avoid
104
- Redis.current.inspect.should == Redis.new.inspect
105
+ # Redis.current.inspect.should == Redis.new.inspect
105
106
  Redis::Objects.redis.inspect.should == Redis.new.inspect
106
107
 
107
108
  v = Redis::Value.new('conn/value', @redis_handle)
@@ -140,13 +141,13 @@ describe 'Connection tests' do
140
141
  end
141
142
 
142
143
  it "should support local handles with a connection_pool" do
143
- Redis.current = nil # reset from other tests
144
+ # Redis.current = nil # reset from other tests
144
145
  Redis::Objects.redis = nil
145
146
  @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
146
147
 
147
148
  # Redis.current is lazily auto-populated to touch 6379
148
149
  # This why we choose the weird 9212 port to avoid
149
- Redis.current.inspect.should == Redis.new.inspect
150
+ # Redis.current.inspect.should == Redis.new.inspect
150
151
  Redis::Objects.redis.inspect.should == Redis.new.inspect
151
152
 
152
153
  v = Redis::Value.new('conn/value', @redis_handle)
@@ -184,24 +185,19 @@ describe 'Connection tests' do
184
185
  c.decr(1)
185
186
  end
186
187
 
187
- it "should support Redis.current" do
188
- Redis.current = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
189
-
190
- Redis::Value.new('conn/value').should == 'yay'
191
- Redis::HashKey.new('conn/hash').keys.should == ['k']
192
- Redis::List.new('conn/list').sort.should == ['3', '4', '5']
193
- Redis::Set.new('conn/set').sort.should == ['5', '6', '7']
194
- Redis::SortedSet.new('conn/zset').should == ['d', 'b', 'a', 'c']
195
- Redis::Counter.new('conn/counter').should == 2
188
+ it "should properly support fallback handle variables" do
189
+ # Redis.current is lazily auto-populated to touch 6379
190
+ # This why we choose the weird 9212 port to avoid
191
+ old_redis = $redis
192
+ $redis = BAD_REDIS
193
+ Redis::Objects.redis.should == BAD_REDIS
194
+ $redis = old_redis
196
195
  end
197
196
 
198
197
  it "should support Redis::Objects.redis= with a connection_pool" do
198
+ # reset redis
199
199
  @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
200
-
201
- # Redis.current is lazily auto-populated to touch 6379
202
- # This why we choose the weird 9212 port to avoid
203
- Redis.current = BAD_REDIS
204
- Redis::Objects.redis.should == BAD_REDIS
200
+ Redis::Objects.redis = @redis_handle
205
201
 
206
202
  # This set of tests sucks, it fucks up the per-data-type handles
207
203
  # because Redis.current is then set to a BS value, and the lazy
@@ -229,13 +225,8 @@ describe 'Connection tests' do
229
225
 
230
226
  it "should support Redis::Objects.redis= with a vanilla redis connection" do
231
227
  # reset redis
232
- Redis::Objects.redis = nil
233
228
  @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
234
-
235
- # Redis.current is lazily auto-populated to touch 6379
236
- # This why we choose the weird 9212 port to avoid
237
- Redis.current = BAD_REDIS
238
- Redis::Objects.redis.should == BAD_REDIS
229
+ Redis::Objects.redis = @redis_handle
239
230
 
240
231
  # This set of tests sucks, it fucks up the per-data-type handles
241
232
  # because Redis.current is then set to a BS value, and the lazy
@@ -260,7 +251,7 @@ describe 'Connection tests' do
260
251
  Redis::Counter.new('conn/counter').should == 2
261
252
 
262
253
  # Fix for future tests
263
- Redis.current = @redis_handle
254
+ # Redis.current = @redis_handle
264
255
  end
265
256
 
266
257
  it "should support pipelined changes" do
@@ -0,0 +1,199 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ require 'redis/objects'
5
+ Redis::Objects.redis = REDIS_HANDLE
6
+
7
+ class CustomSerializer
8
+ class << self
9
+ attr_accessor :dump_called, :load_called, :dump_args, :load_args
10
+ end
11
+
12
+ def self.dump(value, *args, **kargs)
13
+ @dump_called = true
14
+ @dump_args = [args, kargs]
15
+ Marshal.dump(value)
16
+ end
17
+
18
+ def self.load(value, *args, **kargs)
19
+ @load_called = true
20
+ @load_args = [args, kargs]
21
+ Marshal.load(value)
22
+ end
23
+
24
+ def self.reset!
25
+ @dump_called = nil
26
+ @load_called = nil
27
+ end
28
+ end
29
+
30
+ describe 'with custom serialization' do
31
+ before do
32
+ CustomSerializer.reset!
33
+ end
34
+
35
+ describe Redis::Value do
36
+ before do
37
+ @value = Redis::Value.new(
38
+ 'spec/value_custom_serializer',
39
+ marshal: true,
40
+ serializer: CustomSerializer
41
+ )
42
+ @value.clear
43
+ end
44
+
45
+ it 'uses custom serializer' do
46
+ @value.value = { json: 'data' }
47
+ CustomSerializer.dump_called.should == true
48
+ CustomSerializer.load_called.should == nil
49
+ @value.value.should == { json: 'data' }
50
+ CustomSerializer.dump_called.should == true
51
+ CustomSerializer.load_called.should == true
52
+ end
53
+
54
+ it 'passes extra arguments to dump' do
55
+ @value.options[:marshal_dump_args] = ['some', { extra: 'arguments' }]
56
+ @value.value = 1
57
+ CustomSerializer.dump_args.should == [['some'], { extra: 'arguments' }]
58
+ end
59
+
60
+ it 'passes extra arguments to load' do
61
+ @value.options[:marshal_load_args] = ['some', { extra: 'arguments' }]
62
+ @value.value = 1
63
+ @value.value.should == 1
64
+ CustomSerializer.load_args.should == [['some'], { extra: 'arguments' }]
65
+ end
66
+ end
67
+
68
+ describe Redis::List do
69
+ before do
70
+ @list = Redis::List.new(
71
+ 'spec/list_custom_serializer',
72
+ marshal: true,
73
+ serializer: CustomSerializer
74
+ )
75
+ @list.clear
76
+ end
77
+
78
+ it 'uses custom serializer' do
79
+ @list << { json: 'data' }
80
+ CustomSerializer.dump_called.should == true
81
+ CustomSerializer.load_called.should == nil
82
+ @list.should == [{ json: 'data' }]
83
+ CustomSerializer.dump_called.should == true
84
+ CustomSerializer.load_called.should == true
85
+ end
86
+
87
+ it 'passes extra arguments to dump' do
88
+ @list.options[:marshal_dump_args] = ['some', { extra: 'arguments' }]
89
+ @list << 1
90
+ CustomSerializer.dump_args.should == [['some'], { extra: 'arguments' }]
91
+ end
92
+
93
+ it 'passes extra arguments to load' do
94
+ @list.options[:marshal_load_args] = ['some', { extra: 'arguments' }]
95
+ @list << 1
96
+ @list.values.should == [1]
97
+ CustomSerializer.load_args.should == [['some'], { extra: 'arguments' }]
98
+ end
99
+ end
100
+
101
+ describe Redis::HashKey do
102
+ before do
103
+ @hash = Redis::HashKey.new(
104
+ 'spec/hash_custom_serializer',
105
+ marshal: true,
106
+ serializer: CustomSerializer
107
+ )
108
+ @hash.clear
109
+ end
110
+
111
+ it 'uses custom serializer' do
112
+ @hash['a'] = 1
113
+ CustomSerializer.dump_called.should == true
114
+ CustomSerializer.load_called.should == nil
115
+ @hash.value.should == { 'a' => 1 }
116
+ CustomSerializer.dump_called.should == true
117
+ CustomSerializer.load_called.should == true
118
+ end
119
+
120
+ it 'passes extra arguments to dump' do
121
+ @hash.options[:marshal_dump_args] = ['some', { extra: 'arguments' }]
122
+ @hash['a'] = 1
123
+ CustomSerializer.dump_args.should == [['some'], { extra: 'arguments' }]
124
+ end
125
+
126
+ it 'passes extra arguments to load' do
127
+ @hash.options[:marshal_load_args] = ['some', { extra: 'arguments' }]
128
+ @hash['a'] = 1
129
+ @hash.value.should == { 'a' => 1 }
130
+ CustomSerializer.load_args.should == [['some'], { extra: 'arguments' }]
131
+ end
132
+ end
133
+
134
+ describe Redis::Set do
135
+ before do
136
+ @set = Redis::Set.new(
137
+ 'spec/set_custom_serializer',
138
+ marshal: true,
139
+ serializer: CustomSerializer
140
+ )
141
+ @set.clear
142
+ end
143
+
144
+ it 'uses custom serializer' do
145
+ @set << 'a'
146
+ CustomSerializer.dump_called.should == true
147
+ CustomSerializer.load_called.should == nil
148
+ @set.members.should == ['a']
149
+ CustomSerializer.dump_called.should == true
150
+ CustomSerializer.load_called.should == true
151
+ end
152
+
153
+ it 'passes extra arguments to dump' do
154
+ @set.options[:marshal_dump_args] = ['some', { extra: 'arguments' }]
155
+ @set << 'a'
156
+ CustomSerializer.dump_args.should == [['some'], { extra: 'arguments' }]
157
+ end
158
+
159
+ it 'passes extra arguments to load' do
160
+ @set.options[:marshal_load_args] = ['some', { extra: 'arguments' }]
161
+ @set << 'a'
162
+ @set.members.should == ['a']
163
+ CustomSerializer.load_args.should == [['some'], { extra: 'arguments' }]
164
+ end
165
+ end
166
+
167
+ describe Redis::SortedSet do
168
+ before do
169
+ @set = Redis::SortedSet.new(
170
+ 'spec/zset_custom_serializer',
171
+ marshal: true,
172
+ serializer: CustomSerializer
173
+ )
174
+ @set.clear
175
+ end
176
+
177
+ it 'uses custom serializer' do
178
+ @set['a'] = 1
179
+ CustomSerializer.dump_called.should == true
180
+ CustomSerializer.load_called.should == nil
181
+ @set.members.should == ['a']
182
+ CustomSerializer.dump_called.should == true
183
+ CustomSerializer.load_called.should == true
184
+ end
185
+
186
+ it 'passes extra arguments to dump' do
187
+ @set.options[:marshal_dump_args] = ['some', { extra: 'arguments' }]
188
+ @set['a'] = 1
189
+ CustomSerializer.dump_args.should == [['some'], { extra: 'arguments' }]
190
+ end
191
+
192
+ it 'passes extra arguments to load' do
193
+ @set.options[:marshal_load_args] = ['some', { extra: 'arguments' }]
194
+ @set['a'] = 1
195
+ @set.members.should == ['a']
196
+ CustomSerializer.load_args.should == [['some'], { extra: 'arguments' }]
197
+ end
198
+ end
199
+ end
@@ -2,6 +2,7 @@
2
2
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
 
4
4
  require 'redis/objects'
5
+ Redis::Objects.redis = REDIS_HANDLE
5
6
 
6
7
  describe Redis::Value do
7
8
  before do
@@ -19,6 +20,21 @@ describe Redis::Value do
19
20
  @value.value.should == false
20
21
  end
21
22
 
23
+ it "should handle simple values" do
24
+ @value.should == nil
25
+ @value.value = 'Trevor Hoffman'
26
+ @value.should == 'Trevor Hoffman'
27
+ @value.get.should == 'Trevor Hoffman'
28
+ @value.exists?.should == true
29
+ @value.exists.should == 1
30
+ @value.del.should == 1
31
+ @value.should.be.nil
32
+ @value.exists?.should == false
33
+ @value.exists.should == 0
34
+ @value.value = 42
35
+ @value.value.should == '42'
36
+ end
37
+
22
38
  it "should compress non marshaled values" do
23
39
  @value = Redis::Value.new('spec/value', compress: true)
24
40
  @value.value = 'Trevor Hoffman'
@@ -39,17 +55,8 @@ describe Redis::Value do
39
55
  @value.value.should == nil
40
56
  @value.value = ''
41
57
  @value.value.should == ''
42
- end
43
-
44
- it "should handle simple values" do
45
- @value.should == nil
46
- @value.value = 'Trevor Hoffman'
47
- @value.should == 'Trevor Hoffman'
48
- @value.get.should == 'Trevor Hoffman'
49
- @value.del.should == 1
50
- @value.should.be.nil
51
- @value.value = 42
52
- @value.value.should == '42'
58
+ @value.delete
59
+ @value.value.should.be.nil
53
60
  end
54
61
 
55
62
  it "should handle complex marshaled values" do
@@ -88,7 +95,7 @@ describe Redis::Value do
88
95
  marshalled_string = Marshal.dump({json: 'marshal'})
89
96
  @value = Redis::Value.new('spec/marshal', :default => marshalled_string, :marshal => true)
90
97
  @value.value.should == marshalled_string
91
- @value.clear
98
+ @value.delete
92
99
  end
93
100
 
94
101
  it "should support renaming values" do
@@ -499,23 +506,56 @@ describe Redis::Counter do
499
506
  @counter.incrbyfloat 2.0e2
500
507
  @counter.to_f.should == 5214.31
501
508
  @counter.clear
509
+ @counter.should.be.nil
502
510
  end
503
511
 
504
512
  it "should support an atomic block" do
505
513
  @counter = Redis::Counter.new("spec/block_counter")
506
514
  @counter.should == 0
507
515
  @counter.increment(1)
508
- # The block is never executed.
509
- @updated =
510
- @counter.increment(1) do |updated|
511
- if updated == 2
512
- 'yep'
513
- else
514
- raise("test failed")
515
- end
516
- end
516
+
517
+ # successfully increments
518
+ @updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
519
+ @updated.should == 'yep'
520
+ @counter.should == 2
521
+
522
+ # fails to increment
523
+ @updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
524
+ @updated.should == nil
525
+ @counter.should == 2
526
+
527
+ # successfully increments by float
528
+ @updated = @counter.incrbyfloat(1.5) { |updated| updated == 3.5 ? 'yep' : nil }
529
+ @updated.should == 'yep'
530
+ @counter.should == 3.5
531
+
532
+ # fails to increment by float
533
+ @updated = @counter.incrbyfloat(2.5) { |updated| updated == 5 ? 'yep' : nil }
534
+ @updated.should == nil
535
+ @counter.should == 3.5
536
+
537
+ # fails to decrement by float
538
+ @updated = @counter.decrbyfloat(0.5) { |updated| updated == 5 ? 'yep' : nil }
539
+ @updated.should == nil
540
+ @counter.should == 3.5
541
+
542
+ # successfully decrements by float
543
+ @updated = @counter.decrbyfloat(0.5) { |updated| updated == 3 ? 'yep' : nil }
544
+ @updated.should == 'yep'
545
+ @counter.should == 3
546
+
547
+ # fails to decrement
548
+ @updated = @counter.decrement(1) { |updated| updated == 3 ? 'yep' : nil }
549
+ @updated.should == nil
550
+ @counter.should == 3
551
+
552
+ # successfully decrements
553
+ @updated = @counter.decrement(1) { |updated| updated == 2 ? 'yep' : nil }
517
554
  @updated.should == 'yep'
518
555
  @counter.should == 2
556
+
557
+ @counter.value = nil
558
+ @counter.should == 0
519
559
  end
520
560
 
521
561
  it "should support #to_json" do
@@ -615,7 +655,7 @@ describe Redis::Lock do
615
655
  REDIS_HANDLE.get("test_lock").should.be.nil
616
656
  end
617
657
 
618
- it "should not let non-expired locks be gettable" do
658
+ it "should not let blocks execute if they timeout" do
619
659
  expiry = 15
620
660
  lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
621
661
 
@@ -640,19 +680,68 @@ describe Redis::Lock do
640
680
  REDIS_HANDLE.get("test_lock").should.not.be.nil
641
681
  end
642
682
 
683
+ it "should handle a timeout of 0" do
684
+ expiry = 15
685
+ lock = Redis::Lock.new(:test_lock, :timeout => 0)
686
+
687
+ # create a fake lock
688
+ REDIS_HANDLE.set("test_lock", (Time.now + expiry).to_f)
689
+
690
+ gotit = false
691
+ error = nil
692
+ begin
693
+ lock.lock do
694
+ gotit = true
695
+ end
696
+ rescue => error
697
+ end
698
+
699
+ error.should.be.kind_of(Redis::Lock::LockTimeout)
700
+
701
+ # should not have the lock
702
+ gotit.should.not.be.true
703
+
704
+ # lock value should still be set
705
+ REDIS_HANDLE.get("test_lock").should.not.be.nil
706
+ end
707
+
708
+ it "should properly keep the lock across threads" do
709
+ lock = Redis::Lock.new(:test_lock0)
710
+
711
+ t1 = Thread.new do
712
+ lock.lock do
713
+ lock.exists?.should.be.true if RUNNING_LOCALLY
714
+ REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
715
+ sleep 1.0 # hang onto the lock across other thread
716
+ end
717
+ end
718
+
719
+ t2 = Thread.new do
720
+ # check for the lock from another thread
721
+ lock.exists?.should.be.true if RUNNING_LOCALLY
722
+ REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
723
+ end
724
+
725
+ t1.join
726
+ t2.join
727
+
728
+ # lock value should not be set since the lock was held for more than the expiry
729
+ lock.exists?.should.be.false
730
+ REDIS_HANDLE.exists?("test_lock0").should.be.false
731
+ end
732
+
643
733
  it "Redis should remove the key if lock is held past expiration" do
644
- lock = Redis::Lock.new(:test_lock, :expiration => 0.1)
734
+ lock = Redis::Lock.new(:test_lock1, :expiration => 0.1)
645
735
 
646
736
  lock.lock do
647
- REDIS_HANDLE.exists("test_lock").should.be.true
648
- sleep 0.3
649
- # technically undefined behavior because we don't have a BG thread
650
- # running and deleting lock keys - that is only triggered on block exit
651
- #REDIS_HANDLE.exists("test_lock").should.be.false
737
+ lock.exists?.should.be.true
738
+ REDIS_HANDLE.exists?("test_lock1").should.be.true
739
+ sleep 1.0 # hang onto the lock > expiry
652
740
  end
653
741
 
654
742
  # lock value should not be set since the lock was held for more than the expiry
655
- REDIS_HANDLE.exists("test_lock").should.be.false
743
+ lock.exists?.should.be.false
744
+ REDIS_HANDLE.exists?("test_lock1").should.be.false
656
745
  end
657
746
 
658
747
 
@@ -660,9 +749,9 @@ describe Redis::Lock do
660
749
  lock = Redis::Lock.new(:test_lock2, :expiration => 0.1)
661
750
 
662
751
  lock.lock do
663
- REDIS_HANDLE.exists("test_lock2").should.be.true
664
- sleep 0.3 # expired, key is deleted
665
- REDIS_HANDLE.exists("test_lock2").should.be.false
752
+ REDIS_HANDLE.exists?("test_lock2").should.be.true
753
+ sleep 1.0 # expired, key is deleted
754
+ REDIS_HANDLE.exists?("test_lock2").should.be.false
666
755
  REDIS_HANDLE.set("test_lock2", "foo") # this is no longer a lock key, name is a coincidence
667
756
  end
668
757
 
@@ -670,17 +759,17 @@ describe Redis::Lock do
670
759
  end
671
760
 
672
761
  it "should manually delete the key if finished before expiration" do
673
- lock = Redis::Lock.new(:test_lock3, :expiration => 0.5)
762
+ lock = Redis::Lock.new(:test_lock3, :expiration => 10.0)
674
763
 
675
764
  lock.lock do
676
- REDIS_HANDLE.exists("test_lock3").should.be.true
765
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
677
766
  sleep 0.1
678
- REDIS_HANDLE.exists("test_lock3").should.be.true
767
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
679
768
  end
680
769
 
681
- # should delete the key because the lock block is done, regardless of time
770
+ # should delete the key because the lock block is done, regardless of expiry
682
771
  # for some strange reason, I have seen this test fail randomly, which is worrisome.
683
- #REDIS_HANDLE.exists("test_lock3").should.be.false
772
+ REDIS_HANDLE.exists?("test_lock3").should.be.false
684
773
  end
685
774
 
686
775
 
@@ -900,6 +989,13 @@ describe Redis::HashKey do
900
989
  hsh['foo'].should == 'bar'
901
990
  end
902
991
 
992
+ it "should return multiple items via bulk_values" do
993
+ @hash['taco'] = 42
994
+ @hash['burrito'] = 99
995
+ res = @hash.bulk_values('taco', 'burrito')
996
+ res.should == ['42', '99'] # hashes don't convert
997
+ end
998
+
903
999
  it "should increment field" do
904
1000
  @hash.incr('counter')
905
1001
  @hash.incr('counter')
@@ -1007,7 +1103,9 @@ describe Redis::Set do
1007
1103
 
1008
1104
  it "should handle sets of simple values" do
1009
1105
  @set.should.be.empty
1010
- @set << 'a' << 'a' << 'a'
1106
+ @set << 'a'
1107
+ @set.randmember.should == 'a'
1108
+ @set << 'a' << 'a'
1011
1109
  @set.should == ['a']
1012
1110
  @set.to_s.should == 'a'
1013
1111
  @set.get.should == ['a']
@@ -1048,11 +1146,13 @@ describe Redis::Set do
1048
1146
  coll = @set.select{|st| st == 'c'}
1049
1147
  coll.should == ['c']
1050
1148
  @set.sort.should == ['a','b','c']
1149
+ @set.randmember.should.not == 'd'
1051
1150
  @set.delete_if{|m| m == 'c'}
1052
1151
  @set.sort.should == ['a','b']
1053
1152
 
1054
1153
  @set << nil
1055
1154
  @set.include?("").should.be.true
1155
+ @set.sort.should == ['','a','b']
1056
1156
  end
1057
1157
 
1058
1158
  it "should handle empty array adds" do
@@ -1168,6 +1268,12 @@ describe Redis::Set do
1168
1268
  @set_1.value.should == ['a']
1169
1269
  end
1170
1270
 
1271
+ it "should support moving between sets" do
1272
+ @set_1 << 'X' << 'Y' << 'Z'
1273
+ @set_1.move('X', @set_2)
1274
+ @set_2.should == ['X']
1275
+ end
1276
+
1171
1277
  it "should respond to #to_json" do
1172
1278
  @set_1 << 'a'
1173
1279
  JSON.parse(@set_1.to_json)['value'].should == ['a']
@@ -1267,6 +1373,9 @@ describe Redis::SortedSet do
1267
1373
  @set.to_s.should == 'a, b'
1268
1374
  @set.should == ['a','b']
1269
1375
  @set.members.should == ['a','b']
1376
+ @set.member?('a').should == true
1377
+ @set.member?('b').should == true
1378
+ @set.member?('c').should == false
1270
1379
  @set['d'] = 0
1271
1380
 
1272
1381
  @set.rangebyscore(0, 4).should == ['d','a']
@@ -1316,6 +1425,10 @@ describe Redis::SortedSet do
1316
1425
 
1317
1426
  @set.delete_if{|m| m == 'b'}
1318
1427
  @set.size.should == 3
1428
+
1429
+ # this is destructive so must come last
1430
+ res = @set.remrangebyrank(0, 2)
1431
+ res.should == 3
1319
1432
  end
1320
1433
 
1321
1434
  it "should handle inserting multiple values at once" do