mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #43080 from rails/del-as-clear
Final touches deleting the classic autoloader
This commit is contained in:
commit
891189d9b8
7 changed files with 34 additions and 114 deletions
|
@ -8,11 +8,6 @@ module ActiveSupport # :nodoc:
|
|||
module Dependencies # :nodoc:
|
||||
require_relative "dependencies/require_dependency"
|
||||
|
||||
extend self
|
||||
|
||||
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
|
||||
private_constant :UNBOUND_METHOD_MODULE_NAME
|
||||
|
||||
mattr_accessor :interlock, default: Interlock.new
|
||||
|
||||
# :doc:
|
||||
|
@ -20,29 +15,25 @@ module ActiveSupport # :nodoc:
|
|||
# Execute the supplied block without interference from any
|
||||
# concurrent loads.
|
||||
def self.run_interlock
|
||||
Dependencies.interlock.running { yield }
|
||||
interlock.running { yield }
|
||||
end
|
||||
|
||||
# Execute the supplied block while holding an exclusive lock,
|
||||
# preventing any other thread from being inside a #run_interlock
|
||||
# block at the same time.
|
||||
def self.load_interlock
|
||||
Dependencies.interlock.loading { yield }
|
||||
interlock.loading { yield }
|
||||
end
|
||||
|
||||
# Execute the supplied block while holding an exclusive lock,
|
||||
# preventing any other thread from being inside a #run_interlock
|
||||
# block at the same time.
|
||||
def self.unload_interlock
|
||||
Dependencies.interlock.unloading { yield }
|
||||
interlock.unloading { yield }
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
|
||||
def eager_load?(path)
|
||||
Dependencies._eager_load_paths.member?(path)
|
||||
end
|
||||
|
||||
# The set of directories from which we may automatically load files. Files
|
||||
# under these directories will be reloaded on each request in development mode,
|
||||
# unless the directory also appears in autoload_once_paths.
|
||||
|
@ -62,23 +53,33 @@ module ActiveSupport # :nodoc:
|
|||
# main autoloader. Used to clear state.
|
||||
mattr_accessor :_autoloaded_tracked_classes, default: Set.new
|
||||
|
||||
# An array of qualified constant names that have been loaded. Adding a name
|
||||
# to this array will cause it to be unloaded the next time Dependencies are
|
||||
# cleared.
|
||||
mattr_accessor :autoloaded_constants, default: []
|
||||
|
||||
def clear
|
||||
# Private method that reloads constants autoloaded by the main autoloader.
|
||||
#
|
||||
# Rails.application.reloader.reload! is the public interface for application
|
||||
# reload. That involves more things, like deleting unloaded classes from the
|
||||
# internal state of the descendants tracker, or reloading routes.
|
||||
def self.clear
|
||||
unload_interlock do
|
||||
_autoloaded_tracked_classes.clear
|
||||
Rails.autoloaders.main.reload
|
||||
rescue Zeitwerk::ReloadingDisabledError
|
||||
raise "reloading is disabled because config.cache_classes is true"
|
||||
end
|
||||
end
|
||||
|
||||
# Search for a file in autoload_paths matching the provided suffix.
|
||||
def search_for_file(path_suffix)
|
||||
path_suffix += ".rb" unless path_suffix.end_with?(".rb")
|
||||
|
||||
autoload_paths.each do |root|
|
||||
path = File.join(root, path_suffix)
|
||||
return path if File.file? path
|
||||
# Private method used by require_dependency.
|
||||
def self.search_for_file(relpath)
|
||||
relpath += ".rb" unless relpath.end_with?(".rb")
|
||||
autoload_paths.each do |autoload_path|
|
||||
abspath = File.join(autoload_path, relpath)
|
||||
return abspath if File.file?(abspath)
|
||||
end
|
||||
nil # Gee, I sure wish we had first_match ;-)
|
||||
nil
|
||||
end
|
||||
|
||||
# Private method that helps configuring the autoloaders.
|
||||
def self.eager_load?(path)
|
||||
_eager_load_paths.member?(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,26 +6,6 @@ require "zeitwerk"
|
|||
module ActiveSupport
|
||||
module Dependencies
|
||||
module ZeitwerkIntegration # :nodoc: all
|
||||
module Decorations
|
||||
def clear
|
||||
Dependencies.unload_interlock do
|
||||
_autoloaded_tracked_classes.clear
|
||||
Rails.autoloaders.main.reload
|
||||
rescue Zeitwerk::ReloadingDisabledError
|
||||
raise "reloading is disabled because config.cache_classes is true"
|
||||
end
|
||||
end
|
||||
|
||||
def autoloaded_constants
|
||||
Rails.autoloaders.main.unloadable_cpaths
|
||||
end
|
||||
|
||||
def verbose=(verbose)
|
||||
l = verbose ? logger || Rails.logger : nil
|
||||
Rails.autoloaders.each { |autoloader| autoloader.logger = l }
|
||||
end
|
||||
end
|
||||
|
||||
module Inflector
|
||||
# Concurrent::Map is not needed. This is a private class, and overrides
|
||||
# must be defined while the application boots.
|
||||
|
@ -39,10 +19,6 @@ module ActiveSupport
|
|||
@overrides.merge!(overrides)
|
||||
end
|
||||
end
|
||||
|
||||
def self.take_over
|
||||
Dependencies.singleton_class.prepend(Decorations)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,26 +16,6 @@ module ModuleWithConstant
|
|||
InheritedConstant = "Hello"
|
||||
end
|
||||
|
||||
class DependenciesTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@loaded_features_copy = $LOADED_FEATURES.dup
|
||||
$LOAD_PATH << "test"
|
||||
end
|
||||
|
||||
teardown do
|
||||
ActiveSupport::Dependencies.clear
|
||||
$LOADED_FEATURES.replace(@loaded_features_copy)
|
||||
$LOAD_PATH.pop
|
||||
end
|
||||
|
||||
def test_smart_name_error_strings
|
||||
e = assert_raise NameError do
|
||||
Object.module_eval "ImaginaryObject"
|
||||
end
|
||||
assert_includes "uninitialized constant ImaginaryObject", e.message
|
||||
end
|
||||
end
|
||||
|
||||
class RequireDependencyTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@loaded_features_copy = $LOADED_FEATURES.dup
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "zeitwerk"
|
||||
require "active_support/core_ext/string/inflections"
|
||||
require "active_support/core_ext/array/conversions"
|
||||
require "active_support/descendants_tracker"
|
||||
require "active_support/dependencies"
|
||||
require "zeitwerk"
|
||||
|
||||
module Rails
|
||||
class Application
|
||||
|
@ -44,11 +44,6 @@ module Rails
|
|||
autoloader.setup
|
||||
end
|
||||
|
||||
initializer :let_zeitwerk_take_over do
|
||||
require "active_support/dependencies/zeitwerk_integration"
|
||||
ActiveSupport::Dependencies::ZeitwerkIntegration.take_over
|
||||
end
|
||||
|
||||
# Setup default session store if not already set in config/application.rb
|
||||
initializer :setup_default_session_store, before: :build_middleware_stack do |app|
|
||||
unless app.config.session_store?
|
||||
|
@ -179,6 +174,7 @@ module Rails
|
|||
# added in the hook are taken into account.
|
||||
initializer :set_clear_dependencies_hook, group: :all do |app|
|
||||
callback = lambda do
|
||||
# Order matters.
|
||||
ActiveSupport::DescendantsTracker.clear(
|
||||
only: ActiveSupport::Dependencies._autoloaded_tracked_classes
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "active_support/dependencies/zeitwerk_integration"
|
||||
require "zeitwerk"
|
||||
require "active_support/dependencies/zeitwerk_integration"
|
||||
|
||||
module Rails
|
||||
module Autoloaders # :nodoc:
|
||||
|
|
|
@ -440,6 +440,7 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
test "db:migrate set back connection to its original state" do
|
||||
require "#{app_path}/config/environment"
|
||||
Dir.chdir(app_path) do
|
||||
dummy_task = <<~RUBY
|
||||
task foo: :environment do
|
||||
|
@ -457,6 +458,7 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
test "db:migrate:name sets the connection back to its original state" do
|
||||
require "#{app_path}/config/environment"
|
||||
Dir.chdir(app_path) do
|
||||
dummy_task = <<~RUBY
|
||||
task foo: :environment do
|
||||
|
@ -1088,6 +1090,7 @@ module ApplicationTests
|
|||
end
|
||||
|
||||
test "when database_tasks is false, then do not run the database tasks on that db" do
|
||||
require "#{app_path}/config/environment"
|
||||
app_file "config/database.yml", <<-YAML
|
||||
development:
|
||||
primary:
|
||||
|
|
|
@ -23,14 +23,9 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
|
|||
ActiveSupport::Dependencies
|
||||
end
|
||||
|
||||
def decorated?
|
||||
deps.singleton_class < deps::ZeitwerkIntegration::Decorations
|
||||
end
|
||||
|
||||
test "ActiveSupport::Dependencies is decorated" do
|
||||
test "The integration is minimally looking good" do
|
||||
boot
|
||||
|
||||
assert decorated?
|
||||
assert Rails.autoloaders.zeitwerk_enabled?
|
||||
assert_instance_of Zeitwerk::Loader, Rails.autoloaders.main
|
||||
assert_instance_of Zeitwerk::Loader, Rails.autoloaders.once
|
||||
|
@ -111,37 +106,6 @@ class ZeitwerkIntegrationTest < ActiveSupport::TestCase
|
|||
assert Object.const_defined?(:MoneySerializer)
|
||||
end
|
||||
|
||||
test "unloadable constants (main)" do
|
||||
app_file "app/models/user.rb", "class User; end"
|
||||
app_file "app/models/post.rb", "class Post; end"
|
||||
boot
|
||||
|
||||
assert Post
|
||||
|
||||
assert_equal ["Post"], deps.autoloaded_constants
|
||||
end
|
||||
|
||||
test "unloadable constants (once)" do
|
||||
add_to_config 'config.autoload_once_paths << "#{Rails.root}/extras"'
|
||||
app_file "extras/foo.rb", "class Foo; end"
|
||||
app_file "extras/bar.rb", "class Bar; end"
|
||||
boot
|
||||
|
||||
assert Foo
|
||||
|
||||
assert_empty deps.autoloaded_constants
|
||||
end
|
||||
|
||||
test "unloadable constants (reloading disabled)" do
|
||||
app_file "app/models/user.rb", "class User; end"
|
||||
app_file "app/models/post.rb", "class Post; end"
|
||||
boot("production")
|
||||
|
||||
assert Post
|
||||
|
||||
assert_empty deps.autoloaded_constants
|
||||
end
|
||||
|
||||
test "eager loading loads the application code" do
|
||||
$zeitwerk_integration_test_user = false
|
||||
$zeitwerk_integration_test_post = false
|
||||
|
|
Loading…
Reference in a new issue