redis-objects 2.0.0.alpha → 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 +12 -0
- data/README.md +230 -177
- data/lib/redis/list.rb +2 -10
- data/lib/redis/objects/version.rb +1 -1
- data/lib/redis/objects.rb +74 -33
- data/lib/redis/set.rb +2 -1
- data/lib/redis/sorted_set.rb +2 -2
- data/redis-objects.gemspec +1 -1
- data/spec/redis_key_naming_spec.rb +427 -0
- data/spec/redis_objects_active_record_spec.rb +1 -1
- data/spec/redis_objects_conn_spec.rb +2 -12
- data/spec/redis_objects_instance_spec.rb +41 -28
- data/spec/redis_objects_model_spec.rb +34 -12
- data/spec/spec_helper.rb +4 -3
- metadata +11 -14
- data/spec/redis_legacy_key_naming_spec.rb +0 -419
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
|
-
|
|
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
|
-
|
|
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
|
data/lib/redis/objects.rb
CHANGED
|
@@ -62,10 +62,23 @@ class Redis
|
|
|
62
62
|
@redis = Objects::ConnectionPoolProxy.proxy_if_needed(conn)
|
|
63
63
|
end
|
|
64
64
|
def redis
|
|
65
|
-
@redis || $redis ||
|
|
65
|
+
@redis || $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,52 +148,79 @@ 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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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!
|
|
166
|
+
[redis-objects] Current key prefix: #{legacy.inspect}
|
|
167
|
+
[redis-objects] Future key prefix: #{modern.inspect}
|
|
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
|
-
warn "Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
|
|
214
|
+
warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
|
|
184
215
|
end
|
|
185
216
|
break if cursor == "0"
|
|
186
217
|
end
|
|
187
218
|
|
|
188
|
-
warn "Migrated #{total_keys} total number of redis keys"
|
|
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
|
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
|
-
|
|
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
|
|
data/lib/redis/sorted_set.rb
CHANGED
|
@@ -211,7 +211,7 @@ class Redis
|
|
|
211
211
|
result = redis.zrange(temp_key, 0, -1)
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
-
result
|
|
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
|
|
251
|
+
result
|
|
252
252
|
end
|
|
253
253
|
alias_method :|, :union
|
|
254
254
|
alias_method :+, :union
|
data/redis-objects.gemspec
CHANGED
|
@@ -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,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
|