redis-objects 2.0.0.beta → 2.0.0.beta2
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/CHANGELOG.rdoc +4 -0
- data/README.md +230 -177
- data/lib/redis/objects/version.rb +1 -1
- data/lib/redis/objects.rb +68 -27
- data/spec/redis_key_naming_spec.rb +427 -0
- data/spec/redis_objects_active_record_spec.rb +1 -1
- data/spec/redis_objects_model_spec.rb +34 -12
- data/spec/spec_helper.rb +2 -1
- metadata +7 -10
- data/spec/redis_legacy_key_naming_spec.rb +0 -419
data/lib/redis/objects.rb
CHANGED
|
@@ -66,6 +66,19 @@ class Redis
|
|
|
66
66
|
raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# Toggles whether to use the legacy redis key naming scheme, which causes
|
|
70
|
+
# naming conflicts in certain cases.
|
|
71
|
+
# (attr_accessor with a default value)
|
|
72
|
+
attr_writer :prefix_style
|
|
73
|
+
def prefix_style
|
|
74
|
+
# NOTE: In a future release the default will change to :modern
|
|
75
|
+
@prefix_style ||= :legacy
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def redis_legacy_naming?
|
|
79
|
+
prefix_style == :legacy
|
|
80
|
+
end
|
|
81
|
+
|
|
69
82
|
def included(klass)
|
|
70
83
|
# Core (this file)
|
|
71
84
|
klass.instance_variable_set(:@redis, nil)
|
|
@@ -101,11 +114,6 @@ class Redis
|
|
|
101
114
|
@redis_objects ||= {}
|
|
102
115
|
end
|
|
103
116
|
|
|
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
117
|
# Set the Redis redis_prefix to use. Defaults to class_name.
|
|
110
118
|
def redis_prefix=(redis_prefix)
|
|
111
119
|
@silence_warnings_as_redis_prefix_was_set_manually = true
|
|
@@ -114,10 +122,10 @@ class Redis
|
|
|
114
122
|
|
|
115
123
|
def redis_prefix(klass = self) #:nodoc:
|
|
116
124
|
@redis_prefix ||=
|
|
117
|
-
if redis_legacy_naming
|
|
125
|
+
if Objects.redis_legacy_naming?
|
|
126
|
+
redis_legacy_naming_warning_message(klass)
|
|
118
127
|
redis_legacy_prefix(klass)
|
|
119
128
|
else
|
|
120
|
-
redis_legacy_naming_warning_message(klass)
|
|
121
129
|
redis_modern_prefix(klass)
|
|
122
130
|
end
|
|
123
131
|
|
|
@@ -140,45 +148,68 @@ class Redis
|
|
|
140
148
|
downcase
|
|
141
149
|
end
|
|
142
150
|
|
|
143
|
-
|
|
151
|
+
attr_accessor :redis_silence_warnings
|
|
152
|
+
|
|
153
|
+
# Temporary warning to help with migrating key names
|
|
144
154
|
def redis_legacy_naming_warning_message(klass)
|
|
145
155
|
# warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
return if redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually
|
|
157
|
+
|
|
158
|
+
modern = redis_modern_prefix(klass)
|
|
159
|
+
legacy = redis_legacy_prefix(klass)
|
|
160
|
+
return if modern == legacy
|
|
161
|
+
|
|
162
|
+
warn <<EOW
|
|
163
|
+
|
|
164
|
+
[redis-objects] WARNING: redis-objects 2.0.0, revises key naming to fix a longstanding bug.
|
|
165
|
+
[redis-objects] Your class #{klass.name} should be updated to resolve this bug!
|
|
153
166
|
[redis-objects] Current key prefix: #{legacy.inspect}
|
|
154
167
|
[redis-objects] Future key prefix: #{modern.inspect}
|
|
155
168
|
[redis-objects] Read more at https://github.com/nateware/redis-objects/issues/231
|
|
156
169
|
EOW
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
170
|
end
|
|
160
171
|
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
# To be run once per Redis::Objects enhanced model
|
|
173
|
+
def migrate_redis_legacy_keys(scan_count=10, verbose=false)
|
|
174
|
+
unless Objects.redis_legacy_naming?
|
|
175
|
+
raise "Redis::Objects is already configured to use modern key prefixes."
|
|
176
|
+
end
|
|
177
|
+
|
|
163
178
|
legacy = redis_legacy_prefix
|
|
164
|
-
|
|
165
|
-
if
|
|
166
|
-
|
|
179
|
+
modern = redis_modern_prefix
|
|
180
|
+
if modern == legacy
|
|
181
|
+
warn "[redis-objects] #{self.name}.#{__method__} NOOP. Legacy and modern redis_prefix are the same (#{modern})"
|
|
182
|
+
return
|
|
167
183
|
end
|
|
168
|
-
|
|
184
|
+
|
|
185
|
+
warn "\n[redis-objects] Migrating keys from '#{legacy}' prefix to '#{modern}'"
|
|
186
|
+
|
|
187
|
+
cursor = 0
|
|
188
|
+
total_keys = 0
|
|
189
|
+
|
|
190
|
+
# Temporarily update the prefix to modern while we update these keys.
|
|
191
|
+
# NOTE: we cannot simply adjust the prefix_style, because the prefix is cached by @redis_prefix
|
|
192
|
+
self.redis_prefix = modern
|
|
169
193
|
|
|
170
194
|
loop do
|
|
171
|
-
cursor, keys = redis.scan(cursor, :match => "#{legacy}:*")
|
|
195
|
+
cursor, keys = redis.scan(cursor, :match => "#{legacy}:*", :count => scan_count)
|
|
196
|
+
# REM: scan returns keys in a randomized order
|
|
197
|
+
keys.sort!
|
|
198
|
+
#puts "got #{keys.length} keys"
|
|
172
199
|
total_keys += keys.length
|
|
173
200
|
keys.each do |key|
|
|
174
201
|
# Split key name apart on ':'
|
|
175
|
-
|
|
202
|
+
# REM: global keys will have an empty id
|
|
203
|
+
# klass::object_accessor_name
|
|
204
|
+
_base_class, id, name = key.split(':')
|
|
176
205
|
|
|
177
206
|
# Figure out the new name
|
|
178
|
-
new_key = redis_field_key(name, id
|
|
207
|
+
new_key = redis_field_key(name, id)
|
|
179
208
|
|
|
180
209
|
# Rename the key
|
|
181
|
-
|
|
210
|
+
if verbose
|
|
211
|
+
warn "[redis-objects] Rename '#{key}', '#{new_key}'"
|
|
212
|
+
end
|
|
182
213
|
ok = redis.rename(key, new_key)
|
|
183
214
|
warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
|
|
184
215
|
end
|
|
@@ -186,6 +217,10 @@ EOW
|
|
|
186
217
|
end
|
|
187
218
|
|
|
188
219
|
warn "[redis-objects] Migrated #{total_keys} total number of redis keys"
|
|
220
|
+
|
|
221
|
+
ensure
|
|
222
|
+
# Change the prefix back (just in case)
|
|
223
|
+
self.redis_prefix = legacy
|
|
189
224
|
end
|
|
190
225
|
|
|
191
226
|
def redis_options(name)
|
|
@@ -207,18 +242,24 @@ EOW
|
|
|
207
242
|
klass = first_ancestor_with(name)
|
|
208
243
|
# READ THIS: This can never ever ever ever change or upgrades will corrupt all data
|
|
209
244
|
# I don't think people were using Proc as keys before (that would create a weird key). Should be ok
|
|
245
|
+
|
|
246
|
+
# If a custom key was set for this accessor
|
|
210
247
|
if key = klass.redis_objects[name.to_sym][:key]
|
|
248
|
+
# If that custom key was callable (E.G. a proc)
|
|
211
249
|
if key.respond_to?(:call)
|
|
212
250
|
key = key.call context
|
|
213
251
|
else
|
|
214
252
|
context.instance_eval "%(#{key})"
|
|
215
253
|
end
|
|
254
|
+
|
|
216
255
|
else
|
|
256
|
+
# If its not a global key, and ID is nil, then throw an error
|
|
217
257
|
if id.nil? and !klass.redis_objects[name.to_sym][:global]
|
|
218
258
|
raise NilObjectId,
|
|
219
259
|
"[#{klass.redis_objects[name.to_sym]}] Attempt to address redis-object " +
|
|
220
260
|
":#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
|
|
221
261
|
end
|
|
262
|
+
# Otherwise return the constructed key
|
|
222
263
|
"#{redis_prefix(klass)}:#{id}:#{name}"
|
|
223
264
|
end
|
|
224
265
|
end
|
|
@@ -0,0 +1,427 @@
|
|
|
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 'Redis key prefix naming compatibility' do
|
|
23
|
+
|
|
24
|
+
describe 'verifies single level classes' do # context
|
|
25
|
+
|
|
26
|
+
it 'work the same (modern)' do
|
|
27
|
+
Redis::Objects.prefix_style = :modern
|
|
28
|
+
|
|
29
|
+
class SingleLevelOne
|
|
30
|
+
include Redis::Objects
|
|
31
|
+
|
|
32
|
+
def id
|
|
33
|
+
1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
obj = SingleLevelOne.new
|
|
38
|
+
obj.class.redis_prefix.should == 'single_level_one'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'work the same (legacy)' do
|
|
42
|
+
Redis::Objects.prefix_style = :legacy
|
|
43
|
+
|
|
44
|
+
class SingleLevelTwo
|
|
45
|
+
include Redis::Objects
|
|
46
|
+
|
|
47
|
+
def id
|
|
48
|
+
1
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
obj = SingleLevelTwo.new
|
|
53
|
+
obj.class.redis_prefix.should == 'single_level_two'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end # context
|
|
57
|
+
|
|
58
|
+
describe 'verifies nested classes' do # context
|
|
59
|
+
|
|
60
|
+
it 'do NOT work the same (modern)' do
|
|
61
|
+
Redis::Objects.prefix_style = :modern
|
|
62
|
+
|
|
63
|
+
module Nested
|
|
64
|
+
class NamingOne
|
|
65
|
+
include Redis::Objects
|
|
66
|
+
|
|
67
|
+
def id
|
|
68
|
+
1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
obj = Nested::NamingOne.new
|
|
74
|
+
obj.class.redis_prefix.should == 'nested__naming_one'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'do NOT work the same (legacy)' do
|
|
78
|
+
Redis::Objects.prefix_style = :legacy
|
|
79
|
+
|
|
80
|
+
module Nested
|
|
81
|
+
class NamingTwo
|
|
82
|
+
include Redis::Objects
|
|
83
|
+
self.redis_silence_warnings = true
|
|
84
|
+
|
|
85
|
+
def id
|
|
86
|
+
1
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
obj = Nested::NamingTwo.new
|
|
92
|
+
obj.class.redis_prefix.should == 'naming_two'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end # context
|
|
96
|
+
|
|
97
|
+
describe 'verifies that multiple levels' do # context
|
|
98
|
+
|
|
99
|
+
it 'respect __ vs _' do
|
|
100
|
+
Redis::Objects.prefix_style = :modern
|
|
101
|
+
|
|
102
|
+
module NestedLevel
|
|
103
|
+
module Further
|
|
104
|
+
class NamingThree
|
|
105
|
+
include Redis::Objects
|
|
106
|
+
|
|
107
|
+
def id
|
|
108
|
+
1
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
obj = NestedLevel::Further::NamingThree.new
|
|
115
|
+
obj.class.redis_prefix.should == 'nested_level__further__naming_three'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'respect legacy naming' do
|
|
119
|
+
Redis::Objects.prefix_style = :legacy
|
|
120
|
+
|
|
121
|
+
module NestedLevel
|
|
122
|
+
module Further
|
|
123
|
+
class NamingFour
|
|
124
|
+
include Redis::Objects
|
|
125
|
+
self.redis_silence_warnings = true
|
|
126
|
+
|
|
127
|
+
def id
|
|
128
|
+
1
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31)
|
|
132
|
+
value :redis_value, :redis => redis_handle
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
obj = NestedLevel::Further::NamingFour.new
|
|
138
|
+
obj.class.redis_prefix.should == 'naming_four'
|
|
139
|
+
val = SecureRandom.hex(10)
|
|
140
|
+
obj.redis_value = val
|
|
141
|
+
obj.redis_value.should == val
|
|
142
|
+
obj.redis_value.key.should == 'naming_four:1:redis_value'
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'do not conflict 1' do
|
|
146
|
+
Redis::Objects.prefix_style = :modern
|
|
147
|
+
|
|
148
|
+
module NestedLevel
|
|
149
|
+
module Further
|
|
150
|
+
class NamingFive
|
|
151
|
+
include Redis::Objects
|
|
152
|
+
|
|
153
|
+
def id
|
|
154
|
+
1
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
value :redis_value
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
obj = NestedLevel::Further::NamingFive.new
|
|
163
|
+
obj.class.redis_prefix.should == 'nested_level__further__naming_five'
|
|
164
|
+
val = SecureRandom.hex(10)
|
|
165
|
+
obj.redis_value = val
|
|
166
|
+
obj.redis_value.should == val
|
|
167
|
+
obj.redis_value.key.should == 'nested_level__further__naming_five:1:redis_value'
|
|
168
|
+
obj.redis_value.redis.should == obj.redis
|
|
169
|
+
obj.redis.get('nested_level__further__naming_five:1:redis_value').should == val
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'do not conflict 2' do
|
|
173
|
+
Redis::Objects.prefix_style = :modern
|
|
174
|
+
|
|
175
|
+
module Nested
|
|
176
|
+
module LevelFurtherNaming
|
|
177
|
+
class Five
|
|
178
|
+
include Redis::Objects
|
|
179
|
+
|
|
180
|
+
def id
|
|
181
|
+
1
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
value :redis_value
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
obj = Nested::LevelFurtherNaming::Five.new
|
|
190
|
+
obj.class.redis_prefix.should == 'nested__level_further_naming__five'
|
|
191
|
+
val = SecureRandom.hex(10)
|
|
192
|
+
obj.redis_value = val
|
|
193
|
+
obj.redis_value.should == val
|
|
194
|
+
obj.redis_value.key.should == 'nested__level_further_naming__five:1:redis_value'
|
|
195
|
+
obj.redis.get('nested__level_further_naming__five:1:redis_value').should == val
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'do not conflict 3' do
|
|
199
|
+
Redis::Objects.prefix_style = :modern
|
|
200
|
+
|
|
201
|
+
module Nested
|
|
202
|
+
module LevelFurther
|
|
203
|
+
class NamingFive
|
|
204
|
+
include Redis::Objects
|
|
205
|
+
|
|
206
|
+
def id
|
|
207
|
+
1
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
value :redis_value
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
obj = Nested::LevelFurther::NamingFive.new
|
|
216
|
+
obj.class.redis_prefix.should == 'nested__level_further__naming_five'
|
|
217
|
+
val = SecureRandom.hex(10)
|
|
218
|
+
obj.redis_value = val
|
|
219
|
+
obj.redis_value.should == val
|
|
220
|
+
obj.redis_value.key.should == 'nested__level_further__naming_five:1:redis_value'
|
|
221
|
+
obj.redis.get('nested__level_further__naming_five:1:redis_value').should == val
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
end # context
|
|
225
|
+
|
|
226
|
+
describe 'handles dynamically created classes correctly' do # context
|
|
227
|
+
|
|
228
|
+
it 'in modern mode' do
|
|
229
|
+
Redis::Objects.prefix_style = :modern
|
|
230
|
+
|
|
231
|
+
module Nested
|
|
232
|
+
class LevelSix
|
|
233
|
+
include Redis::Objects
|
|
234
|
+
|
|
235
|
+
def id
|
|
236
|
+
1
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
value :redis_value
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
obj = Nested::LevelSix.new
|
|
244
|
+
obj.class.redis_prefix.should == 'nested__level_six'
|
|
245
|
+
val = SecureRandom.hex(10)
|
|
246
|
+
obj.redis_value = val
|
|
247
|
+
obj.redis_value.should == val
|
|
248
|
+
obj.redis_value.key.should == 'nested__level_six:1:redis_value'
|
|
249
|
+
obj.redis.get('nested__level_six:1:redis_value').should == val
|
|
250
|
+
|
|
251
|
+
DynamicClass = Class.new(Nested::LevelSix)
|
|
252
|
+
DynamicClass.value :redis_value2
|
|
253
|
+
obj2 = DynamicClass.new
|
|
254
|
+
DynamicClass.redis_prefix.should == 'dynamic_class'
|
|
255
|
+
obj2.redis_value.should.be.kind_of(Redis::Value)
|
|
256
|
+
obj2.redis_value2.should.be.kind_of(Redis::Value)
|
|
257
|
+
obj2.redis_value.key.should == 'dynamic_class:1:redis_value'
|
|
258
|
+
obj2.redis_value2.key.should == 'dynamic_class:1:redis_value2'
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it 'in legacy mode' do
|
|
263
|
+
Redis::Objects.prefix_style = :legacy
|
|
264
|
+
|
|
265
|
+
module Nested
|
|
266
|
+
class LevelSeven
|
|
267
|
+
include Redis::Objects
|
|
268
|
+
self.redis_silence_warnings = true
|
|
269
|
+
|
|
270
|
+
def id
|
|
271
|
+
1
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
value :redis_value
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
obj = Nested::LevelSeven.new
|
|
279
|
+
obj.class.redis_prefix.should == 'level_seven'
|
|
280
|
+
val = SecureRandom.hex(10)
|
|
281
|
+
obj.redis_value = val
|
|
282
|
+
obj.redis_value.should == val
|
|
283
|
+
obj.redis_value.key.should == 'level_seven:1:redis_value'
|
|
284
|
+
obj.redis.get('level_seven:1:redis_value').should == val
|
|
285
|
+
|
|
286
|
+
DynamicClass2 = Class.new(Nested::LevelSeven)
|
|
287
|
+
DynamicClass2.value :redis_value2
|
|
288
|
+
obj2 = DynamicClass2.new
|
|
289
|
+
DynamicClass2.redis_prefix.should == 'dynamic_class2'
|
|
290
|
+
obj2.redis_value.should.be.kind_of(Redis::Value)
|
|
291
|
+
obj2.redis_value2.should.be.kind_of(Redis::Value)
|
|
292
|
+
obj2.redis_value.key.should == 'dynamic_class2:1:redis_value'
|
|
293
|
+
obj2.redis_value2.key.should == 'dynamic_class2:1:redis_value2'
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
end # context
|
|
297
|
+
|
|
298
|
+
# ---- other tests ----
|
|
299
|
+
|
|
300
|
+
it 'prints a warning message if the key name changes' do
|
|
301
|
+
Redis::Objects.prefix_style = :legacy
|
|
302
|
+
|
|
303
|
+
module Nested
|
|
304
|
+
class LevelNine
|
|
305
|
+
include Redis::Objects
|
|
306
|
+
|
|
307
|
+
def id
|
|
308
|
+
1
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
value :redis_value
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
captured_output = capture_stderr do
|
|
316
|
+
# Does not output anything directly.
|
|
317
|
+
obj = Nested::LevelNine.new
|
|
318
|
+
val = SecureRandom.hex(10)
|
|
319
|
+
obj.redis_value = val
|
|
320
|
+
obj.redis_value.should == val
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
captured_output.should =~ /Warning:/i
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
it 'supports a method to migrate legacy key names' do
|
|
327
|
+
|
|
328
|
+
module Nested
|
|
329
|
+
def self.make_class
|
|
330
|
+
# TODO notes
|
|
331
|
+
klass = Class.new do
|
|
332
|
+
def initialize(id)
|
|
333
|
+
@id = id
|
|
334
|
+
end
|
|
335
|
+
def id
|
|
336
|
+
@id
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
include Redis::Objects
|
|
340
|
+
|
|
341
|
+
self.redis_silence_warnings = true
|
|
342
|
+
|
|
343
|
+
value :redis_value
|
|
344
|
+
counter :redis_counter
|
|
345
|
+
hash_key :redis_hash
|
|
346
|
+
list :redis_list
|
|
347
|
+
set :redis_set
|
|
348
|
+
sorted_set :redis_sorted_set
|
|
349
|
+
|
|
350
|
+
# global class counters
|
|
351
|
+
value :global_value, :global => true
|
|
352
|
+
counter :global_counter, :global => true
|
|
353
|
+
hash_key :global_hash_key, :global => true
|
|
354
|
+
list :global_list, :global => true
|
|
355
|
+
set :global_set, :global => true
|
|
356
|
+
sorted_set :global_sorted_set, :global => true
|
|
357
|
+
|
|
358
|
+
# use a callable as the key
|
|
359
|
+
value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# First define the class using legacy prefix
|
|
365
|
+
Redis::Objects.prefix_style = :legacy
|
|
366
|
+
Nested::UpgradeTest = Nested.make_class
|
|
367
|
+
|
|
368
|
+
# Sanity checks
|
|
369
|
+
Nested::UpgradeTest.redis_objects.length.should == 13
|
|
370
|
+
Nested::UpgradeTest.redis_prefix.should == 'upgrade_test'
|
|
371
|
+
|
|
372
|
+
# Create a whole bunch of keys using the legacy prefixed keys
|
|
373
|
+
|
|
374
|
+
30.times do |i|
|
|
375
|
+
# warn i.inspect
|
|
376
|
+
obj = Nested::UpgradeTest.new(i)
|
|
377
|
+
obj.redis_value = i
|
|
378
|
+
obj.redis_counter.increment
|
|
379
|
+
obj.redis_hash[:key] = i
|
|
380
|
+
obj.redis_list << i
|
|
381
|
+
obj.redis_set << i
|
|
382
|
+
obj.redis_sorted_set[i] = i
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
obj = Nested::UpgradeTest.new(99)
|
|
386
|
+
obj.global_value = 42
|
|
387
|
+
obj.global_counter.increment
|
|
388
|
+
obj.global_counter.increment
|
|
389
|
+
obj.global_counter.increment
|
|
390
|
+
obj.global_hash_key[:key] = 'value'
|
|
391
|
+
obj.global_set << 'a' << 'b'
|
|
392
|
+
obj.global_sorted_set[:key] = 2.2
|
|
393
|
+
|
|
394
|
+
# Run the upgrade
|
|
395
|
+
Nested::UpgradeTest.migrate_redis_legacy_keys(1000)
|
|
396
|
+
|
|
397
|
+
# Re-Create the class using modern prefix
|
|
398
|
+
Nested.send(:remove_const, :UpgradeTest)
|
|
399
|
+
Redis::Objects.prefix_style = :modern
|
|
400
|
+
Nested::UpgradeTest = Nested.make_class
|
|
401
|
+
|
|
402
|
+
# Sanity checks
|
|
403
|
+
Nested::UpgradeTest.redis_objects.length.should == 13
|
|
404
|
+
Nested::UpgradeTest.redis_prefix.should == 'nested__upgrade_test'
|
|
405
|
+
|
|
406
|
+
# Try to access the keys through modern prefixed keys now
|
|
407
|
+
30.times do |i|
|
|
408
|
+
# warn i.inspect
|
|
409
|
+
obj = Nested::UpgradeTest.new(i)
|
|
410
|
+
obj.redis_value.to_i.should == i
|
|
411
|
+
obj.redis_counter.to_i.should == 1
|
|
412
|
+
obj.redis_hash[:key].to_i.should == i
|
|
413
|
+
obj.redis_list[0].to_i.should == i
|
|
414
|
+
obj.redis_set.include?(i).should == true
|
|
415
|
+
obj.redis_sorted_set[i].should == i
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
obj = Nested::UpgradeTest.new(99)
|
|
419
|
+
obj.global_value.to_i.should == 42
|
|
420
|
+
obj.global_counter.to_i.should == 3
|
|
421
|
+
obj.global_hash_key[:key].should == 'value'
|
|
422
|
+
obj.global_set.include?('a').should == true
|
|
423
|
+
obj.global_set.include?('b').should == true
|
|
424
|
+
obj.global_sorted_set[:key].should == 2.2
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
end
|
|
@@ -6,6 +6,7 @@ Redis::Objects.redis = REDIS_HANDLE
|
|
|
6
6
|
|
|
7
7
|
class Roster
|
|
8
8
|
include Redis::Objects
|
|
9
|
+
|
|
9
10
|
counter :available_slots, :start => 10
|
|
10
11
|
counter :pitchers, :limit => :max_pitchers
|
|
11
12
|
counter :basic
|
|
@@ -35,7 +36,7 @@ class Roster
|
|
|
35
36
|
def self.jimmyhat; 350; end
|
|
36
37
|
value :weird_key, :key => 'players:weird_key:#{jimmyhat}', :global => true
|
|
37
38
|
|
|
38
|
-
#callable as key
|
|
39
|
+
# callable as key
|
|
39
40
|
counter :daily, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" }
|
|
40
41
|
|
|
41
42
|
# set default expiration
|
|
@@ -61,16 +62,31 @@ class Roster
|
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
class VanillaRoster < Roster
|
|
64
|
-
#
|
|
65
|
+
# inherits Redis::Objects
|
|
66
|
+
# No explicit RedisAccessors (but they are inherited)
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
class CustomRoster < Roster
|
|
68
|
-
|
|
70
|
+
# inherits Redis::Objects
|
|
69
71
|
|
|
70
72
|
counter :basic # Override
|
|
71
73
|
counter :special # New
|
|
72
74
|
end
|
|
73
75
|
|
|
76
|
+
class UidRoster < Roster
|
|
77
|
+
# inherits Redis::Objects
|
|
78
|
+
|
|
79
|
+
attr_accessor :uid
|
|
80
|
+
def initialize(uid=123) @uid = uid end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class CustomIdFieldRoster < UidRoster
|
|
84
|
+
# inherits Redis::Objects
|
|
85
|
+
redis_id_field :uid
|
|
86
|
+
|
|
87
|
+
counter :basic
|
|
88
|
+
end
|
|
89
|
+
|
|
74
90
|
class MethodRoster
|
|
75
91
|
def increment(attribute, by=1)
|
|
76
92
|
42
|
|
@@ -87,18 +103,24 @@ class CustomMethodRoster < MethodRoster
|
|
|
87
103
|
counter :basic
|
|
88
104
|
end
|
|
89
105
|
|
|
90
|
-
class UidRoster < Roster
|
|
91
|
-
attr_accessor :uid
|
|
92
|
-
def initialize(uid=123) @uid = uid end
|
|
93
|
-
end
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
# TODO: @redis_objects is un-initialized in subclasses. It only picks up accessors declared within the subclass itself.
|
|
108
|
+
# but all the accessors continue to work as expected.
|
|
109
|
+
=begin
|
|
110
|
+
puts "Roster.redis_objects.length: #{Roster.redis_objects.length} (expected: 35)" # got 35
|
|
111
|
+
VanillaRoster.total_players_online.increment(5)
|
|
112
|
+
VanillaRoster.global_player_leaderboard.add('nate', 22)
|
|
113
|
+
puts "VanillaRoster.redis_objects.length: #{VanillaRoster.redis_objects.length} (expected: 35)" # got 0
|
|
114
|
+
puts "VanillaRoster: total_players_online: #{VanillaRoster.total_players_online} global_player_leaderboard: #{VanillaRoster.global_player_leaderboard}" # got 5 & nate
|
|
115
|
+
puts "CustomRoster.redis_objects.length: #{CustomRoster.redis_objects.length} (expected: 36)" # got 2
|
|
116
|
+
puts "UidRoster.redis_objects.length: #{UidRoster.redis_objects.length} (expected: 35)" # got 0
|
|
117
|
+
puts "CustomIdFieldRoster.redis_objects.length: #{CustomIdFieldRoster.redis_objects.length} (expected: 35)" # got 1
|
|
118
|
+
#puts "MethodRoster.redis_objects.length: #{MethodRoster.redis_objects.length}"
|
|
119
|
+
puts "CustomMethodRoster.redis_objects.length: #{CustomMethodRoster.redis_objects.length} (expected: 1)" # got 1
|
|
120
|
+
=end
|
|
100
121
|
|
|
101
122
|
describe Redis::Objects do
|
|
123
|
+
# Before each
|
|
102
124
|
before do
|
|
103
125
|
@roster = Roster.new
|
|
104
126
|
@roster2 = Roster.new
|