redis-objects 1.5.1 → 2.0.0.alpha

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.
@@ -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