redis-objects 1.7.0 → 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.
- checksums.yaml +4 -4
- data/README.md +54 -4
- data/lib/redis/objects/locks.rb +1 -1
- data/lib/redis/objects/version.rb +1 -1
- data/lib/redis/objects.rb +83 -6
- data/spec/redis_legacy_key_naming_spec.rb +419 -0
- data/spec/redis_objects_conn_spec.rb +2 -2
- data/spec/redis_objects_model_spec.rb +8 -4
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22b6b39234249ccd4c9485061bb918a211c539b3612d1a916a56eab0e13e031f
|
4
|
+
data.tar.gz: 9465be0b72fb8d160421eae75cf5cc5996a2aaa6b507bac6492f6a0b66fad788
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff4f516b5618e2a010c32ee0360dbfdbc172d400d3570cae3e5078aff845188171f3900849bdbb9c9c8cc869cc419d1995c9162b79e09a2a7d750d05b0591987
|
7
|
+
data.tar.gz: 75677a0da407ad97925c69ea51a55d51d4fc3b7a036768e4c5cb2457adbeefdeb924160d3ec8656949b4d2369785c8de4a272a4284ccc12f379eb89b3a7496b3
|
data/README.md
CHANGED
@@ -5,6 +5,56 @@ Redis::Objects - Map Redis types directly to Ruby objects
|
|
5
5
|
[](https://codecov.io/gh/nateware/redis-objects)
|
6
6
|
[](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.alpha'
|
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 2022.
|
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
|
+
Rename 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 [#213](https://github.com/nateware/redis-objects/issues/231).
|
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
|
-
|
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
|
-
|
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
|
-
|
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-
|
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/objects/locks.rb
CHANGED
@@ -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
|
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)
|
data/lib/redis/objects.rb
CHANGED
@@ -101,16 +101,93 @@ class Redis
|
|
101
101
|
@redis_objects ||= {}
|
102
102
|
end
|
103
103
|
|
104
|
-
#
|
105
|
-
|
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 ||=
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
+
WARNING: In redis-objects 2.0.0, key naming will change to fix longstanding bugs.
|
152
|
+
Your class #{klass.name.to_s} will be affected by this change!
|
153
|
+
Current key prefix: #{legacy.inspect}
|
154
|
+
Future key prefix: #{modern.inspect}
|
155
|
+
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 "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 "Rename '#{key}', '#{new_key}'"
|
182
|
+
ok = redis.rename(key, new_key)
|
183
|
+
warn "Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
|
184
|
+
end
|
185
|
+
break if cursor == "0"
|
186
|
+
end
|
187
|
+
|
188
|
+
warn "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] || {}
|
@@ -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 = '
|
38
|
-
obj.redis_value.value.should == '
|
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
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-objects
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
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: 2022-06-
|
11
|
+
date: 2022-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -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,9 +211,9 @@ 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:
|
216
|
+
version: 1.3.1
|
216
217
|
requirements: []
|
217
218
|
rubygems_version: 3.3.7
|
218
219
|
signing_key:
|
@@ -220,6 +221,7 @@ 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
|