lib/active_support/dependencies.rb in activesupport-6.0.6.1 vs lib/active_support/dependencies.rb in activesupport-6.1.0.rc1
- old
+ new
@@ -10,11 +10,10 @@
require "active_support/core_ext/module/anonymous"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/load_error"
require "active_support/core_ext/name_error"
-require "active_support/core_ext/string/starts_ends_with"
require "active_support/dependencies/interlock"
require "active_support/inflector"
module ActiveSupport #:nodoc:
module Dependencies #:nodoc:
@@ -260,19 +259,28 @@
Dependencies.require_or_load(file_name)
end
# :doc:
- # Interprets a file using <tt>mechanism</tt> and marks its defined
- # constants as autoloaded. <tt>file_name</tt> can be either a string or
+ # <b>Warning:</b> This method is obsolete in +:zeitwerk+ mode. In
+ # +:zeitwerk+ mode semantics match Ruby's and you do not need to be
+ # defensive with load order. Just refer to classes and modules normally.
+ # If the constant name is dynamic, camelize if needed, and constantize.
+ #
+ # In +:classic+ mode, interprets a file using +mechanism+ and marks its
+ # defined constants as autoloaded. +file_name+ can be either a string or
# respond to <tt>to_path</tt>.
#
- # Use this method in code that absolutely needs a certain constant to be
- # defined at that point. A typical use case is to make constant name
- # resolution deterministic for constants with the same relative name in
- # different namespaces whose evaluation would depend on load order
- # otherwise.
+ # In +:classic+ mode, use this method in code that absolutely needs a
+ # certain constant to be defined at that point. A typical use case is to
+ # make constant name resolution deterministic for constants with the same
+ # relative name in different namespaces whose evaluation would depend on
+ # load order otherwise.
+ #
+ # Engines that do not control the mode in which their parent application
+ # runs should call +require_dependency+ where needed in case the runtime
+ # mode is +:classic+.
def require_dependency(file_name, message = "No such file to load -- %s.rb")
file_name = file_name.to_path if file_name.respond_to?(:to_path)
unless file_name.is_a?(String)
raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
end
@@ -365,16 +373,11 @@
def depend_on(file_name, message = "No such file to load -- %s.rb")
path = search_for_file(file_name)
require_or_load(path || file_name)
rescue LoadError => load_error
if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
- load_error_message = if load_error.respond_to?(:original_message)
- load_error.original_message
- else
- load_error.message
- end
- load_error_message.replace(message % file_name)
+ load_error.message.replace(message % file_name)
load_error.copy_blame!(load_error)
end
raise
end
@@ -456,11 +459,11 @@
paths
end
# Search for a file in autoload_paths matching the provided suffix.
def search_for_file(path_suffix)
- path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
+ 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
end
@@ -476,13 +479,13 @@
end
nil
end
def load_once_path?(path)
- # to_s works around a ruby issue where String#starts_with?(Pathname)
+ # to_s works around a ruby issue where String#start_with?(Pathname)
# will raise a TypeError: no implicit conversion of Pathname into String
- autoload_once_paths.any? { |base| path.starts_with? base.to_s }
+ autoload_once_paths.any? { |base| path.start_with?(base.to_s) }
end
# Attempt to autoload the provided module name by searching for a directory
# matching the expected path suffix. If found, the module is created and
# assigned to +into+'s constants with the name +const_name+. Provided that
@@ -528,22 +531,23 @@
# Load the constant named +const_name+ which is missing from +from_mod+. If
# it is not possible to load the constant into from_mod, try its parent
# module using +const_missing+.
def load_missing_constant(from_mod, const_name)
- unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
+ from_mod_name = real_mod_name(from_mod)
+ unless qualified_const_defined?(from_mod_name) && Inflector.constantize(from_mod_name).equal?(from_mod)
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
qualified_name = qualified_name_for(from_mod, const_name)
path_suffix = qualified_name.underscore
file_path = search_for_file(path_suffix)
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, "")
+ expanded.delete_suffix!(".rb")
if loading.include?(expanded)
raise "Circular dependency detected while autoloading constant #{qualified_name}"
else
require_or_load(expanded, qualified_name)
@@ -587,12 +591,12 @@
rescue NameError => e
raise unless e.missing_name? qualified_name_for(parent, const_name)
end
end
- name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
- name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
+ name_error = uninitialized_constant(qualified_name, const_name, receiver: from_mod)
+ name_error.set_backtrace(caller.reject { |l| l.start_with? __FILE__ })
raise name_error
end
# Remove the constants that have been autoloaded, and those that have been
# marked for unloading. Before each constant is removed a callback is sent
@@ -717,32 +721,32 @@
# Convert the provided const desc to a qualified constant name (as a string).
# A module, class, symbol, or string may be provided.
def to_constant_name(desc) #:nodoc:
case desc
- when String then desc.sub(/^::/, "")
+ when String then desc.delete_prefix("::")
when Symbol then desc.to_s
when Module
real_mod_name(desc) ||
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
end
end
def remove_constant(const) #:nodoc:
# Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
- normalized = const.to_s.sub(/\A::/, "")
+ normalized = const.to_s.delete_prefix("::")
normalized.sub!(/\A(Object::)+/, "")
constants = normalized.split("::")
to_remove = constants.pop
# Remove the file path from the loaded list.
file_path = search_for_file(const.underscore)
if file_path
expanded = File.expand_path(file_path)
- expanded.sub!(/\.rb\z/, "")
+ expanded.delete_suffix!(".rb")
loaded.delete(expanded)
end
if constants.empty?
parent = Object
@@ -796,9 +800,19 @@
def log(message)
logger.debug("autoloading: #{message}") if logger && verbose
end
private
+ if RUBY_VERSION < "2.6"
+ def uninitialized_constant(qualified_name, const_name, receiver:)
+ NameError.new("uninitialized constant #{qualified_name}", const_name)
+ end
+ else
+ def uninitialized_constant(qualified_name, const_name, receiver:)
+ NameError.new("uninitialized constant #{qualified_name}", const_name, receiver: receiver)
+ end
+ end
+
# Returns the original name of a class or module even if `name` has been
# overridden.
def real_mod_name(mod)
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
end