Module: Redis::Objects::ClassMethods

Defined in:
lib/redis/objects.rb

Overview

Class methods that appear in your class when you include Redis::Objects.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#redis_objectsObject



113
114
115
# File 'lib/redis/objects.rb', line 113

def redis_objects
  @redis_objects ||= {}
end

#redis_silence_warningsObject

Returns the value of attribute redis_silence_warnings.



151
152
153
# File 'lib/redis/objects.rb', line 151

def redis_silence_warnings
  @redis_silence_warnings
end

Instance Method Details

#first_ancestor_with(name) ⇒ Object



267
268
269
270
271
272
273
# File 'lib/redis/objects.rb', line 267

def first_ancestor_with(name)
  if redis_objects && redis_objects.key?(name.to_sym)
    self
  elsif superclass && superclass.respond_to?(:redis_objects)
    superclass.first_ancestor_with(name)
  end
end

#migrate_redis_legacy_keys(scan_count = 10, verbose = false) ⇒ Object

To be run once per Redis::Objects enhanced model



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/redis/objects.rb', line 173

def migrate_redis_legacy_keys(scan_count=10, verbose=false)
  unless Objects.redis_legacy_naming?
    raise "Redis::Objects is already configured to use modern key prefixes."
  end

  legacy = redis_legacy_prefix
  modern = redis_modern_prefix
  if modern == legacy
    warn "[redis-objects] #{self.name}.#{__method__} NOOP. Legacy and modern redis_prefix are the same (#{modern})"
    return
  end

  warn "\n[redis-objects] Migrating keys from '#{legacy}' prefix to '#{modern}'"

  cursor = 0
  total_keys = 0

  # Temporarily update the prefix to modern while we update these keys.
  # NOTE: we cannot simply adjust the prefix_style, because the prefix is cached by @redis_prefix
  self.redis_prefix = modern

  loop do
    cursor, keys = redis.scan(cursor, :match => "#{legacy}:*", :count => scan_count)
    # REM: scan returns keys in a randomized order
    keys.sort!
    #puts "got #{keys.length} keys"
    total_keys += keys.length
    keys.each do |key|
      # Split key name apart on ':'
      # REM: global keys will have an empty id
      #      klass::object_accessor_name
      _base_class, id, name = key.split(':')

      # Figure out the new name
      new_key = redis_field_key(name, id)

      # Rename the key
      if verbose
        warn "[redis-objects] Rename '#{key}', '#{new_key}'"
      end
      ok = redis.rename(key, new_key)
      warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
    end
    break if cursor == "0"
  end

  warn "[redis-objects] Migrated #{total_keys} total number of redis keys"

ensure
  # Change the prefix back (just in case)
  self.redis_prefix = legacy
end

#redisObject



107
108
109
# File 'lib/redis/objects.rb', line 107

def redis
  @redis || Objects.redis
end

#redis=(conn) ⇒ Object

Enable per-class connections (eg, User and Post can use diff redis-server)



103
104
105
# File 'lib/redis/objects.rb', line 103

def redis=(conn)
  @redis = Objects::ConnectionPoolProxy.proxy_if_needed(conn)
end

#redis_field_key(name, id = nil, context = self) ⇒ Object

:nodoc:



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/redis/objects.rb', line 241

def redis_field_key(name, id=nil, context=self) #:nodoc:
  klass = first_ancestor_with(name)
  # READ THIS: This can never ever ever ever change or upgrades will corrupt all data
  # I don't think people were using Proc as keys before (that would create a weird key). Should be ok
  
  # If a custom key was set for this accessor
  if key = klass.redis_objects[name.to_sym][:key]
    # If that custom key was callable (E.G. a proc)
    if key.respond_to?(:call)
      key = key.call context
    else
      context.instance_eval "%(#{key})"
    end

  else
    # If its not a global key, and ID is nil, then throw an error
    if id.nil? and !klass.redis_objects[name.to_sym][:global]
      raise NilObjectId,
        "[#{klass.redis_objects[name.to_sym]}] Attempt to address redis-object " +
        ":#{name} on class #{klass.name} with nil id (unsaved record?) [object_id=#{object_id}]"
    end
    # Otherwise return the constructed key
    "#{redis_prefix(klass)}:#{id}:#{name}"
  end
end

#redis_field_redis(name) ⇒ Object

:nodoc:



231
232
233
234
235
236
237
238
239
# File 'lib/redis/objects.rb', line 231

def redis_field_redis(name) #:nodoc:
  klass = first_ancestor_with(name)
  override_redis = klass.redis_objects[name.to_sym][:redis]
  if override_redis
    Objects::ConnectionPoolProxy.proxy_if_needed(override_redis)
  else
    self.redis
  end
end

#redis_id_field(id = nil) ⇒ Object



275
276
277
278
279
280
281
282
283
# File 'lib/redis/objects.rb', line 275

def redis_id_field(id=nil)
  @redis_id_field = id || @redis_id_field

  if superclass && superclass.respond_to?(:redis_id_field)
    @redis_id_field ||= superclass.redis_id_field
  end

  @redis_id_field ||= :id
end

#redis_legacy_naming_warning_message(klass) ⇒ Object

Temporary warning to help with migrating key names



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/redis/objects.rb', line 154

def redis_legacy_naming_warning_message(klass)
  # warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
  return if redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually

  modern = redis_modern_prefix(klass)
  legacy = redis_legacy_prefix(klass)
  return if modern == legacy

  warn <<EOW

[redis-objects] WARNING: redis-objects 2.0.0, revises key naming to fix a longstanding bug.
[redis-objects] Your class #{klass.name} should be updated to resolve this bug!
[redis-objects] Current key prefix: #{legacy.inspect}
[redis-objects] Future  key prefix: #{modern.inspect}
[redis-objects] Read more at https://github.com/nateware/redis-objects/issues/231
EOW
end

#redis_legacy_prefix(klass = self) ⇒ Object

:nodoc:



143
144
145
146
147
148
149
# File 'lib/redis/objects.rb', line 143

def redis_legacy_prefix(klass = self) #:nodoc:
  klass.name.to_s.
    sub(%r{(.*::)}, '').                  # Nested::Class => Class (problematic)
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
    gsub(/([a-z\d])([A-Z])/,'\1_\2').     # className => class_Name
    downcase
end

#redis_modern_prefix(klass = self) ⇒ Object

:nodoc:



135
136
137
138
139
140
141
# File 'lib/redis/objects.rb', line 135

def redis_modern_prefix(klass = self) #:nodoc:
  klass.name.to_s.
    gsub(/::/, '__').                     # Nested::Class => Nested__Class
    gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # ClassName => Class_Name
    gsub(/([a-z\d])([A-Z])/,'\1_\2').     # className => class_Name
    downcase
end

#redis_options(name) ⇒ Object



226
227
228
229
# File 'lib/redis/objects.rb', line 226

def redis_options(name)
  klass = first_ancestor_with(name)
  return klass.redis_objects[name.to_sym] || {}
end

#redis_prefix(klass = self) ⇒ Object

:nodoc:



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/redis/objects.rb', line 123

def redis_prefix(klass = self) #:nodoc:
  @redis_prefix ||=
    if Objects.redis_legacy_naming?
      redis_legacy_naming_warning_message(klass)
      redis_legacy_prefix(klass)
    else
      redis_modern_prefix(klass)
    end

  @redis_prefix
end

#redis_prefix=(redis_prefix) ⇒ Object

Set the Redis redis_prefix to use. Defaults to class_name.



118
119
120
121
# File 'lib/redis/objects.rb', line 118

def redis_prefix=(redis_prefix)
  @silence_warnings_as_redis_prefix_was_set_manually = true
  @redis_prefix = redis_prefix
end