From 3f61a480671b58d5ccbdc6241ba22390a4c1d082 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Fri, 14 Nov 2025 11:48:28 -0800 Subject: [PATCH 01/14] Rename this unit test --- .../{redis_legacy_key_naming_spec.rb => redis_key_naming_spec.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/{redis_legacy_key_naming_spec.rb => redis_key_naming_spec.rb} (100%) diff --git a/spec/redis_legacy_key_naming_spec.rb b/spec/redis_key_naming_spec.rb similarity index 100% rename from spec/redis_legacy_key_naming_spec.rb rename to spec/redis_key_naming_spec.rb From 304b3770a75da8272158792754e7fb42c7cadcef Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Fri, 31 Oct 2025 15:43:10 -0700 Subject: [PATCH 02/14] README updated 2.0.0 release notes to better match behavior --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 73b4c36..d8453c6 100644 --- a/README.md +++ b/README.md @@ -15,19 +15,21 @@ explicitly in your Gemfile: gem 'redis-objects', '>= 2.0.0.beta' ~~~ You're encouraged to try it out in test code (not production) to ensure it works for you. -Official release is expected later in 2023. +Official release is expected eventually. Key Naming Changes ------------------ The internal key naming scheme has changed for `Nested::Class::Namespaces` to fix a longstanding bug. -**This means your existing data in Redis will not be accessible until you call `migrate_redis_legacy_keys`.** +(Refer to [#213](https://github.com/nateware/redis-objects/issues/231)) +If your Redis::Object enhanced classes are nested in this way then +**your existing data in Redis will not be accessible until you call `migrate_redis_legacy_keys`.** To fix this (only needed once), create a script like this: ~~~ruby class YouClassNameHere < ActiveRecord::Base include Redis::Objects - # ... your relevant definitions here ... + # ... your relevant redis_object definitions here (counters/sets) ... end YourClassName.migrate_redis_legacy_keys @@ -37,7 +39,7 @@ Then, you need to find a time when you can temporarily pause writes to your redi so that you can run that script. It uses `redis.scan` internally so it should be able to handle a high number of keys. For large data sets, it could take a while. -For more details on the issue and fix refer to [#213](https://github.com/nateware/redis-objects/issues/231). +Alternatively you can revert the behavior to the old prefix scheme by setting `redis_legacy_naming = true`. Use at your own risk. Renaming of `lock` Method ------------------------- From 8579b66977cad672ede1b6741e08698e16f8c144 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Fri, 14 Nov 2025 13:25:50 -0800 Subject: [PATCH 03/14] spacing --- lib/redis/objects.rb | 2 +- spec/redis_key_naming_spec.rb | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/redis/objects.rb b/lib/redis/objects.rb index c526078..b4f38d7 100644 --- a/lib/redis/objects.rb +++ b/lib/redis/objects.rb @@ -140,7 +140,7 @@ def redis_legacy_prefix(klass = self) #:nodoc: downcase end - # Temporary warning to help with migrating key names + # 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 diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 85c1ebb..511cc0c 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -185,7 +185,7 @@ class NamingFive include Redis::Objects self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true - + def id 1 end @@ -328,7 +328,7 @@ def id list :global_list, :global => true set :global_set, :global => true sorted_set :global_sorted_set, :global => true - + #callable as key value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" } end @@ -360,7 +360,7 @@ def id list :global_list, :global => true set :global_set, :global => true sorted_set :global_sorted_set, :global => true - + #callable as key value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" } end @@ -416,4 +416,5 @@ def id obj.global_set.should.include?('b').should == true obj.global_sorted_set[:key].should == 2.2 end + end From 14bb5369543f2f38f1f16f9916d222531ecc6ead Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Tue, 11 Nov 2025 11:58:25 -0800 Subject: [PATCH 04/14] move variables --- lib/redis/objects.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/redis/objects.rb b/lib/redis/objects.rb index b4f38d7..fb486f4 100644 --- a/lib/redis/objects.rb +++ b/lib/redis/objects.rb @@ -159,14 +159,15 @@ def redis_legacy_naming_warning_message(klass) 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 "[redis-objects] Migrating keys from #{legacy} prefix to #{redis_prefix}" + cursor = 0 + total_keys = 0 + loop do cursor, keys = redis.scan(cursor, :match => "#{legacy}:*") total_keys += keys.length From 288742adce4a26c6d4bffbca601dd4a94e231558 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Fri, 31 Oct 2025 16:07:59 -0700 Subject: [PATCH 05/14] redis_legacy_naming_warning_message uses guard clause return Less indentation needed. --- lib/redis/objects.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/redis/objects.rb b/lib/redis/objects.rb index fb486f4..4c70d8e 100644 --- a/lib/redis/objects.rb +++ b/lib/redis/objects.rb @@ -143,19 +143,19 @@ def redis_legacy_prefix(klass = self) #:nodoc: # 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 < Date: Fri, 14 Nov 2025 13:47:20 -0800 Subject: [PATCH 06/14] Use Redis::Objects.prefix_style to manage legacy => modern prefix update And updated the README for revised logic --- README.md | 17 +++++++++++------ lib/redis/objects.rb | 26 ++++++++++++++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d8453c6..a48f79f 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,14 @@ Official release is expected eventually. Key Naming Changes ------------------ -The internal key naming scheme has changed for `Nested::Class::Namespaces` to fix a longstanding bug. -(Refer to [#213](https://github.com/nateware/redis-objects/issues/231)) -If your Redis::Object enhanced classes are nested in this way then -**your existing data in Redis will not be accessible until you call `migrate_redis_legacy_keys`.** +A new method to generate the internal key naming scheme has been added to fix a +longstanding bug. (Refer to [#213](https://github.com/nateware/redis-objects/issues/231)) +It can be controlled by `Redis::Objects.prefix_style =` either `:legacy` (default) +or `:modern`. +If your Redis::Object enhanced classes are nested such as `Nested::Class::Namespaces` +then you should upgrade them using the below proceedure. (only needed once) -To fix this (only needed once), create a script like this: +Create a script like this: ~~~ruby class YouClassNameHere < ActiveRecord::Base @@ -39,7 +41,10 @@ Then, you need to find a time when you can temporarily pause writes to your redi so that you can run that script. It uses `redis.scan` internally so it should be able to handle a high number of keys. For large data sets, it could take a while. -Alternatively you can revert the behavior to the old prefix scheme by setting `redis_legacy_naming = true`. Use at your own risk. +After migrating all of your redis keys, update `Redis::Objects.prefix_style = :modern` to +start using the new keys. +**Your existing data in Redis will not be accessible after running `migrate_redis_legacy_keys` +until the prefix_style has been changed.** Renaming of `lock` Method ------------------------- diff --git a/lib/redis/objects.rb b/lib/redis/objects.rb index 4c70d8e..cc145ec 100644 --- a/lib/redis/objects.rb +++ b/lib/redis/objects.rb @@ -66,6 +66,19 @@ def redis raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection") end + # Toggles whether to use the legacy redis key naming scheme, which causes + # naming conflicts in certain cases. + # (attr_accessor with a default value) + attr_writer :prefix_style + def prefix_style + # NOTE: In a future release the default will change to :modern + @prefix_style ||= :legacy + end + + def redis_legacy_naming? + prefix_style == :legacy + end + def included(klass) # Core (this file) klass.instance_variable_set(:@redis, nil) @@ -101,11 +114,6 @@ def redis_objects @redis_objects ||= {} 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 @@ -114,10 +122,10 @@ def redis_prefix=(redis_prefix) def redis_prefix(klass = self) #:nodoc: @redis_prefix ||= - if redis_legacy_naming + if Objects.redis_legacy_naming? + redis_legacy_naming_warning_message(klass) redis_legacy_prefix(klass) else - redis_legacy_naming_warning_message(klass) redis_modern_prefix(klass) end @@ -140,10 +148,12 @@ def redis_legacy_prefix(klass = self) #:nodoc: downcase end + attr_accessor :redis_silence_warnings + # 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 - return if redis_legacy_naming || redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually + return if redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually modern = redis_modern_prefix(klass) legacy = redis_legacy_prefix(klass) From bdb302023919471959030f0e500ba279963a80df Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Fri, 14 Nov 2025 12:03:25 -0800 Subject: [PATCH 07/14] Fix migrate_redis_legacy_keys to temporarily use ":modern" prefix --- lib/redis/objects.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/redis/objects.rb b/lib/redis/objects.rb index cc145ec..320b637 100644 --- a/lib/redis/objects.rb +++ b/lib/redis/objects.rb @@ -169,6 +169,10 @@ def redis_legacy_naming_warning_message(klass) end def migrate_redis_legacy_keys + unless Objects.redis_legacy_naming? + raise "Redis::Objects is already configured to use modern key prefixes." + end + legacy = redis_legacy_prefix 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})" @@ -178,6 +182,10 @@ def migrate_redis_legacy_keys 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}:*") total_keys += keys.length @@ -194,6 +202,10 @@ def migrate_redis_legacy_keys warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK' end break if cursor == "0" + + ensure + # Change the prefix back (just in case) + self.redis_prefix = legacy end warn "[redis-objects] Migrated #{total_keys} total number of redis keys" From 0f1498712924e741b4a90ae5961911c02fedc023 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Thu, 13 Nov 2025 15:45:18 -0800 Subject: [PATCH 08/14] Require the duration extensions Dunno how the tests passed previously without this --- spec/spec_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3002d8a..3a77d91 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,8 +24,9 @@ #require "active_support/xml_mini" require "active_support" +require "active_support/core_ext/integer/time" # needed for 1.second 1.minute etc durations to function require "active_support/testing/time_helpers" -include ActiveSupport::Testing::TimeHelpers +include ActiveSupport::Testing::TimeHelpers # needed by one line in redis_objects_model_spec.rb REDIS_CLASS_NAMES = [:Counter, :HashKey, :List, :Lock, :Set, :SortedSet, :Value] From 019a3447d7419d3c2333ebd6e847a80610e995e9 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Tue, 11 Nov 2025 11:17:44 -0800 Subject: [PATCH 09/14] Nest the key_naming_spec tests contexts don't exist in "bacon", so use nested "describe" blocks instead. --- spec/redis_key_naming_spec.rb | 375 ++++++++++++++++++---------------- 1 file changed, 197 insertions(+), 178 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 511cc0c..1700f09 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -19,169 +19,209 @@ def capture_stderr $stderr = previous_stderr end -describe 'Legacy redis key prefix naming compatibility' do - it 'verifies single level classes work the same' do - class SingleLevelOne - include Redis::Objects +describe 'Redis key prefix naming compatibility' do - def id - 1 - end - end - - obj = SingleLevelOne.new - obj.class.redis_prefix.should == 'single_level_one' - end - - it 'verifies single level classes obey the legacy naming flag' do - class SingleLevelTwo - include Redis::Objects - self.redis_legacy_naming = true - - def id - 1 - end - end + describe 'verifies single level classes' do # context - obj = SingleLevelTwo.new - obj.class.redis_prefix.should == 'single_level_two' - end - - it 'verifies nested classes do NOT work the same' do - module Nested - class NamingOne + it 'work the same' do + class SingleLevelOne include Redis::Objects - self.redis_silence_warnings = true - + def id 1 end end - end - obj = Nested::NamingOne.new - obj.class.redis_prefix.should == 'nested__naming_one' - end + obj = SingleLevelOne.new + obj.class.redis_prefix.should == 'single_level_one' + end - it 'verifies the legacy naming flag is respected' do - module Nested - class NamingTwo + it 'obey the legacy naming flag' do + class SingleLevelTwo include Redis::Objects self.redis_legacy_naming = true - self.redis_silence_warnings = true - + def id 1 end end + + obj = SingleLevelTwo.new + obj.class.redis_prefix.should == 'single_level_two' end - Nested::NamingTwo.redis_legacy_naming.should == true - obj = Nested::NamingTwo.new - obj.class.redis_prefix.should == 'naming_two' - end + end # context + + describe 'verifies nested classes' do # context - it 'verifies that multiple levels respect __ vs _' do - module NestedLevel - module Further - class NamingThree + it 'do NOT work the same' do + module Nested + class NamingOne include Redis::Objects self.redis_silence_warnings = true - + def id 1 end end end - end - obj = NestedLevel::Further::NamingThree.new - obj.class.redis_prefix.should == 'nested_level__further__naming_three' - end + obj = Nested::NamingOne.new + obj.class.redis_prefix.should == 'nested__naming_one' + end - it 'verifies that multiple levels respect the legacy naming' do - module NestedLevel - module Further - class NamingFour + it 'respect the legacy naming flag' do + module Nested + class NamingTwo include Redis::Objects self.redis_legacy_naming = true - + self.redis_silence_warnings = true + def id 1 end + end + end + + Nested::NamingTwo.redis_legacy_naming.should == true + obj = Nested::NamingTwo.new + obj.class.redis_prefix.should == 'naming_two' + end + + end # context - redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31) - value :redis_value, :redis => redis_handle + describe 'verifies that multiple levels' do # context + + it 'respect __ vs _' do + module NestedLevel + module Further + class NamingThree + include Redis::Objects + self.redis_silence_warnings = true + + def id + 1 + end + end end end + + obj = NestedLevel::Further::NamingThree.new + obj.class.redis_prefix.should == 'nested_level__further__naming_three' end - NestedLevel::Further::NamingFour.redis_legacy_naming.should == true - obj = NestedLevel::Further::NamingFour.new - obj.class.redis_prefix.should == 'naming_four' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'naming_four:1:redis_value' - end + it 'respect the legacy naming' do + module NestedLevel + module Further + class NamingFour + include Redis::Objects + self.redis_legacy_naming = true - it 'verifies that multiple levels do not conflict 1' do - module NestedLevel - module Further - class NamingFive - include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) - self.redis_silence_warnings = true - - def id - 1 + def id + 1 + end + + redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT, :db => 31) + value :redis_value, :redis => redis_handle end + end + end - value :redis_value + NestedLevel::Further::NamingFour.redis_legacy_naming.should == true + obj = NestedLevel::Further::NamingFour.new + obj.class.redis_prefix.should == 'naming_four' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'naming_four:1:redis_value' + end + + it 'do not conflict 1' do + module NestedLevel + module Further + class NamingFive + include Redis::Objects + self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) + self.redis_silence_warnings = true + + def id + 1 + end + + value :redis_value + end end end + + obj = NestedLevel::Further::NamingFive.new + obj.class.redis_prefix.should == 'nested_level__further__naming_five' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'nested_level__further__naming_five:1:redis_value' + obj.redis_value.redis.should == obj.redis + obj.redis.get('nested_level__further__naming_five:1:redis_value').should == val end - obj = NestedLevel::Further::NamingFive.new - obj.class.redis_prefix.should == 'nested_level__further__naming_five' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'nested_level__further__naming_five:1:redis_value' - obj.redis_value.redis.should == obj.redis - obj.redis.get('nested_level__further__naming_five:1:redis_value').should == val - end + it 'do not conflict 2' do + module Nested + module LevelFurtherNaming + class Five + include Redis::Objects + self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) + self.redis_silence_warnings = true - it 'verifies that multiple levels do not conflict 2' do - module Nested - module LevelFurtherNaming - class Five - include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) - self.redis_silence_warnings = true - - def id - 1 + def id + 1 + end + + value :redis_value end + end + end - value :redis_value + obj = Nested::LevelFurtherNaming::Five.new + obj.class.redis_prefix.should == 'nested__level_further_naming__five' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'nested__level_further_naming__five:1:redis_value' + obj.redis.get('nested__level_further_naming__five:1:redis_value').should == val + end + + it 'do not conflict 3' do + module Nested + module LevelFurther + class NamingFive + include Redis::Objects + self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) + self.redis_silence_warnings = true + + def id + 1 + end + + value :redis_value + end end end + + obj = Nested::LevelFurther::NamingFive.new + obj.class.redis_prefix.should == 'nested__level_further__naming_five' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'nested__level_further__naming_five:1:redis_value' + obj.redis.get('nested__level_further__naming_five:1:redis_value').should == val end - obj = Nested::LevelFurtherNaming::Five.new - obj.class.redis_prefix.should == 'nested__level_further_naming__five' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'nested__level_further_naming__five:1:redis_value' - obj.redis.get('nested__level_further_naming__five:1:redis_value').should == val - end + end # context - it 'verifies that multiple levels do not conflict 3' do - module Nested - module LevelFurther - class NamingFive + describe 'handles dynamically created classes correctly' do # context + + it 'in modern mode' do + module Nested + class LevelSix include Redis::Objects self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true @@ -193,83 +233,62 @@ def id value :redis_value end end - end - obj = Nested::LevelFurther::NamingFive.new - obj.class.redis_prefix.should == 'nested__level_further__naming_five' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'nested__level_further__naming_five:1:redis_value' - obj.redis.get('nested__level_further__naming_five:1:redis_value').should == val - end - - it 'handles dynamically created classes correctly' do - module Nested - class LevelSix - include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) - self.redis_silence_warnings = true - - def id - 1 - end + obj = Nested::LevelSix.new + obj.class.redis_prefix.should == 'nested__level_six' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'nested__level_six:1:redis_value' + obj.redis.get('nested__level_six:1:redis_value').should == val + + DynamicClass = Class.new(Nested::LevelSix) + DynamicClass.value :redis_value2 + obj2 = DynamicClass.new + DynamicClass.redis_prefix.should == 'dynamic_class' + obj2.redis_value.should.be.kind_of(Redis::Value) + obj2.redis_value2.should.be.kind_of(Redis::Value) + obj2.redis_value.key.should == 'dynamic_class:1:redis_value' + obj2.redis_value2.key.should == 'dynamic_class:1:redis_value2' - value :redis_value - end end - obj = Nested::LevelSix.new - obj.class.redis_prefix.should == 'nested__level_six' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'nested__level_six:1:redis_value' - obj.redis.get('nested__level_six:1:redis_value').should == val - - DynamicClass = Class.new(Nested::LevelSix) - DynamicClass.value :redis_value2 - obj2 = DynamicClass.new - DynamicClass.redis_prefix.should == 'dynamic_class' - obj2.redis_value.should.be.kind_of(Redis::Value) - obj2.redis_value2.should.be.kind_of(Redis::Value) - obj2.redis_value.key.should == 'dynamic_class:1:redis_value' - obj2.redis_value2.key.should == 'dynamic_class:1:redis_value2' - - end + it 'in legacy mode' do + module Nested + class LevelSeven + include Redis::Objects + self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) + self.redis_legacy_naming = true - it 'handles dynamically created classes correctly in legacy mode' do - module Nested - class LevelSeven - include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) - self.redis_legacy_naming = true + def id + 1 + end - def id - 1 + value :redis_value end - - value :redis_value end + + obj = Nested::LevelSeven.new + obj.class.redis_prefix.should == 'level_seven' + val = SecureRandom.hex(10) + obj.redis_value = val + obj.redis_value.should == val + obj.redis_value.key.should == 'level_seven:1:redis_value' + obj.redis.get('level_seven:1:redis_value').should == val + + DynamicClass2 = Class.new(Nested::LevelSeven) + DynamicClass2.value :redis_value2 + obj2 = DynamicClass2.new + DynamicClass2.redis_prefix.should == 'dynamic_class2' + obj2.redis_value.should.be.kind_of(Redis::Value) + obj2.redis_value2.should.be.kind_of(Redis::Value) + obj2.redis_value.key.should == 'dynamic_class2:1:redis_value' + obj2.redis_value2.key.should == 'dynamic_class2:1:redis_value2' end - obj = Nested::LevelSeven.new - obj.class.redis_prefix.should == 'level_seven' - val = SecureRandom.hex(10) - obj.redis_value = val - obj.redis_value.should == val - obj.redis_value.key.should == 'level_seven:1:redis_value' - obj.redis.get('level_seven:1:redis_value').should == val - - DynamicClass2 = Class.new(Nested::LevelSeven) - DynamicClass2.value :redis_value2 - obj2 = DynamicClass2.new - DynamicClass2.redis_prefix.should == 'dynamic_class2' - obj2.redis_value.should.be.kind_of(Redis::Value) - obj2.redis_value2.should.be.kind_of(Redis::Value) - obj2.redis_value.key.should == 'dynamic_class2:1:redis_value' - obj2.redis_value2.key.should == 'dynamic_class2:1:redis_value2' - end + end # context + + # ---- other tests ---- it 'prints a warning message if the key name changes' do module Nested From 84687eec7d5dc0bdde5e02730c933d0302e86a69 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Thu, 13 Nov 2025 22:49:18 -0800 Subject: [PATCH 10/14] Remove pointless assertions --- spec/redis_key_naming_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 1700f09..b012350 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -83,7 +83,6 @@ def id end end - Nested::NamingTwo.redis_legacy_naming.should == true obj = Nested::NamingTwo.new obj.class.redis_prefix.should == 'naming_two' end @@ -127,7 +126,6 @@ def id end end - NestedLevel::Further::NamingFour.redis_legacy_naming.should == true obj = NestedLevel::Further::NamingFour.new obj.class.redis_prefix.should == 'naming_four' val = SecureRandom.hex(10) From f285105915e2e8a44c2d8e18062a3eff314d5995 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Thu, 13 Nov 2025 14:49:51 -0800 Subject: [PATCH 11/14] Remove nonsense `should`s --- spec/redis_key_naming_spec.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index b012350..8ec3867 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -385,7 +385,7 @@ def id # Iterate over them Nested::Modern.redis_objects.length.should == 13 - Nested::Modern.redis_objects.length.should == Nested::Legacy.redis_objects.length.should + Nested::Modern.redis_objects.length.should == Nested::Legacy.redis_objects.length Nested::Legacy.redis_prefix.should == 'modern' Nested::Modern.redis_prefix.should == 'nested__modern' @@ -394,7 +394,6 @@ def id # warn i.inspect obj = Nested::Legacy.new(i) obj.redis_value = i - obj.redis_value.to_i.should == i obj.redis_counter.increment obj.redis_hash[:key] = i obj.redis_list << i @@ -429,8 +428,8 @@ def id obj.global_value.to_i.should == 42 obj.global_counter.to_i.should == 3 obj.global_hash_key[:key].should == 'value' - obj.global_set.should.include?('a').should == true - obj.global_set.should.include?('b').should == true + obj.global_set.include?('a').should == true + obj.global_set.include?('b').should == true obj.global_sorted_set[:key].should == 2.2 end From d6ed4817618bf8725e78d33caa22db46a1cefa7a Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Tue, 11 Nov 2025 12:33:17 -0800 Subject: [PATCH 12/14] More nonsense, redis host is already defined globally --- spec/redis_key_naming_spec.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 8ec3867..582cb27 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -139,7 +139,6 @@ module NestedLevel module Further class NamingFive include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true def id @@ -166,7 +165,6 @@ module Nested module LevelFurtherNaming class Five include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true def id @@ -192,7 +190,6 @@ module Nested module LevelFurther class NamingFive include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true def id @@ -221,7 +218,6 @@ def id module Nested class LevelSix include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_silence_warnings = true def id @@ -255,7 +251,6 @@ def id module Nested class LevelSeven include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_legacy_naming = true def id @@ -292,7 +287,6 @@ def id module Nested class LevelNine include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) def id 1 @@ -317,7 +311,6 @@ def id module Nested class Legacy include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) self.redis_legacy_naming = true # override this for testing - need two classes as if we imagine an old and new one @@ -354,7 +347,6 @@ def id module Nested class Modern include Redis::Objects - self.redis = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) def initialize(id) @id = id From affca839946817decb429de3db1512c2bb82f933 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Tue, 11 Nov 2025 12:33:17 -0800 Subject: [PATCH 13/14] Update key_naming migration method test - Only using a single class now named `UpgradeTest` - Class is deleted and recreated to switch between legacy & modern conventions --- spec/redis_key_naming_spec.rb | 123 +++++++++++++++------------------- 1 file changed, 53 insertions(+), 70 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 582cb27..1b184de 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -308,83 +308,56 @@ def id end it 'supports a method to migrate legacy key names' do + module Nested - class Legacy - include Redis::Objects - self.redis_legacy_naming = true + def self.make_class + # TODO notes + klass = Class.new do + def initialize(id) + @id = id + end + def id + @id + end - # override this for testing - need two classes as if we imagine an old and new one - # also use the legacy flat prefix that ignores the nested class name - self.redis_prefix = 'modern' + include Redis::Objects - def initialize(id) - @id = id - end - def id - @id - end + self.redis_silence_warnings = true - value :redis_value - counter :redis_counter - hash_key :redis_hash - list :redis_list - set :redis_set - sorted_set :redis_sorted_set - - # global class counters - value :global_value, :global => true - counter :global_counter, :global => true - hash_key :global_hash_key, :global => true - list :global_list, :global => true - set :global_set, :global => true - sorted_set :global_sorted_set, :global => true - - #callable as key - value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" } + value :redis_value + counter :redis_counter + hash_key :redis_hash + list :redis_list + set :redis_set + sorted_set :redis_sorted_set + + # global class counters + value :global_value, :global => true + counter :global_counter, :global => true + hash_key :global_hash_key, :global => true + list :global_list, :global => true + set :global_set, :global => true + sorted_set :global_sorted_set, :global => true + + # use a callable as the key + value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" } + end end end - module Nested - class Modern - include Redis::Objects - - def initialize(id) - @id = id - end - def id - @id - end + # First define the class using legacy prefix + Redis::Objects.prefix_style = :legacy + Nested::UpgradeTest = Nested.make_class - value :redis_value - counter :redis_counter - hash_key :redis_hash - list :redis_list - set :redis_set - sorted_set :redis_sorted_set - - # global class counters - value :global_value, :global => true - counter :global_counter, :global => true - hash_key :global_hash_key, :global => true - list :global_list, :global => true - set :global_set, :global => true - sorted_set :global_sorted_set, :global => true - - #callable as key - value :global_proc_value, :global => true, :key => Proc.new { |roster| "#{roster.name}:#{Time.now.strftime('%Y-%m-%dT%H')}:daily" } - end - end + # Sanity checks + Nested::UpgradeTest.redis_objects.length.should == 13 + Nested::UpgradeTest.redis_prefix.should == 'upgrade_test' - # Iterate over them - Nested::Modern.redis_objects.length.should == 13 - Nested::Modern.redis_objects.length.should == Nested::Legacy.redis_objects.length - Nested::Legacy.redis_prefix.should == 'modern' - Nested::Modern.redis_prefix.should == 'nested__modern' + # Create a whole bunch of keys using the legacy prefixed keys - # Create a whole bunch of keys using the legacy names 30.times do |i| # warn i.inspect - obj = Nested::Legacy.new(i) + obj = Nested::UpgradeTest.new(i) obj.redis_value = i obj.redis_counter.increment obj.redis_hash[:key] = i @@ -393,7 +366,7 @@ def id obj.redis_sorted_set[i] = i end - obj = Nested::Legacy.new(99) + obj = Nested::UpgradeTest.new(99) obj.global_value = 42 obj.global_counter.increment obj.global_counter.increment @@ -402,12 +375,22 @@ def id obj.global_set << 'a' << 'b' obj.global_sorted_set[:key] = 2.2 - Nested::Modern.migrate_redis_legacy_keys + # Run the upgrade + Nested::UpgradeTest.migrate_redis_legacy_keys + + # Re-Create the class using modern prefix + Nested.send(:remove_const, :UpgradeTest) + Redis::Objects.prefix_style = :modern + Nested::UpgradeTest = Nested.make_class + + # Sanity checks + Nested::UpgradeTest.redis_objects.length.should == 13 + Nested::UpgradeTest.redis_prefix.should == 'nested__upgrade_test' - # Try to access the keys through modern names now + # Try to access the keys through modern prefixed keys now 30.times do |i| # warn i.inspect - obj = Nested::Modern.new(i) + obj = Nested::UpgradeTest.new(i) obj.redis_value.to_i.should == i obj.redis_counter.to_i.should == 1 obj.redis_hash[:key].to_i.should == i @@ -416,7 +399,7 @@ def id obj.redis_sorted_set[i].should == i end - obj = Nested::Modern.new(99) + obj = Nested::UpgradeTest.new(99) obj.global_value.to_i.should == 42 obj.global_counter.to_i.should == 3 obj.global_hash_key[:key].should == 'value' From 28ccf6f5d1edd14a9e508ed3c8b0dc5a26595215 Mon Sep 17 00:00:00 2001 From: Matthew Hively Date: Thu, 13 Nov 2025 23:07:40 -0800 Subject: [PATCH 14/14] Update the other key_naming tests --- spec/redis_key_naming_spec.rb | 46 +++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/spec/redis_key_naming_spec.rb b/spec/redis_key_naming_spec.rb index 1b184de..60cc858 100644 --- a/spec/redis_key_naming_spec.rb +++ b/spec/redis_key_naming_spec.rb @@ -23,7 +23,9 @@ def capture_stderr describe 'verifies single level classes' do # context - it 'work the same' do + it 'work the same (modern)' do + Redis::Objects.prefix_style = :modern + class SingleLevelOne include Redis::Objects @@ -36,10 +38,11 @@ def id obj.class.redis_prefix.should == 'single_level_one' end - it 'obey the legacy naming flag' do + it 'work the same (legacy)' do + Redis::Objects.prefix_style = :legacy + class SingleLevelTwo include Redis::Objects - self.redis_legacy_naming = true def id 1 @@ -54,11 +57,12 @@ def id describe 'verifies nested classes' do # context - it 'do NOT work the same' do + it 'do NOT work the same (modern)' do + Redis::Objects.prefix_style = :modern + module Nested class NamingOne include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -70,11 +74,12 @@ def id obj.class.redis_prefix.should == 'nested__naming_one' end - it 'respect the legacy naming flag' do + it 'do NOT work the same (legacy)' do + Redis::Objects.prefix_style = :legacy + module Nested class NamingTwo include Redis::Objects - self.redis_legacy_naming = true self.redis_silence_warnings = true def id @@ -92,11 +97,12 @@ def id describe 'verifies that multiple levels' do # context it 'respect __ vs _' do + Redis::Objects.prefix_style = :modern + module NestedLevel module Further class NamingThree include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -109,12 +115,14 @@ def id obj.class.redis_prefix.should == 'nested_level__further__naming_three' end - it 'respect the legacy naming' do + it 'respect legacy naming' do + Redis::Objects.prefix_style = :legacy + module NestedLevel module Further class NamingFour include Redis::Objects - self.redis_legacy_naming = true + self.redis_silence_warnings = true def id 1 @@ -135,11 +143,12 @@ def id end it 'do not conflict 1' do + Redis::Objects.prefix_style = :modern + module NestedLevel module Further class NamingFive include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -161,11 +170,12 @@ def id end it 'do not conflict 2' do + Redis::Objects.prefix_style = :modern + module Nested module LevelFurtherNaming class Five include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -186,11 +196,12 @@ def id end it 'do not conflict 3' do + Redis::Objects.prefix_style = :modern + module Nested module LevelFurther class NamingFive include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -215,10 +226,11 @@ def id describe 'handles dynamically created classes correctly' do # context it 'in modern mode' do + Redis::Objects.prefix_style = :modern + module Nested class LevelSix include Redis::Objects - self.redis_silence_warnings = true def id 1 @@ -248,10 +260,12 @@ def id end it 'in legacy mode' do + Redis::Objects.prefix_style = :legacy + module Nested class LevelSeven include Redis::Objects - self.redis_legacy_naming = true + self.redis_silence_warnings = true def id 1 @@ -284,6 +298,8 @@ def id # ---- other tests ---- it 'prints a warning message if the key name changes' do + Redis::Objects.prefix_style = :legacy + module Nested class LevelNine include Redis::Objects