aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/dependencies.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/dependencies.rb')
-rw-r--r--activesupport/lib/active_support/dependencies.rb137
1 files changed, 60 insertions, 77 deletions
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 2c5950edf5..48be96f176 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -129,16 +129,14 @@ module ActiveSupport #:nodoc:
# Add a set of modules to the watch stack, remembering the initial constants
def watch_namespaces(namespaces)
- watching = []
- namespaces.map do |namespace|
+ @watching << namespaces.map do |namespace|
module_name = Dependencies.to_constant_name(namespace)
original_constants = Dependencies.qualified_const_defined?(module_name) ?
Inflector.constantize(module_name).local_constants : []
- watching << module_name
@stack[module_name] << original_constants
+ module_name
end
- @watching << watching
end
private
@@ -170,36 +168,13 @@ module ActiveSupport #:nodoc:
end
end
- # Use const_missing to autoload associations so we don't have to
- # require_association when using single-table inheritance.
- def const_missing(const_name, nesting = nil)
- klass_name = name.presence || "Object"
-
- unless nesting
- # We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
- # even though it might not be, such as in the case of
- # class Foo::Bar; Baz; end
- nesting = []
- klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
- end
-
- # If there are multiple levels of nesting to search under, the top
- # level is the one we want to report as the lookup fail.
- error = nil
-
- nesting.each do |namespace|
- begin
- return Dependencies.load_missing_constant Inflector.constantize(namespace), const_name
- rescue NoMethodError then raise
- rescue NameError => e
- error ||= e
- end
- end
-
- # Raise the first error for this set. If this const_missing came from an
- # earlier const_missing, this will result in the real error bubbling
- # all the way up
- raise error
+ def const_missing(const_name)
+ # The interpreter does not pass nesting information, and in the
+ # case of anonymous modules we cannot even make the trade-off of
+ # assuming their name reflects the nesting. Resort to Object as
+ # the only meaningful guess we can make.
+ from_mod = anonymous? ? ::Object : self
+ Dependencies.load_missing_constant(from_mod, const_name)
end
def unloadable(const_desc = self)
@@ -222,11 +197,7 @@ module ActiveSupport #:nodoc:
raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
end
- Dependencies.depend_on(file_name, false, message)
- end
-
- def require_association(file_name)
- Dependencies.associate_with(file_name)
+ Dependencies.depend_on(file_name, message)
end
def load_dependency(file)
@@ -295,33 +266,26 @@ module ActiveSupport #:nodoc:
Object.class_eval { include Loadable }
Module.class_eval { include ModuleConstMissing }
Exception.class_eval { include Blamable }
- true
end
def unhook!
ModuleConstMissing.exclude_from(Module)
Loadable.exclude_from(Object)
- true
end
def load?
mechanism == :load
end
- def depend_on(file_name, swallow_load_errors = false, message = "No such file to load -- %s.rb")
+ 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
- unless swallow_load_errors
- if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
- raise LoadError.new(message % file_name).copy_blame!(load_error)
- end
- raise
+ if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
+ load_error.message.replace(message % file_name)
+ load_error.copy_blame!(load_error)
end
- end
-
- def associate_with(file_name)
- depend_on(file_name, true)
+ raise
end
def clear
@@ -332,7 +296,7 @@ module ActiveSupport #:nodoc:
def require_or_load(file_name, const_path = nil)
log_call file_name, const_path
- file_name = $1 if file_name =~ /^(.*)\.rb$/
+ file_name = $` if file_name =~ /\.rb\z/
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -365,7 +329,7 @@ module ActiveSupport #:nodoc:
# Record history *after* loading so first load gets warnings.
history << expanded
- return result
+ result
end
# Is the provided constant path defined?
@@ -373,14 +337,10 @@ module ActiveSupport #:nodoc:
Object.qualified_const_defined?(path.sub(/^::/, ''), false)
end
- def local_const_defined?(mod, const) #:nodoc:
- mod.const_defined?(const, false)
- end
-
# Given +path+, a filesystem path to a ruby file, return an array of constant
# paths which would cause Dependencies to attempt to load this file.
def loadable_constants_for_path(path, bases = autoload_paths)
- path = $1 if path =~ /\A(.*)\.rb\Z/
+ path = $` if path =~ /\.rb\z/
expanded_path = File.expand_path(path)
paths = []
@@ -434,7 +394,7 @@ module ActiveSupport #:nodoc:
mod = Module.new
into.const_set const_name, mod
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
- return mod
+ mod
end
# Load the file at the provided path. +const_paths+ is a set of qualified
@@ -448,7 +408,7 @@ module ActiveSupport #:nodoc:
def load_file(path, const_paths = loadable_constants_for_path(path))
log_call path, const_paths
const_paths = [const_paths].compact unless const_paths.is_a? Array
- parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
result = nil
newly_defined_paths = new_constants_in(*parent_paths) do
@@ -458,7 +418,7 @@ module ActiveSupport #:nodoc:
autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
autoloaded_constants.uniq!
log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
- return result
+ result
end
# Return the constant path for the provided parent and constant name.
@@ -477,26 +437,52 @@ module ActiveSupport #:nodoc:
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
end
- raise NameError, "#{from_mod} is not missing constant #{const_name}!" if local_const_defined?(from_mod, const_name)
+ raise NameError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name, false)
qualified_name = qualified_name_for from_mod, const_name
path_suffix = qualified_name.underscore
file_path = search_for_file(path_suffix)
- if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
- require_or_load file_path
- raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name)
- return from_mod.const_get(const_name)
+ if file_path
+ expanded = File.expand_path(file_path)
+ expanded.sub!(/\.rb\z/, '')
+
+ if loaded.include?(expanded)
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
+ else
+ require_or_load(expanded)
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
+ return from_mod.const_get(const_name)
+ end
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
elsif (parent = from_mod.parent) && parent != from_mod &&
- ! from_mod.parents.any? { |p| local_const_defined?(p, const_name) }
+ ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
# If our parents do not have a constant named +const_name+ then we are free
# to attempt to load upwards. If they do have such a constant, then this
# const_missing must be due to from_mod::const_name, which should not
# return constants from from_mod's parents.
begin
+ # Since Ruby does not pass the nesting at the point the unknown
+ # constant triggered the callback we cannot fully emulate constant
+ # name lookup and need to make a trade-off: we are going to assume
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
+ # though it might not be. Counterexamples are
+ #
+ # class Foo::Bar
+ # Module.nesting # => [Foo::Bar]
+ # end
+ #
+ # or
+ #
+ # module M::N
+ # module S::T
+ # Module.nesting # => [S::T, M::N]
+ # end
+ # end
+ #
+ # for example.
return parent.const_missing(const_name)
rescue NameError => e
raise unless e.missing_name? qualified_name_for(parent, const_name)
@@ -505,7 +491,7 @@ module ActiveSupport #:nodoc:
raise NameError,
"uninitialized constant #{qualified_name}",
- caller.reject {|l| l.starts_with? __FILE__ }
+ caller.reject { |l| l.starts_with? __FILE__ }
end
# Remove the constants that have been autoloaded, and those that have been
@@ -543,10 +529,7 @@ module ActiveSupport #:nodoc:
def safe_get(key)
key = key.name if key.respond_to?(:name)
- @store[key] || begin
- klass = Inflector.safe_constantize(key)
- @store[key] = klass
- end
+ @store[key] ||= Inflector.safe_constantize(key)
end
def store(klass)
@@ -600,10 +583,10 @@ module ActiveSupport #:nodoc:
def mark_for_unload(const_desc)
name = to_constant_name const_desc
if explicitly_unloadable_constants.include? name
- return false
+ false
else
explicitly_unloadable_constants << name
- return true
+ true
end
end
@@ -631,10 +614,10 @@ module ActiveSupport #:nodoc:
return new_constants unless aborting
log "Error during loading, removing partially loaded constants "
- new_constants.each {|c| remove_constant(c) }.clear
+ new_constants.each { |c| remove_constant(c) }.clear
end
- return []
+ []
end
# Convert the provided const desc to a qualified constant name (as a string).
@@ -663,7 +646,7 @@ module ActiveSupport #:nodoc:
constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
parent.instance_eval { remove_const to_remove }
- return true
+ true
end
protected