From 6010192ce8971a0ceeda6d7a946bd19cd924d85b Mon Sep 17 00:00:00 2001 From: Shinichi Maeshima Date: Thu, 2 May 2024 19:13:22 +0900 Subject: [PATCH] Fix SystemStackError when extending the reload method with Module#prepend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For example, when using the master branch of activerecord-multi-tenant, if activerecord-multi-tenant and attr_encrypted are listed in the Gemfile in that order, calling the reload method raises a SystemStackError. This happens because activerecord-multi-tenant extends Active Record’s reload method using prepend, while attr_encrypted extends it using an alias method. Here’s an example of how extending the same method with both prepend and alias methods in that order can result in a SystemStackError ``` class Hello def hello 'hello' end end Hello.prepend(Module.new do def hello super end end) Hello.class_eval do alias orig_hello hello def hello "#{orig_hello} world" end end Hello.new.hello #=> SystemStackError ``` However, reversing the order works: ``` class Hello def hello 'hello' end end Hello.class_eval do alias orig_hello hello def hello "#{orig_hello} world" end end Hello.prepend(Module.new do def hello super end end) Hello.new.hello #=> "hello world" ``` This issue can be resolved by standardizing the method extension to use prepend to avoid conflicts. --- lib/attr_encrypted/adapters/active_record.rb | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/attr_encrypted/adapters/active_record.rb b/lib/attr_encrypted/adapters/active_record.rb index 62cec13d..c1ee258a 100644 --- a/lib/attr_encrypted/adapters/active_record.rb +++ b/lib/attr_encrypted/adapters/active_record.rb @@ -6,19 +6,18 @@ module Adapters module ActiveRecord RAILS_VERSION = Gem::Version.new(::ActiveRecord::VERSION::STRING).freeze - def self.extended(base) # :nodoc: - base.class_eval do - - # https://github.com/attr-encrypted/attr_encrypted/issues/68 - alias_method :reload_without_attr_encrypted, :reload - def reload(*args, &block) - result = reload_without_attr_encrypted(*args, &block) - self.class.attr_encrypted_encrypted_attributes.keys.each do |attribute_name| - instance_variable_set("@#{attribute_name}", nil) - end - result + module Reload + def reload(...) + result = super + self.class.attr_encrypted_encrypted_attributes.keys.each do |attribute_name| + instance_variable_set("@#{attribute_name}", nil) end + result + end + end + def self.extended(base) # :nodoc: + base.class_eval do attr_encrypted_options[:encode] = true class << self @@ -148,5 +147,6 @@ def method_missing_with_attr_encrypted(method, *args, &block) ActiveSupport.on_load(:active_record) do extend AttrEncrypted extend AttrEncrypted::Adapters::ActiveRecord + prepend AttrEncrypted::Adapters::ActiveRecord::Reload end end