redis-objects 1.5.1 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,6 +506,7 @@ 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
@@ -545,6 +553,9 @@ describe Redis::Counter do
545
553
  @updated = @counter.decrement(1) { |updated| updated == 2 ? 'yep' : nil }
546
554
  @updated.should == 'yep'
547
555
  @counter.should == 2
556
+
557
+ @counter.value = nil
558
+ @counter.should == 0
548
559
  end
549
560
 
550
561
  it "should support #to_json" do
@@ -644,7 +655,7 @@ describe Redis::Lock do
644
655
  REDIS_HANDLE.get("test_lock").should.be.nil
645
656
  end
646
657
 
647
- it "should not let non-expired locks be gettable" do
658
+ it "should not let blocks execute if they timeout" do
648
659
  expiry = 15
649
660
  lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
650
661
 
@@ -669,19 +680,68 @@ describe Redis::Lock do
669
680
  REDIS_HANDLE.get("test_lock").should.not.be.nil
670
681
  end
671
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
+
672
733
  it "Redis should remove the key if lock is held past expiration" do
673
- lock = Redis::Lock.new(:test_lock, :expiration => 0.1)
734
+ lock = Redis::Lock.new(:test_lock1, :expiration => 0.1)
674
735
 
675
736
  lock.lock do
676
- REDIS_HANDLE.exists("test_lock").should.be.true
677
- sleep 0.3
678
- # technically undefined behavior because we don't have a BG thread
679
- # running and deleting lock keys - that is only triggered on block exit
680
- #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
681
740
  end
682
741
 
683
742
  # lock value should not be set since the lock was held for more than the expiry
684
- REDIS_HANDLE.exists("test_lock").should.be.false
743
+ lock.exists?.should.be.false
744
+ REDIS_HANDLE.exists?("test_lock1").should.be.false
685
745
  end
686
746
 
687
747
 
@@ -689,9 +749,9 @@ describe Redis::Lock do
689
749
  lock = Redis::Lock.new(:test_lock2, :expiration => 0.1)
690
750
 
691
751
  lock.lock do
692
- REDIS_HANDLE.exists("test_lock2").should.be.true
693
- sleep 0.3 # expired, key is deleted
694
- 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
695
755
  REDIS_HANDLE.set("test_lock2", "foo") # this is no longer a lock key, name is a coincidence
696
756
  end
697
757
 
@@ -699,17 +759,17 @@ describe Redis::Lock do
699
759
  end
700
760
 
701
761
  it "should manually delete the key if finished before expiration" do
702
- lock = Redis::Lock.new(:test_lock3, :expiration => 0.5)
762
+ lock = Redis::Lock.new(:test_lock3, :expiration => 10.0)
703
763
 
704
764
  lock.lock do
705
- REDIS_HANDLE.exists("test_lock3").should.be.true
765
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
706
766
  sleep 0.1
707
- REDIS_HANDLE.exists("test_lock3").should.be.true
767
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
708
768
  end
709
769
 
710
- # 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
711
771
  # for some strange reason, I have seen this test fail randomly, which is worrisome.
712
- #REDIS_HANDLE.exists("test_lock3").should.be.false
772
+ REDIS_HANDLE.exists?("test_lock3").should.be.false
713
773
  end
714
774
 
715
775
 
@@ -929,6 +989,13 @@ describe Redis::HashKey do
929
989
  hsh['foo'].should == 'bar'
930
990
  end
931
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
+
932
999
  it "should increment field" do
933
1000
  @hash.incr('counter')
934
1001
  @hash.incr('counter')
@@ -1036,7 +1103,9 @@ describe Redis::Set do
1036
1103
 
1037
1104
  it "should handle sets of simple values" do
1038
1105
  @set.should.be.empty
1039
- @set << 'a' << 'a' << 'a'
1106
+ @set << 'a'
1107
+ @set.randmember.should == 'a'
1108
+ @set << 'a' << 'a'
1040
1109
  @set.should == ['a']
1041
1110
  @set.to_s.should == 'a'
1042
1111
  @set.get.should == ['a']
@@ -1077,11 +1146,13 @@ describe Redis::Set do
1077
1146
  coll = @set.select{|st| st == 'c'}
1078
1147
  coll.should == ['c']
1079
1148
  @set.sort.should == ['a','b','c']
1149
+ @set.randmember.should.not == 'd'
1080
1150
  @set.delete_if{|m| m == 'c'}
1081
1151
  @set.sort.should == ['a','b']
1082
1152
 
1083
1153
  @set << nil
1084
1154
  @set.include?("").should.be.true
1155
+ @set.sort.should == ['','a','b']
1085
1156
  end
1086
1157
 
1087
1158
  it "should handle empty array adds" do
@@ -1197,6 +1268,12 @@ describe Redis::Set do
1197
1268
  @set_1.value.should == ['a']
1198
1269
  end
1199
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
+
1200
1277
  it "should respond to #to_json" do
1201
1278
  @set_1 << 'a'
1202
1279
  JSON.parse(@set_1.to_json)['value'].should == ['a']
@@ -1296,6 +1373,9 @@ describe Redis::SortedSet do
1296
1373
  @set.to_s.should == 'a, b'
1297
1374
  @set.should == ['a','b']
1298
1375
  @set.members.should == ['a','b']
1376
+ @set.member?('a').should == true
1377
+ @set.member?('b').should == true
1378
+ @set.member?('c').should == false
1299
1379
  @set['d'] = 0
1300
1380
 
1301
1381
  @set.rangebyscore(0, 4).should == ['d','a']
@@ -1345,6 +1425,10 @@ describe Redis::SortedSet do
1345
1425
 
1346
1426
  @set.delete_if{|m| m == 'b'}
1347
1427
  @set.size.should == 3
1428
+
1429
+ # this is destructive so must come last
1430
+ res = @set.remrangebyrank(0, 2)
1431
+ res.should == 3
1348
1432
  end
1349
1433
 
1350
1434
  it "should handle inserting multiple values at once" do
@@ -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
  class Roster
7
8
  include Redis::Objects
@@ -9,16 +10,20 @@ class Roster
9
10
  counter :pitchers, :limit => :max_pitchers
10
11
  counter :basic
11
12
  hash_key :contact_information, :marshal_keys=>{'updated_at'=>true}
12
- lock :resort, :timeout => 2
13
+ redis_lock :resort, :timeout => 2
13
14
  value :starting_pitcher, :marshal => true
14
15
  list :player_stats, :marshal => true
15
16
  set :outfielders, :marshal => true
16
17
  sorted_set :rank
18
+ redis_lock :per_field
17
19
 
18
20
  # global class counters
19
21
  counter :total_players_online, :global => true
20
22
  set :all_players_online, :global => true
21
23
  value :last_player, :global => true
24
+ redis_lock :nasty_global_mutex, :global => true # remember it appends "_lock"
25
+ sorted_set :global_player_leaderboard, :global => true
26
+ hash_key :global_player_online_status, :global => true
22
27
 
23
28
  # custom keys
24
29
  counter :player_totals, :key => 'players/#{username}/total'
@@ -210,6 +215,21 @@ describe Redis::Objects do
210
215
  end
211
216
  end
212
217
 
218
+ it "should handle obtaining / clearing locks" do
219
+ # class-level calls for Admin reasons
220
+ # this is a really weird API and I wonder if anyone actually uses it
221
+ id = 88
222
+ roster = Roster.new(id)
223
+ roster.per_field_lock.exists?.should == false
224
+ Roster.obtain_lock(:per_field, id) do
225
+ roster.per_field_lock.exists?.should == true
226
+ end
227
+ roster.per_field_lock.exists?.should == false
228
+
229
+ Roster.clear_lock(:per_field, id)
230
+ roster.per_field_lock.exists?.should == false
231
+ end
232
+
213
233
  it "should support increment/decrement of counters" do
214
234
  @roster.available_slots.key.should == 'roster:1:available_slots'
215
235
  @roster.available_slots.should == 10
@@ -273,6 +293,26 @@ describe Redis::Objects do
273
293
  Roster.get_counter(:total_players_online).should == 111
274
294
  end
275
295
 
296
+ it "should support class-level manipulation of global objects" do
297
+ Roster.nasty_global_mutex_lock.exists?.should == false
298
+ Roster.nasty_global_mutex_lock do
299
+ Roster.nasty_global_mutex_lock.exists?.should == true
300
+ end
301
+ Roster.nasty_global_mutex_lock.exists?.should == false
302
+
303
+ Roster.global_player_leaderboard.exists?.should == false
304
+ Roster.global_player_leaderboard.add('nate', 22)
305
+ Roster.global_player_leaderboard.add('jim', 11)
306
+ Roster.global_player_leaderboard.rank('nate').should == 1 # 0-based
307
+
308
+ Roster.global_player_online_status.exists?.should == false
309
+ Roster.global_player_online_status['nate'] = 'online'
310
+ Roster.global_player_online_status['jeff'] = 'offline'
311
+ Roster.global_player_online_status['nate'].should == 'online'
312
+ Roster.global_player_online_status['jeff'].should == 'offline'
313
+ Roster.global_player_online_status['bobby'].should.be.nil
314
+ end
315
+
276
316
  it "should take an atomic block for increment/decrement" do
277
317
  a = false
278
318
  @roster.available_slots.should == 10
@@ -431,6 +471,13 @@ describe Redis::Objects do
431
471
  end
432
472
  error.should.be.kind_of(NoMethodError)
433
473
 
474
+ error = nil
475
+ begin
476
+ Roster.increment_counter(:available_slots, nil)
477
+ rescue => error
478
+ end
479
+ error.should.be.kind_of(Redis::Objects::MissingID)
480
+
434
481
  error = nil
435
482
  begin
436
483
  Roster.obtain_lock(:badness, 2){}
@@ -919,9 +966,13 @@ describe Redis::Objects do
919
966
  end
920
967
 
921
968
  it "should pick up class methods from superclass automatically" do
969
+ Roster.redis_prefix.should == 'roster'
922
970
  CounterRoster = Class.new(Roster)
971
+ CounterRoster.redis_prefix.should == 'counter_roster'
923
972
  CounterRoster.counter :extended_counter
924
973
  extended_roster = CounterRoster.new
974
+ Roster.redis_prefix.should == 'roster'
975
+ extended_roster.class.redis_prefix.should == 'counter_roster'
925
976
  extended_roster.basic.should.be.kind_of(Redis::Counter)
926
977
  extended_roster.extended_counter.should.be.kind_of(Redis::Counter)
927
978
  @roster.respond_to?(:extended_counter).should == false
@@ -934,7 +985,7 @@ describe Redis::Objects do
934
985
  @roster.respond_to?(:extended_hash_key).should == false
935
986
 
936
987
  LockRoster = Class.new(Roster)
937
- LockRoster.lock :extended
988
+ LockRoster.redis_lock :extended
938
989
  extended_roster = LockRoster.new
939
990
  extended_roster.resort_lock.should.be.kind_of(Redis::Lock)
940
991
  extended_roster.extended_lock.should.be.kind_of(Redis::Lock)
@@ -1042,4 +1093,10 @@ describe Redis::Objects do
1042
1093
  @roster.redis_instance_keys.include?('roster:1:player_stats').should == true
1043
1094
  @roster.redis_instance_keys.include?('players:all_stats').should == false
1044
1095
  end
1096
+
1097
+ it "should handle per-class connection handles" do
1098
+ redis = Roster.redis
1099
+ Roster.redis = redis
1100
+ Roster.redis.should == redis
1101
+ end
1045
1102
  end
data/spec/spec_helper.rb CHANGED
@@ -9,6 +9,21 @@ if $0 =~ /\brspec$/
9
9
  raise "\n===\nThese tests are in bacon, not rspec. Try: bacon #{ARGV * ' '}\n===\n"
10
10
  end
11
11
 
12
+ # For the incompatible change from redis.rb
13
+ Redis.exists_returns_integer = true
14
+
15
+ # Avoid phantom remote test failures
16
+ RUNNING_LOCALLY = !ENV['TRAVIS']
17
+
18
+ # Code coverage reports
19
+ require 'simplecov'
20
+ SimpleCov.start
21
+
22
+ require 'simplecov-cobertura'
23
+ SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
24
+
25
+ #require "active_support/xml_mini"
26
+ require "active_support"
12
27
  require "active_support/testing/time_helpers"
13
28
  include ActiveSupport::Testing::TimeHelpers
14
29
 
@@ -69,8 +84,6 @@ end
69
84
 
70
85
  # Grab a global handle
71
86
  REDIS_HANDLE = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
72
- #$redis = REDIS_HANDLE
73
- Redis.current = REDIS_HANDLE
74
87
 
75
88
  SORT_ORDER = {:order => 'desc alpha'}
76
89
  SORT_LIMIT = {:limit => [2, 2]}
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 2.0.0.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Wiger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-10 00:00:00.000000000 Z
11
+ date: 2022-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.2'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.2'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: activerecord
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +136,20 @@ dependencies:
122
136
  - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov-cobertura
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
125
153
  description: Map Redis types directly to Ruby objects. Works with any class or ORM.
126
154
  email:
127
155
  - nwiger@gmail.com
@@ -160,6 +188,7 @@ files:
160
188
  - lib/redis/value.rb
161
189
  - redis-objects.gemspec
162
190
  - spec/redis_autoload_objects_spec.rb
191
+ - spec/redis_legacy_key_naming_spec.rb
163
192
  - spec/redis_namespace_compat_spec.rb
164
193
  - spec/redis_objects_active_record_spec.rb
165
194
  - spec/redis_objects_conn_spec.rb
@@ -182,16 +211,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
211
  version: '0'
183
212
  required_rubygems_version: !ruby/object:Gem::Requirement
184
213
  requirements:
185
- - - ">="
214
+ - - ">"
186
215
  - !ruby/object:Gem::Version
187
- version: '0'
216
+ version: 1.3.1
188
217
  requirements: []
189
- rubygems_version: 3.2.15
218
+ rubygems_version: 3.3.7
190
219
  signing_key:
191
220
  specification_version: 4
192
221
  summary: Map Redis types directly to Ruby objects
193
222
  test_files:
194
223
  - spec/redis_autoload_objects_spec.rb
224
+ - spec/redis_legacy_key_naming_spec.rb
195
225
  - spec/redis_namespace_compat_spec.rb
196
226
  - spec/redis_objects_active_record_spec.rb
197
227
  - spec/redis_objects_conn_spec.rb