lib/active_support/dependencies.rb in activesupport-3.0.20 vs lib/active_support/dependencies.rb in activesupport-3.1.0.beta1

- old
+ new

@@ -3,10 +3,11 @@ require 'pathname' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/anonymous' +require 'active_support/core_ext/module/deprecation' require 'active_support/core_ext/object/blank' 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/inflector' @@ -45,13 +46,10 @@ # 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 self.autoloaded_constants = [] - mattr_accessor :references - self.references = {} - # An array of constant names that need to be unloaded on every request. Used # to allow arbitrary constants to be marked for unloading. mattr_accessor :explicitly_unloadable_constants self.explicitly_unloadable_constants = [] @@ -62,12 +60,12 @@ # Set to true to enable logging of const_missing and file loads mattr_accessor :log_activity self.log_activity = false # The WatchStack keeps a stack of the modules being watched as files are loaded. - # If a file in the process of being loaded (parent.rb) triggers the load of - # another file (child.rb) the stack will ensure that child.rb handles the new + # If a file in the process of being loaded (parent.rb) triggers the load of + # another file (child.rb) the stack will ensure that child.rb handles the new # constants. # # If child.rb is being autoloaded, its constants will be added to # autoloaded_constants. If it was being `require`d, they will be discarded. # @@ -81,17 +79,17 @@ def initialize @watching = [] super { |h,k| h[k] = [] } end - # return a list of new constants found since the last call to watch_modules + # return a list of new constants found since the last call to watch_namespaces def new_constants constants = [] # Grab the list of namespaces that we're looking for new constants under @watching.last.each do |namespace| - # Retrieve the constants that were present under the namespace when watch_modules + # Retrieve the constants that were present under the namespace when watch_namespaces # was originally called original_constants = self[namespace].last mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace) next unless mod.is_a?(Module) @@ -113,11 +111,11 @@ constants << ([namespace, suffix] - ["Object"]).join("::") end end constants ensure - # A call to new_constants is always called after a call to watch_modules + # A call to new_constants is always called after a call to watch_namespaces pop_modules(@watching.pop) end # Add a set of modules to the watch stack, remembering the initial constants def watch_namespaces(namespaces) @@ -164,11 +162,11 @@ # 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" - if !nesting + 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 $` } @@ -230,19 +228,15 @@ exception.blame_file! file raise end def load(file, *) - result = false - load_dependency(file) { result = super } - result + load_dependency(file) { super } end def require(file, *) - result = false - load_dependency(file) { result = super } - result + load_dependency(file) { super } end # Mark the given constant as unloadable. Unloadable constants are removed each # time dependencies are cleared. # @@ -483,10 +477,14 @@ raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if local_const_defined?(from_mod, const_name) qualified_name = qualified_name_for from_mod, const_name path_suffix = qualified_name.underscore + trace = caller.reject {|l| l =~ %r{#{Regexp.escape(__FILE__)}}} + name_error = NameError.new("uninitialized constant #{qualified_name}") + name_error.set_backtrace(trace) + 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) @@ -501,57 +499,101 @@ # return constants from from_mod's parents. begin return parent.const_missing(const_name) rescue NameError => e raise unless e.missing_name? qualified_name_for(parent, const_name) + raise name_error end + else + raise name_error end - - raise NameError, - "uninitialized constant #{qualified_name}", - caller.reject {|l| l.starts_with? __FILE__ } 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 # to its class/module if it implements +before_remove_const+. # # The callback implementation should be restricted to cleaning up caches, etc. - # as the enviroment will be in an inconsistent state, e.g. other constants + # as the environment will be in an inconsistent state, e.g. other constants # may have already been unloaded and not accessible. def remove_unloadable_constants! autoloaded_constants.each { |const| remove_constant const } autoloaded_constants.clear Reference.clear! explicitly_unloadable_constants.each { |const| remove_constant const } end - class Reference - @@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + class ClassCache + def initialize + @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + end - attr_reader :name + def empty? + @store.empty? + end - def initialize(name) - @name = name.to_s - @@constants[@name] = name if name.respond_to?(:name) + def key?(key) + @store.key?(key) end - def get - @@constants[@name] + def []=(key, value) + return unless key.respond_to?(:name) + + raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank? + + @store[key.name] = value end - def self.clear! - @@constants.clear + def [](key) + key = key.name if key.respond_to?(:name) + + @store[key] end + alias :get :[] + + class Getter # :nodoc: + def initialize(name) + @name = name + end + + def get + Reference.get @name + end + deprecate :get + end + + def new(name) + self[name] = name + Getter.new(name) + end + deprecate :new + + def store(name) + self[name] = name + self + end + + def clear! + @store.clear + end end + Reference = ClassCache.new + def ref(name) - references[name] ||= Reference.new(name) + Reference.new(name) end + deprecate :ref + # Store a reference to a class +klass+. + def reference(klass) + Reference.store klass + end + + # Get the reference for class named +name+. def constantize(name) - ref(name).get + Reference.get(name) end # Determine if the given constant has been automatically loaded. def autoloaded?(desc) # No name => anonymous module. @@ -607,10 +649,21 @@ end return [] end + class LoadingModule #:nodoc: + # Old style environment.rb referenced this method directly. Please note, it doesn't + # actually *do* anything any more. + def self.root(*args) + if defined?(Rails) && Rails.logger + Rails.logger.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases." + Rails.logger.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19" + end + end + end + # 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(/^::/, '') @@ -629,29 +682,32 @@ names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::") to_remove = names.pop parent = Inflector.constantize(names * '::') log "removing constant #{const}" - constantize(const).before_remove_const if constantize(const).respond_to?(:before_remove_const) + constantized = constantize(const) + constantized.before_remove_const if constantized.respond_to?(:before_remove_const) parent.instance_eval { remove_const to_remove } return true end protected def log_call(*args) - if logger && log_activity + if log_activity? arg_str = args.collect { |arg| arg.inspect } * ', ' /in `([a-z_\?\!]+)'/ =~ caller(1).first selector = $1 || '<unknown>' log "called #{selector}(#{arg_str})" end end def log(msg) - if logger && log_activity - logger.debug "Dependencies: #{msg}" - end + logger.debug "Dependencies: #{msg}" if log_activity? + end + + def log_activity? + logger && log_activity end end end ActiveSupport::Dependencies.hook!