Implement support for before_remove_const in zeitwek mode

This needs Zeitwerk 2.5 for the on_unload hook.
This commit is contained in:
Xavier Noria 2021-08-09 13:15:10 +02:00
parent 6b49f33054
commit 36e716cdc1
3 changed files with 45 additions and 10 deletions

View File

@ -1266,17 +1266,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal c1, c2
end
def test_current_scope_is_reset
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
UnloadablePost.current_scope = UnloadablePost.all
def test_before_remove_const_resets_the_current_scope
# Done this way because a class cannot be defined in a method using the
# class keyword.
Object.const_set(:ReloadableModel, Class.new(ActiveRecord::Base))
ReloadableModel.current_scope = ReloadableModel.all
assert_not_nil ActiveRecord::Scoping::ScopeRegistry.current_scope(ReloadableModel) # precondition
UnloadablePost.unloadable
klass = UnloadablePost
assert_not_nil ActiveRecord::Scoping::ScopeRegistry.current_scope(klass)
ActiveSupport::Dependencies.remove_unloadable_constants!
assert_nil ActiveRecord::Scoping::ScopeRegistry.current_scope(klass)
ReloadableModel.before_remove_const
assert_nil ActiveRecord::Scoping::ScopeRegistry.current_scope(ReloadableModel)
ensure
Object.class_eval { remove_const :UnloadablePost } if defined?(UnloadablePost)
Object.send(:remove_const, :ReloadableModel)
end
def test_marshal_round_trip

View File

@ -89,7 +89,13 @@ module ActiveSupport
autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path)
end
Rails.autoloaders.main.enable_reloading if enable_reloading
if enable_reloading
Rails.autoloaders.main.enable_reloading
Rails.autoloaders.main.on_unload do |_cpath, value, _abspath|
value.before_remove_const if value.respond_to?(:before_remove_const)
end
end
Rails.autoloaders.each(&:setup)
end

View File

@ -341,6 +341,34 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
assert_equal :no_op, deps.unhook!
end
test "reloading invokes before_remove_const" do
$before_remove_const_invoked = false
app_file "app/models/foo.rb", <<~RUBY
# While the most common use case is classes/modules, the contract does not
# require values to be so. Let's weaken the test down to Object.new.
Foo = Object.new
def Foo.before_remove_const
$before_remove_const_invoked = true
end
RUBY
app_file "app/models/bar.rb", <<~RUBY
# This object does not implement before_remove_const. We define it to make
# sure reloading does not raise. That is, it does not blindly invoke the
# hook on all unloaded objects.
Bar = Object.new
RUBY
boot
assert Foo
assert Bar
ActiveSupport::Dependencies.clear
assert $before_remove_const_invoked
end
test "autoloaders.logger=" do
boot