redis-objects 1.7.0 → 2.0.0.beta

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a34d5bd684e2dfb04c3216b84fc8c0d0ac4b8037919ba6f887bd1930e3eca727
4
- data.tar.gz: e3b4c9df6c48b594850948dd437cec9acecb9a7622102f71d57a4f17068e7fd8
3
+ metadata.gz: 3962b1b4d04b8b94a6d130f2323fe1f93a74cce5546285b011ccbb85293afa5d
4
+ data.tar.gz: cfba25072de6ac2801100f2a11aa0588dc872e0826d88c070327c3be688a9260
5
5
  SHA512:
6
- metadata.gz: 99d33225c4326dec14e62b14e2c0735ac688b7c1d7a86f767d0141fe3a49782e2fc41e02c80cf8c16a79e0c570249e4e3f758e86d1c652688566b4392890abd8
7
- data.tar.gz: d1c6831c0d327fa3f6b2e5e8b3ed45deacdc4373836565084ed16e7492219c7de73e5d79ccc61f0bf28863ad10ef185284c81799369a27fa1cd3f375e426b61c
6
+ metadata.gz: 64a0743cbe2f89145510e00877f2954f6734bdd472c0b272ac165912dffb4ef9d03b395ee7c63bf6a5c06504f6340cf761e69cca0f9fdf5285895b3a544e38b3
7
+ data.tar.gz: eb7cc427ed879586363b84ed1b509052a23e5bf56d7ef8621d5b9fc6d8c4fea3ec20672a1569d018f45530ac131f9e68772213192c6c42baf50beaa7cec977a5
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,13 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
+ == 2.0.0.beta (30 Mar 2023)
4
+
5
+ * Updated internal calls to match `redis-rb`
6
+
7
+ * INCOMPAT: `Redis.current` is no longer allowed due to changes in `redis-rb`
8
+
9
+ * INCOMPAT: The order of items popped off a list by the rarely-used command `list.pop(n)` to specify multiple elements is now reversed to match redis.
10
+
3
11
  == 1.7.0 (29 Apr 2022)
4
12
 
5
13
  * Bumped version to 1.7.0 to revert redis-rb version lock [Nate Wiger]
data/README.md CHANGED
@@ -5,6 +5,56 @@ Redis::Objects - Map Redis types directly to Ruby objects
5
5
  [![Code Coverage](https://codecov.io/gh/nateware/redis-objects/branch/master/graph/badge.svg)](https://codecov.io/gh/nateware/redis-objects)
6
6
  [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MJF7JU5M7F8VL)
7
7
 
8
+ Important 2.0 changes
9
+ =====================
10
+ Redis::Objects 2.0 introduces several important backwards incompatible changes.
11
+ Currently 2.0 can be installed with `gem install redis-objects --pre` or by listing it
12
+ explicitly in your Gemfile:
13
+ ~~~ruby
14
+ # Gemfile
15
+ gem 'redis-objects', '>= 2.0.0.beta'
16
+ ~~~
17
+ You're encouraged to try it out in test code (not production) to ensure it works for you.
18
+ Official release is expected later in 2023.
19
+
20
+ Key Naming Changes
21
+ ------------------
22
+ The internal key naming scheme has changed for `Nested::Class::Namespaces` to fix a longstanding bug.
23
+ **This means your existing data in Redis will not be accessible until you call `migrate_redis_legacy_keys`.**
24
+
25
+ To fix this (only needed once), create a script like this:
26
+
27
+ ~~~ruby
28
+ class YouClassNameHere < ActiveRecord::Base
29
+ include Redis::Objects
30
+ # ... your relevant definitions here ...
31
+ end
32
+
33
+ YourClassName.migrate_redis_legacy_keys
34
+ ~~~
35
+
36
+ Then, you need to find a time when you can temporarily pause writes to your redis server
37
+ so that you can run that script. It uses `redis.scan` internally so it should be able to
38
+ handle a high number of keys. For large data sets, it could take a while.
39
+
40
+ For more details on the issue and fix refer to [#213](https://github.com/nateware/redis-objects/issues/231).
41
+
42
+ Renaming of `lock` Method
43
+ -------------------------
44
+ The `lock` method that collided with `ActiveRecord::Base` has been renamed `redis_lock`.
45
+ This means your classes need to be updated to call `redis_lock` instead:
46
+
47
+ ~~~ruby
48
+ class YouClassNameHere < ActiveRecord::Base
49
+ include Redis::Objects
50
+ redis_lock :mylock # formerly just "lock"
51
+ end
52
+ ~~~
53
+
54
+ For more details on the issue and fix refer to [#196](https://github.com/nateware/redis-objects/issues/196).
55
+
56
+ Overview
57
+ --------
8
58
  This is **not** an ORM. People that are wrapping ORM’s around Redis are missing the point.
9
59
 
10
60
  The killer feature of Redis is that it allows you to perform _atomic_ operations
@@ -119,7 +169,7 @@ Here's an example that integrates several data types with an ActiveRecord model:
119
169
  class Team < ActiveRecord::Base
120
170
  include Redis::Objects
121
171
 
122
- lock :trade_players, :expiration => 15 # sec
172
+ redis_lock :trade_players, :expiration => 15 # sec
123
173
  value :at_bat
124
174
  counter :hits
125
175
  counter :runs
@@ -524,7 +574,7 @@ Locks work similarly. On completion or exception the lock is released:
524
574
 
525
575
  ~~~ruby
526
576
  class Team < ActiveRecord::Base
527
- lock :reorder # declare a lock
577
+ redis_lock :reorder # declare a lock
528
578
  end
529
579
 
530
580
  @team.reorder_lock.lock do
@@ -548,7 +598,7 @@ lock time.
548
598
 
549
599
  ~~~ruby
550
600
  class Team < ActiveRecord::Base
551
- lock :reorder, :expiration => 15.minutes
601
+ redis_lock :reorder, :expiration => 15.minutes
552
602
  end
553
603
  ~~~
554
604
 
@@ -596,5 +646,5 @@ end
596
646
 
597
647
  Author
598
648
  =======
599
- Copyright (c) 2009-2019 [Nate Wiger](http://nateware.com). All Rights Reserved.
649
+ Copyright (c) 2009-2022 [Nate Wiger](http://nateware.com). All Rights Reserved.
600
650
  Released under the [Artistic License](http://www.opensource.org/licenses/artistic-license-2.0.php).
data/lib/redis/list.rb CHANGED
@@ -31,11 +31,7 @@ class Redis
31
31
  # Remove a member from the end of the list. Redis: RPOP
32
32
  def pop(n=nil)
33
33
  if n
34
- result, = redis.multi do
35
- redis.lrange(key, -n, -1)
36
- redis.ltrim(key, 0, -n - 1)
37
- end
38
- unmarshal result
34
+ unmarshal redis.rpop(key, n)
39
35
  else
40
36
  unmarshal redis.rpop(key)
41
37
  end
@@ -65,11 +61,7 @@ class Redis
65
61
  # Remove a member from the start of the list. Redis: LPOP
66
62
  def shift(n=nil)
67
63
  if n
68
- result, = redis.multi do
69
- redis.lrange(key, 0, n - 1)
70
- redis.ltrim(key, n, -1)
71
- end
72
- unmarshal result
64
+ unmarshal redis.lpop(key, n)
73
65
  else
74
66
  unmarshal redis.lpop(key)
75
67
  end
@@ -14,7 +14,7 @@ class Redis
14
14
  module ClassMethods
15
15
  # Define a new lock. It will function like a model attribute,
16
16
  # so it can be used alongside ActiveRecord/DataMapper, etc.
17
- def lock(name, options={})
17
+ def redis_lock(name, options={})
18
18
  options[:timeout] ||= 5 # seconds
19
19
  lock_name = "#{name}_lock"
20
20
  redis_objects[lock_name.to_sym] = options.merge(:type => :lock)
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  module Objects
3
- VERSION = "1.7.0"
3
+ VERSION = "2.0.0.beta"
4
4
  end
5
5
  end
data/lib/redis/objects.rb CHANGED
@@ -62,7 +62,7 @@ class Redis
62
62
  @redis = Objects::ConnectionPoolProxy.proxy_if_needed(conn)
63
63
  end
64
64
  def redis
65
- @redis || $redis || Redis.current ||
65
+ @redis || $redis ||
66
66
  raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
67
67
  end
68
68
 
@@ -101,16 +101,93 @@ class Redis
101
101
  @redis_objects ||= {}
102
102
  end
103
103
 
104
- # Set the Redis redis_prefix to use. Defaults to model_name
105
- def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end
104
+ # Toggles whether to use the legacy redis key naming scheme, which causes
105
+ # naming conflicts in certain cases.
106
+ attr_accessor :redis_legacy_naming
107
+ attr_accessor :redis_silence_warnings
108
+
109
+ # Set the Redis redis_prefix to use. Defaults to class_name.
110
+ def redis_prefix=(redis_prefix)
111
+ @silence_warnings_as_redis_prefix_was_set_manually = true
112
+ @redis_prefix = redis_prefix
113
+ end
114
+
106
115
  def redis_prefix(klass = self) #:nodoc:
107
- @redis_prefix ||= klass.name.to_s.
108
- sub(%r{(.*::)}, '').
109
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
110
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
116
+ @redis_prefix ||=
117
+ if redis_legacy_naming
118
+ redis_legacy_prefix(klass)
119
+ else
120
+ redis_legacy_naming_warning_message(klass)
121
+ redis_modern_prefix(klass)
122
+ end
123
+
124
+ @redis_prefix
125
+ end
126
+
127
+ def redis_modern_prefix(klass = self) #:nodoc:
128
+ klass.name.to_s.
129
+ gsub(/::/, '__'). # Nested::Class => Nested__Class
130
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
131
+ gsub(/([a-z\d])([A-Z])/,'\1_\2'). # className => class_Name
132
+ downcase
133
+ end
134
+
135
+ def redis_legacy_prefix(klass = self) #:nodoc:
136
+ klass.name.to_s.
137
+ sub(%r{(.*::)}, ''). # Nested::Class => Class (problematic)
138
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
139
+ gsub(/([a-z\d])([A-Z])/,'\1_\2'). # className => class_Name
111
140
  downcase
112
141
  end
113
142
 
143
+ # Temporary warning to help with migrating key names
144
+ def redis_legacy_naming_warning_message(klass)
145
+ # warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
146
+ unless redis_legacy_naming || redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually
147
+ modern = redis_modern_prefix(klass)
148
+ legacy = redis_legacy_prefix(klass)
149
+ if modern != legacy
150
+ warn <<EOW
151
+ [redis-objects] WARNING: In redis-objects 2.0.0, key naming will change to fix longstanding bugs.
152
+ [redis-objects] Your class #{klass.name.to_s} will be affected by this change!
153
+ [redis-objects] Current key prefix: #{legacy.inspect}
154
+ [redis-objects] Future key prefix: #{modern.inspect}
155
+ [redis-objects] Read more at https://github.com/nateware/redis-objects/issues/231
156
+ EOW
157
+ end
158
+ end
159
+ end
160
+
161
+ def migrate_redis_legacy_keys
162
+ cursor = 0
163
+ legacy = redis_legacy_prefix
164
+ total_keys = 0
165
+ if legacy == redis_prefix
166
+ raise "Failed to migrate keys for #{self.name.to_s} as legacy and new redis_prefix are the same (#{redis_prefix})"
167
+ end
168
+ warn "[redis-objects] Migrating keys from #{legacy} prefix to #{redis_prefix}"
169
+
170
+ loop do
171
+ cursor, keys = redis.scan(cursor, :match => "#{legacy}:*")
172
+ total_keys += keys.length
173
+ keys.each do |key|
174
+ # Split key name apart on ':'
175
+ base_class, id, name = key.split(':')
176
+
177
+ # Figure out the new name
178
+ new_key = redis_field_key(name, id=id, context=self)
179
+
180
+ # Rename the key
181
+ warn "[redis-objects] Rename '#{key}', '#{new_key}'"
182
+ ok = redis.rename(key, new_key)
183
+ warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
184
+ end
185
+ break if cursor == "0"
186
+ end
187
+
188
+ warn "[redis-objects] Migrated #{total_keys} total number of redis keys"
189
+ end
190
+
114
191
  def redis_options(name)
115
192
  klass = first_ancestor_with(name)
116
193
  return klass.redis_objects[name.to_sym] || {}
data/lib/redis/set.rb CHANGED
@@ -15,7 +15,8 @@ class Redis
15
15
  # Redis: SADD
16
16
  def add(value)
17
17
  allow_expiration do
18
- redis.sadd(key, marshal(value)) if value.nil? || !Array(value).empty?
18
+ value = '' if value.nil?
19
+ redis.sadd(key, marshal(value)) if !Array(value).empty? # allow empty adds
19
20
  end
20
21
  end
21
22
 
@@ -211,7 +211,7 @@ class Redis
211
211
  result = redis.zrange(temp_key, 0, -1)
212
212
  end
213
213
 
214
- result.value
214
+ result
215
215
  end
216
216
  alias_method :intersect, :intersection
217
217
  alias_method :inter, :intersection
@@ -248,7 +248,7 @@ class Redis
248
248
  result = redis.zrange(temp_key, 0, -1)
249
249
  end
250
250
 
251
- result.value
251
+ result
252
252
  end
253
253
  alias_method :|, :union
254
254
  alias_method :+, :union
@@ -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"
22
+ spec.add_dependency "redis", '~> 5.0'
23
23
 
24
24
  # Ignore gemspec warnings on these. Trying to fix them to versions breaks TravisCI
25
25
  spec.add_development_dependency "bundler"
@@ -0,0 +1,419 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'redis/objects'
4
+ Redis::Objects.redis = REDIS_HANDLE
5
+
6
+ require 'securerandom'
7
+
8
+ require "stringio"
9
+
10
+ def capture_stderr
11
+ # The output stream must be an IO-like object. In this case we capture it in
12
+ # an in-memory IO object so we can return the string value. You can assign any
13
+ # IO object here.
14
+ previous_stderr, $stderr = $stderr, StringIO.new
15
+ yield
16
+ $stderr.string
17
+ ensure
18
+ # Restore the previous value of stderr (typically equal to STDERR).
19
+ $stderr = previous_stderr
20
+ end
21
+
22
+ describe 'Legacy redis key prefix naming compatibility' do
23
+ it 'verifies single level classes work the same' do
24
+ class SingleLevelOne
25
+ include Redis::Objects
26
+
27
+ def id
28
+ 1
29
+ end
30
+ end
31
+
32
+ obj = SingleLevelOne.new
33
+ obj.class.redis_prefix.should == 'single_level_one'
34
+ end
35
+
36
+ it 'verifies single level classes obey the legacy naming flag' do
37
+ class SingleLevelTwo
38
+ include Redis::Objects
39
+ self.redis_legacy_naming = true
40
+
41
+ def id
42
+ 1
43
+ end
44
+ end
45
+
46
+ obj = SingleLevelTwo.new
47
+ obj.class.redis_prefix.should == 'single_level_two'
48
+ end
49
+
50
+ it 'verifies nested classes do NOT work the same' do
51
+ module Nested
52
+ class NamingOne
53
+ include Redis::Objects
54
+ self.redis_silence_warnings = true
55
+
56
+ def id
57
+ 1
58
+ end
59
+ end
60
+ end
61
+
62
+ obj = Nested::NamingOne.new
63
+ obj.class.redis_prefix.should == 'nested__naming_one'
64
+ end
65
+
66
+ it 'verifies the legacy naming flag is respected' do
67
+ module Nested
68
+ class NamingTwo
69
+ include Redis::Objects
70
+ self.redis_legacy_naming = true
71
+ self.redis_silence_warnings = true
72
+
73
+ def id
74
+ 1
75
+ end
76
+ end
77
+ end
78
+
79
+ Nested::NamingTwo.redis_legacy_naming.should == true
80
+ obj = Nested::NamingTwo.new
81
+ obj.class.redis_prefix.should == 'naming_two'
82
+ end
83
+
84
+ it 'verifies that multiple levels respect __ vs _' do
85
+ module NestedLevel
86
+ module Further
87
+ class NamingThree
88
+ include Redis::Objects
89
+ self.redis_silence_warnings = true
90
+
91
+ def id
92
+ 1
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ obj = NestedLevel::Further::NamingThree.new
99
+ obj.class.redis_prefix.should == 'nested_level__further__naming_three'
100
+ end
101
+
102
+ it 'verifies that multiple levels respect the legacy naming' do
103
+ module NestedLevel
104
+ module Further
105
+ class NamingFour
106
+ include Redis::Objects
107
+ self.redis_legacy_naming = true
108
+
109
+ def id
110
+ 1
111
+ end
112
+
113
+ redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31)
114
+ value :redis_value, :redis => redis_handle
115
+ end
116
+ end
117
+ end
118
+
119
+ NestedLevel::Further::NamingFour.redis_legacy_naming.should == true
120
+ obj = NestedLevel::Further::NamingFour.new
121
+ obj.class.redis_prefix.should == 'naming_four'
122
+ val = SecureRandom.hex(10)
123
+ obj.redis_value = val
124
+ obj.redis_value.should == val
125
+ obj.redis_value.key.should == 'naming_four:1:redis_value'
126
+ end
127
+
128
+ it 'verifies that multiple levels do not conflict 1' do
129
+ module NestedLevel
130
+ module Further
131
+ class NamingFive
132
+ include Redis::Objects
133
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
134
+ self.redis_silence_warnings = true
135
+
136
+ def id
137
+ 1
138
+ end
139
+
140
+ value :redis_value
141
+ end
142
+ end
143
+ end
144
+
145
+ obj = NestedLevel::Further::NamingFive.new
146
+ obj.class.redis_prefix.should == 'nested_level__further__naming_five'
147
+ val = SecureRandom.hex(10)
148
+ obj.redis_value = val
149
+ obj.redis_value.should == val
150
+ obj.redis_value.key.should == 'nested_level__further__naming_five:1:redis_value'
151
+ obj.redis_value.redis.should == obj.redis
152
+ obj.redis.get('nested_level__further__naming_five:1:redis_value').should == val
153
+ end
154
+
155
+ it 'verifies that multiple levels do not conflict 2' do
156
+ module Nested
157
+ module LevelFurtherNaming
158
+ class Five
159
+ include Redis::Objects
160
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
161
+ self.redis_silence_warnings = true
162
+
163
+ def id
164
+ 1
165
+ end
166
+
167
+ value :redis_value
168
+ end
169
+ end
170
+ end
171
+
172
+ obj = Nested::LevelFurtherNaming::Five.new
173
+ obj.class.redis_prefix.should == 'nested__level_further_naming__five'
174
+ val = SecureRandom.hex(10)
175
+ obj.redis_value = val
176
+ obj.redis_value.should == val
177
+ obj.redis_value.key.should == 'nested__level_further_naming__five:1:redis_value'
178
+ obj.redis.get('nested__level_further_naming__five:1:redis_value').should == val
179
+ end
180
+
181
+ it 'verifies that multiple levels do not conflict 3' do
182
+ module Nested
183
+ module LevelFurther
184
+ class NamingFive
185
+ include Redis::Objects
186
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
187
+ self.redis_silence_warnings = true
188
+
189
+ def id
190
+ 1
191
+ end
192
+
193
+ value :redis_value
194
+ end
195
+ end
196
+ end
197
+
198
+ obj = Nested::LevelFurther::NamingFive.new
199
+ obj.class.redis_prefix.should == 'nested__level_further__naming_five'
200
+ val = SecureRandom.hex(10)
201
+ obj.redis_value = val
202
+ obj.redis_value.should == val
203
+ obj.redis_value.key.should == 'nested__level_further__naming_five:1:redis_value'
204
+ obj.redis.get('nested__level_further__naming_five:1:redis_value').should == val
205
+ end
206
+
207
+ it 'handles dynamically created classes correctly' do
208
+ module Nested
209
+ class LevelSix
210
+ include Redis::Objects
211
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
212
+ self.redis_silence_warnings = true
213
+
214
+ def id
215
+ 1
216
+ end
217
+
218
+ value :redis_value
219
+ end
220
+ end
221
+
222
+ obj = Nested::LevelSix.new
223
+ obj.class.redis_prefix.should == 'nested__level_six'
224
+ val = SecureRandom.hex(10)
225
+ obj.redis_value = val
226
+ obj.redis_value.should == val
227
+ obj.redis_value.key.should == 'nested__level_six:1:redis_value'
228
+ obj.redis.get('nested__level_six:1:redis_value').should == val
229
+
230
+ DynamicClass = Class.new(Nested::LevelSix)
231
+ DynamicClass.value :redis_value2
232
+ obj2 = DynamicClass.new
233
+ DynamicClass.redis_prefix.should == 'dynamic_class'
234
+ obj2.redis_value.should.be.kind_of(Redis::Value)
235
+ obj2.redis_value2.should.be.kind_of(Redis::Value)
236
+ obj2.redis_value.key.should == 'dynamic_class:1:redis_value'
237
+ obj2.redis_value2.key.should == 'dynamic_class:1:redis_value2'
238
+
239
+ end
240
+
241
+ it 'handles dynamically created classes correctly in legacy mode' do
242
+ module Nested
243
+ class LevelSeven
244
+ include Redis::Objects
245
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
246
+ self.redis_legacy_naming = true
247
+
248
+ def id
249
+ 1
250
+ end
251
+
252
+ value :redis_value
253
+ end
254
+ end
255
+
256
+ obj = Nested::LevelSeven.new
257
+ obj.class.redis_prefix.should == 'level_seven'
258
+ val = SecureRandom.hex(10)
259
+ obj.redis_value = val
260
+ obj.redis_value.should == val
261
+ obj.redis_value.key.should == 'level_seven:1:redis_value'
262
+ obj.redis.get('level_seven:1:redis_value').should == val
263
+
264
+ DynamicClass2 = Class.new(Nested::LevelSeven)
265
+ DynamicClass2.value :redis_value2
266
+ obj2 = DynamicClass2.new
267
+ DynamicClass2.redis_prefix.should == 'dynamic_class2'
268
+ obj2.redis_value.should.be.kind_of(Redis::Value)
269
+ obj2.redis_value2.should.be.kind_of(Redis::Value)
270
+ obj2.redis_value.key.should == 'dynamic_class2:1:redis_value'
271
+ obj2.redis_value2.key.should == 'dynamic_class2:1:redis_value2'
272
+ end
273
+
274
+ it 'prints a warning message if the key name changes' do
275
+ module Nested
276
+ class LevelNine
277
+ include Redis::Objects
278
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
279
+
280
+ def id
281
+ 1
282
+ end
283
+
284
+ value :redis_value
285
+ end
286
+ end
287
+
288
+ captured_output = capture_stderr do
289
+ # Does not output anything directly.
290
+ obj = Nested::LevelNine.new
291
+ val = SecureRandom.hex(10)
292
+ obj.redis_value = val
293
+ obj.redis_value.should == val
294
+ end
295
+
296
+ captured_output.should =~ /Warning:/i
297
+ end
298
+
299
+ it 'supports a method to migrate legacy key names' do
300
+ module Nested
301
+ class Legacy
302
+ include Redis::Objects
303
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
304
+ self.redis_legacy_naming = true
305
+
306
+ # override this for testing - need two classes as if we imagine an old and new one
307
+ # also use the legacy flat prefix that ignores the nested class name
308
+ self.redis_prefix = 'modern'
309
+
310
+ def initialize(id)
311
+ @id = id
312
+ end
313
+ def id
314
+ @id
315
+ end
316
+
317
+ value :redis_value
318
+ counter :redis_counter
319
+ hash_key :redis_hash
320
+ list :redis_list
321
+ set :redis_set
322
+ sorted_set :redis_sorted_set
323
+
324
+ # global class counters
325
+ value :global_value, :global => true
326
+ counter :global_counter, :global => true
327
+ hash_key :global_hash_key, :global => true
328
+ list :global_list, :global => true
329
+ set :global_set, :global => true
330
+ sorted_set :global_sorted_set, :global => true
331
+
332
+ #callable as key
333
+ value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
334
+ end
335
+ end
336
+
337
+ module Nested
338
+ class Modern
339
+ include Redis::Objects
340
+ self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
341
+
342
+ def initialize(id)
343
+ @id = id
344
+ end
345
+ def id
346
+ @id
347
+ end
348
+
349
+ value :redis_value
350
+ counter :redis_counter
351
+ hash_key :redis_hash
352
+ list :redis_list
353
+ set :redis_set
354
+ sorted_set :redis_sorted_set
355
+
356
+ # global class counters
357
+ value :global_value, :global => true
358
+ counter :global_counter, :global => true
359
+ hash_key :global_hash_key, :global => true
360
+ list :global_list, :global => true
361
+ set :global_set, :global => true
362
+ sorted_set :global_sorted_set, :global => true
363
+
364
+ #callable as key
365
+ value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
366
+ end
367
+ end
368
+
369
+ # Iterate over them
370
+ Nested::Modern.redis_objects.length.should == 13
371
+ Nested::Modern.redis_objects.length.should == Nested::Legacy.redis_objects.length.should
372
+ Nested::Legacy.redis_prefix.should == 'modern'
373
+ Nested::Modern.redis_prefix.should == 'nested__modern'
374
+
375
+ # Create a whole bunch of keys using the legacy names
376
+ 30.times do |i|
377
+ # warn i.inspect
378
+ obj = Nested::Legacy.new(i)
379
+ obj.redis_value = i
380
+ obj.redis_value.to_i.should == i
381
+ obj.redis_counter.increment
382
+ obj.redis_hash[:key] = i
383
+ obj.redis_list << i
384
+ obj.redis_set << i
385
+ obj.redis_sorted_set[i] = i
386
+ end
387
+
388
+ obj = Nested::Legacy.new(99)
389
+ obj.global_value = 42
390
+ obj.global_counter.increment
391
+ obj.global_counter.increment
392
+ obj.global_counter.increment
393
+ obj.global_hash_key[:key] = 'value'
394
+ obj.global_set << 'a' << 'b'
395
+ obj.global_sorted_set[:key] = 2.2
396
+
397
+ Nested::Modern.migrate_redis_legacy_keys
398
+
399
+ # Try to access the keys through modern names now
400
+ 30.times do |i|
401
+ # warn i.inspect
402
+ obj = Nested::Modern.new(i)
403
+ obj.redis_value.to_i.should == i
404
+ obj.redis_counter.to_i.should == 1
405
+ obj.redis_hash[:key].to_i.should == i
406
+ obj.redis_list[0].to_i.should == i
407
+ obj.redis_set.include?(i).should == true
408
+ obj.redis_sorted_set[i].should == i
409
+ end
410
+
411
+ obj = Nested::Modern.new(99)
412
+ obj.global_value.to_i.should == 42
413
+ obj.global_counter.to_i.should == 3
414
+ obj.global_hash_key[:key].should == 'value'
415
+ obj.global_set.should.include?('a').should == true
416
+ obj.global_set.should.include?('b').should == true
417
+ obj.global_sorted_set[:key].should == 2.2
418
+ end
419
+ end
@@ -34,8 +34,8 @@ describe 'Connection tests' do
34
34
  obj.redis_value.value.should == nil
35
35
 
36
36
  obj.default_redis_value.clear
37
- obj.redis_value.value = 'foo'
38
- obj.redis_value.value.should == 'foo'
37
+ obj.redis_value.value = 'bar'
38
+ obj.redis_value.value.should == 'bar'
39
39
  obj.default_redis_value.value.should == nil
40
40
 
41
41
  obj.redis_value.clear
@@ -96,14 +96,9 @@ describe 'Connection tests' do
96
96
  end
97
97
 
98
98
  it "should support local handles with a vanilla redis connection" do
99
- # Redis.current = nil # reset from other tests
100
99
  Redis::Objects.redis = nil
101
100
  @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
102
-
103
- # Redis.current is lazily auto-populated to touch 6379
104
- # This why we choose the weird 9212 port to avoid
105
- # Redis.current.inspect.should == Redis.new.inspect
106
- Redis::Objects.redis.inspect.should == Redis.new.inspect
101
+ raises_exception{ Redis::Objects.redis.inspect } # NotConnected
107
102
 
108
103
  v = Redis::Value.new('conn/value', @redis_handle)
109
104
  v.clear
@@ -141,14 +136,9 @@ describe 'Connection tests' do
141
136
  end
142
137
 
143
138
  it "should support local handles with a connection_pool" do
144
- # Redis.current = nil # reset from other tests
145
139
  Redis::Objects.redis = nil
146
140
  @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
147
-
148
- # Redis.current is lazily auto-populated to touch 6379
149
- # This why we choose the weird 9212 port to avoid
150
- # Redis.current.inspect.should == Redis.new.inspect
151
- Redis::Objects.redis.inspect.should == Redis.new.inspect
141
+ raises_exception{ Redis::Objects.redis.inspect } # NotConnected
152
142
 
153
143
  v = Redis::Value.new('conn/value', @redis_handle)
154
144
  v.clear
@@ -148,7 +148,7 @@ describe Redis::Value do
148
148
  end
149
149
 
150
150
  it "#{meth} should set expiration when expireat option assigned" do
151
- @value = Redis::Value.new('spec/value', :expireat => Time.now + 10.seconds)
151
+ @value = Redis::Value.new('spec/value', :expireat => Time.now + 10)
152
152
  @value.send(meth, 'monkey')
153
153
  @value.ttl.should > 0
154
154
  end
@@ -286,18 +286,28 @@ describe Redis::List do
286
286
  @list.get.should == ['a','c','f','j','h','i','a']
287
287
  end
288
288
 
289
- it "should support popping & shifting multiple values" do
289
+ it "should support popping and shifting multiple values" do
290
290
  @list.should.be.empty
291
291
 
292
- @list << 'a' << 'b' << 'c'
293
- @list.shift(2).should == ['a', 'b']
294
- @list.shift(2).should == ['c']
295
- @list.shift(2).should == []
292
+ @list << 'a' << 'b' << 'c' << 'd'
293
+ @list.should == ['a', 'b', 'c', 'd']
294
+ @list.shift
295
+ @list.should == ['b', 'c', 'd']
296
+ @list.shift(2).should == ['b', 'c']
297
+ @list.should == ['d']
298
+ @list.shift(2).should == ['d']
299
+ @list.shift(2).should == nil
296
300
 
297
- @list << 'a' << 'b' << 'c'
298
- @list.pop(2).should == ['b', 'c']
299
- @list.pop(2).should == ['a']
300
- @list.pop(2).should == []
301
+ @list << 'e' << 'f' << 'g'
302
+
303
+ # Old behavior
304
+ # @list.pop(2).should == ['f', 'g']
305
+
306
+ # New behavior
307
+ @list.pop(2).should == ['g', 'f']
308
+
309
+ @list.pop(2).should == ['e']
310
+ @list.pop(2).should == nil
301
311
  end
302
312
 
303
313
  it "should handle rpoplpush" do
@@ -412,7 +422,7 @@ describe Redis::List do
412
422
  end
413
423
 
414
424
  it "#{meth} expireat: option" do
415
- @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
425
+ @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10)
416
426
  @list.clear
417
427
  @list.send(meth, 'val')
418
428
  @list.ttl.should > 0
@@ -430,7 +440,7 @@ describe Redis::List do
430
440
  end
431
441
 
432
442
  it "[]= expireat: option" do
433
- @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
443
+ @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10)
434
444
  @list.clear
435
445
  @list.redis.rpush(@list.key, 'hello')
436
446
  @list[0] = 'world'
@@ -448,7 +458,7 @@ describe Redis::List do
448
458
  end
449
459
 
450
460
  it "insert expireat: option" do
451
- @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10.seconds)
461
+ @list = Redis::List.new('spec/list_exp', :expireat => Time.now + 10)
452
462
  @list.clear
453
463
  @list.redis.rpush(@list.key, 'hello')
454
464
  @list.insert 'BEFORE', 'hello', 'world'
@@ -586,7 +596,7 @@ describe Redis::Counter do
586
596
  @counter.ttl.should <= 10
587
597
  end
588
598
  it "expireat: option" do
589
- @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
599
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10)
590
600
  @counter.send(meth)
591
601
  @counter.ttl.should > 0
592
602
  @counter.ttl.should <= 10
@@ -600,14 +610,14 @@ describe Redis::Counter do
600
610
  [:set, :value=].each do |meth|
601
611
  describe meth do
602
612
  it "expiration: option" do
603
- @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
613
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10)
604
614
  @counter.send(meth, 99)
605
615
  @counter.should == 99
606
616
  @counter.ttl.should > 0
607
617
  @counter.ttl.should <= 10
608
618
  end
609
619
  it "expireat: option" do
610
- @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10.seconds)
620
+ @counter = Redis::Counter.new('spec/counter_exp', :expireat => Time.now + 10)
611
621
  @counter.send(meth, 99)
612
622
  @counter.should == 99
613
623
  @counter.ttl.should > 0
@@ -805,22 +815,25 @@ end
805
815
  describe Redis::HashKey do
806
816
  describe "With Marshal" do
807
817
  before do
808
- @hash = Redis::HashKey.new('test_hash', {:marshal_keys=>{'created_at'=>true}})
818
+ @hash = Redis::HashKey.new('test_hash', {:marshal_keys=>{'created_at' => true}})
809
819
  @hash.clear
810
820
  end
811
821
 
812
822
  it "should marshal specified keys" do
813
- @hash['created_at'] = Time.now
823
+ time = Time.now
824
+ @hash['created_at'] = time
825
+ @hash['created_at'].should == time
814
826
  @hash['created_at'].class.should == Time
815
827
  end
816
828
 
817
829
  it "should not marshal unless required" do
818
- @hash['updated_at'] = Time.now
830
+ @hash['updated_at'] = 10
831
+ @hash['updated_at'].should == "10"
819
832
  @hash['updated_at'].class.should == String
820
833
  end
821
834
 
822
835
  it "should marshall appropriate key with bulk set and get" do
823
- @hash.bulk_set({'created_at'=>Time.now, 'updated_at'=>Time.now})
836
+ @hash.bulk_set({'created_at' => Time.now, 'updated_at' => 11})
824
837
 
825
838
  @hash['created_at'].class.should == Time
826
839
  @hash['updated_at'].class.should == String
@@ -849,7 +862,7 @@ describe Redis::HashKey do
849
862
  # no marshaling
850
863
  @hash.options[:marshal] = false
851
864
  v = {:json => 'data'}
852
- @hash['abc'] = v
865
+ @hash['abc'] = v.to_s
853
866
  @hash['abc'].should == v.to_s
854
867
 
855
868
  @hash.options[:marshal] = true
@@ -1075,7 +1088,7 @@ describe Redis::HashKey do
1075
1088
  end
1076
1089
 
1077
1090
  it "#{meth} expireat: option" do
1078
- @hash = Redis::HashKey.new('spec/hash_expireat', :expireat => Time.now + 10.seconds)
1091
+ @hash = Redis::HashKey.new('spec/hash_expireat', :expireat => Time.now + 10)
1079
1092
  @hash.clear
1080
1093
  @hash.send(meth, *args)
1081
1094
  @hash.ttl.should > 0
@@ -1294,7 +1307,7 @@ describe Redis::Set do
1294
1307
  end
1295
1308
 
1296
1309
  it "should set expiration when expireat option assigned" do
1297
- @set = Redis::Set.new('spec/set', :expireat => Time.now + 10.seconds)
1310
+ @set = Redis::Set.new('spec/set', :expireat => Time.now + 10)
1298
1311
  @set.send(meth, 'val')
1299
1312
  @set.ttl.should > 0
1300
1313
  @set.ttl.should <= 10
@@ -1558,7 +1571,7 @@ describe Redis::SortedSet do
1558
1571
  end
1559
1572
 
1560
1573
  it 'should set expiration when expireat option assigned' do
1561
- @set = Redis::SortedSet.new('spec/zset', :expireat => Time.now + 10.seconds)
1574
+ @set = Redis::SortedSet.new('spec/zset', :expireat => Time.now + 10)
1562
1575
  @set['val'] = 1
1563
1576
  @set.ttl.should > 0
1564
1577
  @set.ttl.should <= 10
@@ -1590,7 +1603,7 @@ describe Redis::SortedSet do
1590
1603
  @set.ttl.should <= 10
1591
1604
  end
1592
1605
  it "#{meth} expireat: option" do
1593
- @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
1606
+ @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10)
1594
1607
  @set.clear
1595
1608
  @set.send(meth, 'somekey', 12)
1596
1609
  @set.ttl.should > 0
@@ -1607,7 +1620,7 @@ describe Redis::SortedSet do
1607
1620
  @set.ttl.should <= 10
1608
1621
  end
1609
1622
  it "#{meth} expireat: option" do
1610
- @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
1623
+ @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10)
1611
1624
  @set.clear
1612
1625
  @set.send(meth, 'somekey' => 12)
1613
1626
  @set.ttl.should > 0
@@ -1626,7 +1639,7 @@ describe Redis::SortedSet do
1626
1639
  end
1627
1640
 
1628
1641
  it "#{meth} expireat: option" do
1629
- @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
1642
+ @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10)
1630
1643
  @set.clear
1631
1644
  @set.redis.zadd(@set.key, 1, "1")
1632
1645
  @set.send(meth, 'sets', Redis::SortedSet.new('other'))
@@ -1646,7 +1659,7 @@ describe Redis::SortedSet do
1646
1659
  end
1647
1660
 
1648
1661
  it "delete expireat: option" do
1649
- @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10.seconds)
1662
+ @set = Redis::SortedSet.new('spec/zset_exp', :expireat => Time.now + 10)
1650
1663
  @set.clear
1651
1664
  @set.redis.zadd(@set.key, 1, "1")
1652
1665
  @set.redis.zadd(@set.key, 2, "2")
@@ -10,18 +10,18 @@ class Roster
10
10
  counter :pitchers, :limit => :max_pitchers
11
11
  counter :basic
12
12
  hash_key :contact_information, :marshal_keys=>{'updated_at'=>true}
13
- lock :resort, :timeout => 2
13
+ redis_lock :resort, :timeout => 2
14
14
  value :starting_pitcher, :marshal => true
15
15
  list :player_stats, :marshal => true
16
16
  set :outfielders, :marshal => true
17
17
  sorted_set :rank
18
- lock :per_field
18
+ redis_lock :per_field
19
19
 
20
20
  # global class counters
21
21
  counter :total_players_online, :global => true
22
22
  set :all_players_online, :global => true
23
23
  value :last_player, :global => true
24
- lock :nasty_global_mutex, :global => true # remember it appends "_lock"
24
+ redis_lock :nasty_global_mutex, :global => true # remember it appends "_lock"
25
25
  sorted_set :global_player_leaderboard, :global => true
26
26
  hash_key :global_player_online_status, :global => true
27
27
 
@@ -966,9 +966,13 @@ describe Redis::Objects do
966
966
  end
967
967
 
968
968
  it "should pick up class methods from superclass automatically" do
969
+ Roster.redis_prefix.should == 'roster'
969
970
  CounterRoster = Class.new(Roster)
971
+ CounterRoster.redis_prefix.should == 'counter_roster'
970
972
  CounterRoster.counter :extended_counter
971
973
  extended_roster = CounterRoster.new
974
+ Roster.redis_prefix.should == 'roster'
975
+ extended_roster.class.redis_prefix.should == 'counter_roster'
972
976
  extended_roster.basic.should.be.kind_of(Redis::Counter)
973
977
  extended_roster.extended_counter.should.be.kind_of(Redis::Counter)
974
978
  @roster.respond_to?(:extended_counter).should == false
@@ -981,7 +985,7 @@ describe Redis::Objects do
981
985
  @roster.respond_to?(:extended_hash_key).should == false
982
986
 
983
987
  LockRoster = Class.new(Roster)
984
- LockRoster.lock :extended
988
+ LockRoster.redis_lock :extended
985
989
  extended_roster = LockRoster.new
986
990
  extended_roster.resort_lock.should.be.kind_of(Redis::Lock)
987
991
  extended_roster.extended_lock.should.be.kind_of(Redis::Lock)
data/spec/spec_helper.rb CHANGED
@@ -10,7 +10,7 @@ if $0 =~ /\brspec$/
10
10
  end
11
11
 
12
12
  # For the incompatible change from redis.rb
13
- Redis.exists_returns_integer = true
13
+ # Redis.exists_returns_integer = true
14
14
 
15
15
  # Avoid phantom remote test failures
16
16
  RUNNING_LOCALLY = !ENV['TRAVIS']
@@ -61,7 +61,7 @@ def kill_redis
61
61
  Process.kill "TERM", pid
62
62
  Process.kill "KILL", pid
63
63
  File.unlink pidfile
64
- File.unlink rdbfile if File.exists? rdbfile
64
+ File.unlink rdbfile if File.exist? rdbfile
65
65
  end
66
66
 
67
67
  # Start redis-server except under JRuby
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.7.0
4
+ version: 2.0.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Wiger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-22 00:00:00.000000000 Z
11
+ date: 2023-03-30 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: '0'
19
+ version: '5.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: '0'
26
+ version: '5.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -188,6 +188,7 @@ files:
188
188
  - lib/redis/value.rb
189
189
  - redis-objects.gemspec
190
190
  - spec/redis_autoload_objects_spec.rb
191
+ - spec/redis_legacy_key_naming_spec.rb
191
192
  - spec/redis_namespace_compat_spec.rb
192
193
  - spec/redis_objects_active_record_spec.rb
193
194
  - spec/redis_objects_conn_spec.rb
@@ -210,16 +211,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
210
211
  version: '0'
211
212
  required_rubygems_version: !ruby/object:Gem::Requirement
212
213
  requirements:
213
- - - ">="
214
+ - - ">"
214
215
  - !ruby/object:Gem::Version
215
- version: '0'
216
+ version: 1.3.1
216
217
  requirements: []
217
- rubygems_version: 3.3.7
218
+ rubygems_version: 3.4.10
218
219
  signing_key:
219
220
  specification_version: 4
220
221
  summary: Map Redis types directly to Ruby objects
221
222
  test_files:
222
223
  - spec/redis_autoload_objects_spec.rb
224
+ - spec/redis_legacy_key_naming_spec.rb
223
225
  - spec/redis_namespace_compat_spec.rb
224
226
  - spec/redis_objects_active_record_spec.rb
225
227
  - spec/redis_objects_conn_spec.rb