redis-objects 1.7.0 → 2.0.0
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 +245 -142
- data/lib/redis/list.rb +2 -10
- data/lib/redis/objects/locks.rb +1 -1
- data/lib/redis/objects/version.rb +1 -1
- data/lib/redis/objects.rb +125 -7
- 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 +4 -14
- data/spec/redis_objects_instance_spec.rb +41 -28
- data/spec/redis_objects_model_spec.rb +42 -16
- data/spec/spec_helper.rb +4 -3
- metadata +9 -10
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/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
|
@@ -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,16 +114,115 @@ class Redis
|
|
|
101
114
|
@redis_objects ||= {}
|
|
102
115
|
end
|
|
103
116
|
|
|
104
|
-
# Set the Redis redis_prefix to use. Defaults to
|
|
105
|
-
def redis_prefix=(redis_prefix)
|
|
117
|
+
# Set the Redis redis_prefix to use. Defaults to class_name.
|
|
118
|
+
def redis_prefix=(redis_prefix)
|
|
119
|
+
@silence_warnings_as_redis_prefix_was_set_manually = true
|
|
120
|
+
@redis_prefix = redis_prefix
|
|
121
|
+
end
|
|
122
|
+
|
|
106
123
|
def redis_prefix(klass = self) #:nodoc:
|
|
107
|
-
@redis_prefix ||=
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
124
|
+
@redis_prefix ||=
|
|
125
|
+
if Objects.redis_legacy_naming?
|
|
126
|
+
redis_legacy_naming_warning_message(klass)
|
|
127
|
+
redis_legacy_prefix(klass)
|
|
128
|
+
else
|
|
129
|
+
redis_modern_prefix(klass)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
@redis_prefix
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def redis_modern_prefix(klass = self) #:nodoc:
|
|
136
|
+
klass.name.to_s.
|
|
137
|
+
gsub(/::/, '__'). # Nested::Class => Nested__Class
|
|
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
|
|
140
|
+
downcase
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def redis_legacy_prefix(klass = self) #:nodoc:
|
|
144
|
+
klass.name.to_s.
|
|
145
|
+
sub(%r{(.*::)}, ''). # Nested::Class => Class (problematic)
|
|
146
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
|
|
147
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2'). # className => class_Name
|
|
111
148
|
downcase
|
|
112
149
|
end
|
|
113
150
|
|
|
151
|
+
attr_accessor :redis_silence_warnings
|
|
152
|
+
|
|
153
|
+
# Temporary warning to help with migrating key names
|
|
154
|
+
def redis_legacy_naming_warning_message(klass)
|
|
155
|
+
# warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
|
|
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
|
|
169
|
+
EOW
|
|
170
|
+
end
|
|
171
|
+
|
|
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
|
+
|
|
178
|
+
legacy = redis_legacy_prefix
|
|
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
|
|
183
|
+
end
|
|
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
|
|
193
|
+
|
|
194
|
+
loop do
|
|
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"
|
|
199
|
+
total_keys += keys.length
|
|
200
|
+
keys.each do |key|
|
|
201
|
+
# Split key name apart on ':'
|
|
202
|
+
# REM: global keys will have an empty id
|
|
203
|
+
# klass::object_accessor_name
|
|
204
|
+
_base_class, id, name = key.split(':')
|
|
205
|
+
|
|
206
|
+
# Figure out the new name
|
|
207
|
+
new_key = redis_field_key(name, id)
|
|
208
|
+
|
|
209
|
+
# Rename the key
|
|
210
|
+
if verbose
|
|
211
|
+
warn "[redis-objects] Rename '#{key}', '#{new_key}'"
|
|
212
|
+
end
|
|
213
|
+
ok = redis.rename(key, new_key)
|
|
214
|
+
warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
|
|
215
|
+
end
|
|
216
|
+
break if cursor == "0"
|
|
217
|
+
end
|
|
218
|
+
|
|
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
|
|
224
|
+
end
|
|
225
|
+
|
|
114
226
|
def redis_options(name)
|
|
115
227
|
klass = first_ancestor_with(name)
|
|
116
228
|
return klass.redis_objects[name.to_sym] || {}
|
|
@@ -130,18 +242,24 @@ class Redis
|
|
|
130
242
|
klass = first_ancestor_with(name)
|
|
131
243
|
# READ THIS: This can never ever ever ever change or upgrades will corrupt all data
|
|
132
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
|
|
133
247
|
if key = klass.redis_objects[name.to_sym][:key]
|
|
248
|
+
# If that custom key was callable (E.G. a proc)
|
|
134
249
|
if key.respond_to?(:call)
|
|
135
250
|
key = key.call context
|
|
136
251
|
else
|
|
137
252
|
context.instance_eval "%(#{key})"
|
|
138
253
|
end
|
|
254
|
+
|
|
139
255
|
else
|
|
256
|
+
# If its not a global key, and ID is nil, then throw an error
|
|
140
257
|
if id.nil? and !klass.redis_objects[name.to_sym][:global]
|
|
141
258
|
raise NilObjectId,
|
|
142
259
|
"[#{klass.redis_objects[name.to_sym]}] Attempt to address redis-object " +
|
|
143
260
|
":#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
|
|
144
261
|
end
|
|
262
|
+
# Otherwise return the constructed key
|
|
145
263
|
"#{redis_prefix(klass)}:#{id}:#{name}"
|
|
146
264
|
end
|
|
147
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
|