lib/redis/objects.rb in redis-objects-1.7.0 vs lib/redis/objects.rb in redis-objects-2.0.0.alpha

- old
+ new

@@ -99,17 +99,94 @@ attr_writer :redis_objects def redis_objects @redis_objects ||= {} end - # Set the Redis redis_prefix to use. Defaults to model_name - def redis_prefix=(redis_prefix) @redis_prefix = redis_prefix end + # Toggles whether to use the legacy redis key naming scheme, which causes + # naming conflicts in certain cases. + attr_accessor :redis_legacy_naming + attr_accessor :redis_silence_warnings + + # Set the Redis redis_prefix to use. Defaults to class_name. + def redis_prefix=(redis_prefix) + @silence_warnings_as_redis_prefix_was_set_manually = true + @redis_prefix = redis_prefix + end + def redis_prefix(klass = self) #:nodoc: - @redis_prefix ||= klass.name.to_s. - sub(%r{(.*::)}, ''). - gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). - gsub(/([a-z\d])([A-Z])/,'\1_\2'). + @redis_prefix ||= + if redis_legacy_naming + redis_legacy_prefix(klass) + else + redis_legacy_naming_warning_message(klass) + redis_modern_prefix(klass) + end + + @redis_prefix + end + + 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 + + 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 + + # Temporary warning to help with migrating key names + def redis_legacy_naming_warning_message(klass) + # warn @silence_warnings_as_redis_prefix_was_set_manually.inspect + unless redis_legacy_naming || redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually + modern = redis_modern_prefix(klass) + legacy = redis_legacy_prefix(klass) + if modern != legacy + warn <<EOW +WARNING: In redis-objects 2.0.0, key naming will change to fix longstanding bugs. +Your class #{klass.name.to_s} will be affected by this change! +Current key prefix: #{legacy.inspect} +Future key prefix: #{modern.inspect} +Read more at https://github.com/nateware/redis-objects/issues/231 +EOW + end + end + end + + def migrate_redis_legacy_keys + cursor = 0 + legacy = redis_legacy_prefix + total_keys = 0 + if legacy == redis_prefix + raise "Failed to migrate keys for #{self.name.to_s} as legacy and new redis_prefix are the same (#{redis_prefix})" + end + warn "Migrating keys from #{legacy} prefix to #{redis_prefix}" + + loop do + cursor, keys = redis.scan(cursor, :match => "#{legacy}:*") + total_keys += keys.length + keys.each do |key| + # Split key name apart on ':' + base_class, id, name = key.split(':') + + # Figure out the new name + new_key = redis_field_key(name, id=id, context=self) + + # Rename the key + warn "Rename '#{key}', '#{new_key}'" + ok = redis.rename(key, new_key) + warn "Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK' + end + break if cursor == "0" + end + + warn "Migrated #{total_keys} total number of redis keys" end def redis_options(name) klass = first_ancestor_with(name) return klass.redis_objects[name.to_sym] || {}