redis-objects 1.7.0 → 2.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![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.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
|