File: active_support/dependencies.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: ActiveSupport#3
  module: Dependencies#4
extends
  Dependencies ( ActiveSupport )
includes
  Blamable ( Unknown-Module )
  ClassConstMissing ( Unknown-Module )
  Loadable ( Unknown-Module )
  ModuleConstMissing ( Unknown-Module )
has properties
module method: load_paths #30
module method: load_paths= / 1 #36
module method: load_once_paths #47
module method: load_once_paths= / 1 #53
method: hook! #230
method: unhook! #238
method: load? #244
method: depend_on / 2 #248
method: associate_with / 1 #255
method: clear #259
method: require_or_load / 2 #265
method: qualified_const_defined? / 1 #304
method: uninherited_const_defined? (1/2) / 2 #323
method: uninherited_const_defined? (2/E) / 2 #327
method: loadable_constants_for_path / 2 #334
method: search_for_file / 1 #356
method: autoloadable_module? / 1 #367
method: load_once_path? / 1 #374
method: autoload_module! / 4 #383
method: load_file / 2 #399
method: qualified_name_for / 2 #416
method: load_missing_constant / 2 #424
method: remove_unloadable_constants! #475
method: autoloaded? / 1 #482
method: will_unload? / 1 #491
method: mark_for_unload / 1 #498
method: new_constants_in / 1 #516
method: to_constant_name / 1 #606
method: remove_constant / 1 #617
method: log_call / 1 #634
method: log / 1 #643
  module: ModuleConstMissing#84
has properties
module method: included / 1 #85
module method: excluded / 1 #93
method: const_missing_with_dependencies / 1 #105
method: unloadable / 1 #109
  module: ClassConstMissing#115
has properties
method: const_missing / 1 #116
  module: Loadable#138
has properties
module method: included / 1 #139
module method: excluded / 1 #147
method: require_or_load / 1 #157
method: require_dependency / 1 #161
method: require_association / 1 #165
method: load_with_new_constant_marking / 2 #169
method: require / 2 #180
method: unloadable / 1 #204
  module: Blamable#210
has properties
method: blame_file! / 1 #211
method: blamed_files #215
method: describe_blame #219
method: copy_blame! / 1 #224
  class: LoadingModule#593
inherits from
  Object ( Builtin-Module )
has properties
class method: root / 1 #596

Class Hierarchy

Code

   1  require 'thread'
   2 
   3  module ActiveSupport #:nodoc:
   4    module Dependencies #:nodoc:
   5      extend self
   6 
   7      # Should we turn on Ruby warnings on the first load of dependent files?
   8      mattr_accessor :warnings_on_first_load
   9      self.warnings_on_first_load = false
  10 
  11      # All files ever loaded.
  12      mattr_accessor :history
  13      self.history = Set.new
  14 
  15      # All files currently loaded.
  16      mattr_accessor :loaded
  17      self.loaded = Set.new
  18 
  19      # Should we load files or require them?
  20      mattr_accessor :mechanism
  21      self.mechanism = :load
  22 
  23      # The set of directories from which we may automatically load files. Files
  24      # under these directories will be reloaded on each request in development mode,
  25      # unless the directory also appears in autoload_once_paths.
  26      mattr_accessor :autoload_paths
  27      self.autoload_paths = []
  28      
  29      # Deprecated, use autoload_paths.
  30      def self.load_paths
  31        ActiveSupport::Deprecation.warn("ActiveSupport::Dependencies.load_paths is deprecated, please use autoload_paths instead", caller)
  32        autoload_paths
  33      end
  34 
  35      # Deprecated, use autoload_paths=.
  36      def self.load_paths=(paths)
  37        ActiveSupport::Deprecation.warn("ActiveSupport::Dependencies.load_paths= is deprecated, please use autoload_paths= instead", caller)
  38        self.autoload_paths = paths
  39      end
  40 
  41      # The set of directories from which automatically loaded constants are loaded
  42      # only once. All directories in this set must also be present in +autoload_paths+.
  43      mattr_accessor :autoload_once_paths
  44      self.autoload_once_paths = []
  45 
  46      # Deprecated, use autoload_once_paths.
  47      def self.load_once_paths
  48        ActiveSupport::Deprecation.warn("ActiveSupport::Dependencies.load_once_paths is deprecated and removed in Rails 3, please use autoload_once_paths instead", caller)
  49        autoload_once_paths
  50      end
  51 
  52      # Deprecated, use autoload_once_paths=.
  53      def self.load_once_paths=(paths)
  54        ActiveSupport::Deprecation.warn("ActiveSupport::Dependencies.load_once_paths= is deprecated and removed in Rails 3, please use autoload_once_paths= instead", caller)
  55        self.autoload_once_paths = paths
  56      end
  57 
  58      # An array of qualified constant names that have been loaded. Adding a name to
  59      # this array will cause it to be unloaded the next time Dependencies are cleared.
  60      mattr_accessor :autoloaded_constants
  61      self.autoloaded_constants = []
  62 
  63      # An array of constant names that need to be unloaded on every request. Used
  64      # to allow arbitrary constants to be marked for unloading.
  65      mattr_accessor :explicitly_unloadable_constants
  66      self.explicitly_unloadable_constants = []
  67 
  68      # The logger is used for generating information on the action run-time (including benchmarking) if available.
  69      # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
  70      mattr_accessor :logger
  71 
  72      # Set to true to enable logging of const_missing and file loads
  73      mattr_accessor :log_activity
  74      self.log_activity = false
  75 
  76      # An internal stack used to record which constants are loaded by any block.
  77      mattr_accessor :constant_watch_stack
  78      self.constant_watch_stack = []
  79 
  80      mattr_accessor :constant_watch_stack_mutex
  81      self.constant_watch_stack_mutex = Mutex.new
  82 
  83      # Module includes this module
  84      module ModuleConstMissing #:nodoc:
  85        def self.included(base) #:nodoc:
  86          base.class_eval do
  87            unless defined? const_missing_without_dependencies
  88              alias_method_chain :const_missing, :dependencies
  89            end
  90          end
  91        end
  92 
  93        def self.excluded(base) #:nodoc:
  94          base.class_eval do
  95            if defined? const_missing_without_dependencies
  96              undef_method :const_missing
  97              alias_method :const_missing, :const_missing_without_dependencies
  98              undef_method :const_missing_without_dependencies
  99            end
 100          end
 101        end
 102 
 103        # Use const_missing to autoload associations so we don't have to
 104        # require_association when using single-table inheritance.
 105        def const_missing_with_dependencies(class_id)
 106          ActiveSupport::Dependencies.load_missing_constant self, class_id
 107        end
 108 
 109        def unloadable(const_desc = self)
 110          super(const_desc)
 111        end
 112      end
 113 
 114      # Class includes this module
 115      module ClassConstMissing #:nodoc:
 116        def const_missing(const_name)
 117          if [Object, Kernel].include?(self) || parent == self
 118            super
 119          else
 120            begin
 121              begin
 122                Dependencies.load_missing_constant self, const_name
 123              rescue NameError
 124                parent.send :const_missing, const_name
 125              end
 126            rescue NameError => e
 127              # Make sure that the name we are missing is the one that caused the error
 128              parent_qualified_name = Dependencies.qualified_name_for parent, const_name
 129              raise unless e.missing_name? parent_qualified_name
 130              qualified_name = Dependencies.qualified_name_for self, const_name
 131              raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e)
 132            end
 133          end
 134        end
 135      end
 136 
 137      # Object includes this module
 138      module Loadable #:nodoc:
 139        def self.included(base) #:nodoc:
 140          base.class_eval do
 141            unless defined? load_without_new_constant_marking
 142              alias_method_chain :load, :new_constant_marking
 143            end
 144          end
 145        end
 146 
 147        def self.excluded(base) #:nodoc:
 148          base.class_eval do
 149            if defined? load_without_new_constant_marking
 150              undef_method :load
 151              alias_method :load, :load_without_new_constant_marking
 152              undef_method :load_without_new_constant_marking
 153            end
 154          end
 155        end
 156 
 157        def require_or_load(file_name)
 158          Dependencies.require_or_load(file_name)
 159        end
 160 
 161        def require_dependency(file_name)
 162          Dependencies.depend_on(file_name)
 163        end
 164 
 165        def require_association(file_name)
 166          Dependencies.associate_with(file_name)
 167        end
 168 
 169        def load_with_new_constant_marking(file, *extras) #:nodoc:
 170          if Dependencies.load?
 171            Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) }
 172          else
 173            load_without_new_constant_marking(file, *extras)
 174          end
 175        rescue Exception => exception  # errors from loading file
 176          exception.blame_file! file
 177          raise
 178        end
 179 
 180        def require(file, *extras) #:nodoc:
 181          if Dependencies.load?
 182            Dependencies.new_constants_in(Object) { super }
 183          else
 184            super
 185          end
 186        rescue Exception => exception  # errors from required file
 187          exception.blame_file! file
 188          raise
 189        end
 190 
 191        # Mark the given constant as unloadable. Unloadable constants are removed each
 192        # time dependencies are cleared.
 193        #
 194        # Note that marking a constant for unloading need only be done once. Setup
 195        # or init scripts may list each unloadable constant that may need unloading;
 196        # each constant will be removed for every subsequent clear, as opposed to for
 197        # the first clear.
 198        #
 199        # The provided constant descriptor may be a (non-anonymous) module or class,
 200        # or a qualified constant name as a string or symbol.
 201        #
 202        # Returns true if the constant was not previously marked for unloading, false
 203        # otherwise.
 204        def unloadable(const_desc)
 205          Dependencies.mark_for_unload const_desc
 206        end
 207      end
 208 
 209      # Exception file-blaming
 210      module Blamable #:nodoc:
 211        def blame_file!(file)
 212          (@blamed_files ||= []).unshift file
 213        end
 214 
 215        def blamed_files
 216          @blamed_files ||= []
 217        end
 218 
 219        def describe_blame
 220          return nil if blamed_files.empty?
 221          "This error occurred while loading the following files:\n   #{blamed_files.join "\n   "}"
 222        end
 223 
 224        def copy_blame!(exc)
 225          @blamed_files = exc.blamed_files.clone
 226          self
 227        end
 228      end
 229 
 230      def hook!
 231        Object.instance_eval { include Loadable }
 232        Module.instance_eval { include ModuleConstMissing }
 233        Class.instance_eval { include ClassConstMissing }
 234        Exception.instance_eval { include Blamable }
 235        true
 236      end
 237 
 238      def unhook!
 239        ModuleConstMissing.excluded(Module)
 240        Loadable.excluded(Object)
 241        true
 242      end
 243 
 244      def load?
 245        mechanism == :load
 246      end
 247 
 248      def depend_on(file_name, swallow_load_errors = false)
 249        path = search_for_file(file_name)
 250        require_or_load(path || file_name)
 251      rescue LoadError
 252        raise unless swallow_load_errors
 253      end
 254 
 255      def associate_with(file_name)
 256        depend_on(file_name, true)
 257      end
 258 
 259      def clear
 260        log_call
 261        loaded.clear
 262        remove_unloadable_constants!
 263      end
 264 
 265      def require_or_load(file_name, const_path = nil)
 266        log_call file_name, const_path
 267        file_name = $1 if file_name =~ /^(.*)\.rb$/
 268        expanded = File.expand_path(file_name)
 269        return if loaded.include?(expanded)
 270 
 271        # Record that we've seen this file *before* loading it to avoid an
 272        # infinite loop with mutual dependencies.
 273        loaded << expanded
 274 
 275        begin
 276          if load?
 277            log "loading #{file_name}"
 278 
 279            # Enable warnings iff this file has not been loaded before and
 280            # warnings_on_first_load is set.
 281            load_args = ["#{file_name}.rb"]
 282            load_args << const_path unless const_path.nil?
 283 
 284            if !warnings_on_first_load or history.include?(expanded)
 285              result = load_file(*load_args)
 286            else
 287              enable_warnings { result = load_file(*load_args) }
 288            end
 289          else
 290            log "requiring #{file_name}"
 291            result = require file_name
 292          end
 293        rescue Exception
 294          loaded.delete expanded
 295          raise
 296        end
 297 
 298        # Record history *after* loading so first load gets warnings.
 299        history << expanded
 300        return result
 301      end
 302 
 303      # Is the provided constant path defined?
 304      def qualified_const_defined?(path)
 305        raise NameError, "#{path.inspect} is not a valid constant name!" unless
 306          /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path
 307 
 308        names = path.to_s.split('::')
 309        names.shift if names.first.empty?
 310 
 311        # We can't use defined? because it will invoke const_missing for the parent
 312        # of the name we are checking.
 313        names.inject(Object) do |mod, name|
 314          return false unless uninherited_const_defined?(mod, name)
 315          mod.const_get name
 316        end
 317        return true
 318      end
 319 
 320      if Module.method(:const_defined?).arity == 1
 321        # Does this module define this constant?
 322        # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9
 323        def uninherited_const_defined?(mod, const)
 324          mod.const_defined?(const)
 325        end
 326      else
 327        def uninherited_const_defined?(mod, const) #:nodoc:
 328          mod.const_defined?(const, false)
 329        end
 330      end
 331 
 332      # Given +path+, a filesystem path to a ruby file, return an array of constant
 333      # paths which would cause Dependencies to attempt to load this file.
 334      def loadable_constants_for_path(path, bases = autoload_paths)
 335        path = $1 if path =~ /\A(.*)\.rb\Z/
 336        expanded_path = File.expand_path(path)
 337 
 338        bases.collect do |root|
 339          expanded_root = File.expand_path(root)
 340          next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
 341 
 342          nesting = expanded_path[(expanded_root.size)..-1]
 343          nesting = nesting[1..-1] if nesting && nesting[0] == ?/
 344          next if nesting.blank?
 345          nesting_camel = nesting.camelize
 346          begin
 347            qualified_const_defined?(nesting_camel)
 348          rescue NameError
 349            next
 350          end
 351          [ nesting_camel ]
 352        end.flatten.compact.uniq
 353      end
 354 
 355      # Search for a file in autoload_paths matching the provided suffix.
 356      def search_for_file(path_suffix)
 357        path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb'
 358        autoload_paths.each do |root|
 359          path = File.join(root, path_suffix)
 360          return path if File.file? path
 361        end
 362        nil # Gee, I sure wish we had first_match ;-)
 363      end
 364 
 365      # Does the provided path_suffix correspond to an autoloadable module?
 366      # Instead of returning a boolean, the autoload base for this module is returned.
 367      def autoloadable_module?(path_suffix)
 368        autoload_paths.each do |load_path|
 369          return load_path if File.directory? File.join(load_path, path_suffix)
 370        end
 371        nil
 372      end
 373 
 374      def load_once_path?(path)
 375        autoload_once_paths.any? { |base| path.starts_with? base }
 376      end
 377 
 378      # Attempt to autoload the provided module name by searching for a directory
 379      # matching the expect path suffix. If found, the module is created and assigned
 380      # to +into+'s constants with the name +const_name+. Provided that the directory
 381      # was loaded from a reloadable base path, it is added to the set of constants
 382      # that are to be unloaded.
 383      def autoload_module!(into, const_name, qualified_name, path_suffix)
 384        return nil unless base_path = autoloadable_module?(path_suffix)
 385        mod = Module.new
 386        into.const_set const_name, mod
 387        autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
 388        return mod
 389      end
 390 
 391      # Load the file at the provided path. +const_paths+ is a set of qualified
 392      # constant names. When loading the file, Dependencies will watch for the
 393      # addition of these constants. Each that is defined will be marked as
 394      # autoloaded, and will be removed when Dependencies.clear is next called.
 395      #
 396      # If the second parameter is left off, then Dependencies will construct a set
 397      # of names that the file at +path+ may define. See
 398      # +loadable_constants_for_path+ for more details.
 399      def load_file(path, const_paths = loadable_constants_for_path(path))
 400        log_call path, const_paths
 401        const_paths = [const_paths].compact unless const_paths.is_a? Array
 402        parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
 403 
 404        result = nil
 405        newly_defined_paths = new_constants_in(*parent_paths) do
 406          result = load_without_new_constant_marking path
 407        end
 408 
 409        autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
 410        autoloaded_constants.uniq!
 411        log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
 412        return result
 413      end
 414 
 415      # Return the constant path for the provided parent and constant name.
 416      def qualified_name_for(mod, name)
 417        mod_name = to_constant_name mod
 418        (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}"
 419      end
 420 
 421      # Load the constant named +const_name+ which is missing from +from_mod+. If
 422      # it is not possible to load the constant into from_mod, try its parent module
 423      # using const_missing.
 424      def load_missing_constant(from_mod, const_name)
 425        log_call from_mod, const_name
 426        if from_mod == Kernel
 427          if ::Object.const_defined?(const_name)
 428            log "Returning Object::#{const_name} for Kernel::#{const_name}"
 429            return ::Object.const_get(const_name)
 430          else
 431            log "Substituting Object for Kernel"
 432            from_mod = Object
 433          end
 434        end
 435 
 436        # If we have an anonymous module, all we can do is attempt to load from Object.
 437        from_mod = Object if from_mod.name.blank?
 438 
 439        unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id
 440          raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
 441        end
 442 
 443        raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined?(from_mod, const_name)
 444 
 445        qualified_name = qualified_name_for from_mod, const_name
 446        path_suffix = qualified_name.underscore
 447        name_error = NameError.new("uninitialized constant #{qualified_name}")
 448 
 449        file_path = search_for_file(path_suffix)
 450        if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
 451          require_or_load file_path
 452          raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined?(from_mod, const_name)
 453          return from_mod.const_get(const_name)
 454        elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
 455          return mod
 456        elsif (parent = from_mod.parent) && parent != from_mod &&
 457              ! from_mod.parents.any? { |p| uninherited_const_defined?(p, const_name) }
 458          # If our parents do not have a constant named +const_name+ then we are free
 459          # to attempt to load upwards. If they do have such a constant, then this
 460          # const_missing must be due to from_mod::const_name, which should not
 461          # return constants from from_mod's parents.
 462          begin
 463            return parent.const_missing(const_name)
 464          rescue NameError => e
 465            raise unless e.missing_name? qualified_name_for(parent, const_name)
 466            raise name_error
 467          end
 468        else
 469          raise name_error
 470        end
 471      end
 472 
 473      # Remove the constants that have been autoloaded, and those that have been
 474      # marked for unloading.
 475      def remove_unloadable_constants!
 476        autoloaded_constants.each { |const| remove_constant const }
 477        autoloaded_constants.clear
 478        explicitly_unloadable_constants.each { |const| remove_constant const }
 479      end
 480 
 481      # Determine if the given constant has been automatically loaded.
 482      def autoloaded?(desc)
 483        # No name => anonymous module.
 484        return false if desc.is_a?(Module) && desc.name.blank?
 485        name = to_constant_name desc
 486        return false unless qualified_const_defined? name
 487        return autoloaded_constants.include?(name)
 488      end
 489 
 490      # Will the provided constant descriptor be unloaded?
 491      def will_unload?(const_desc)
 492        autoloaded?(const_desc) ||
 493          explicitly_unloadable_constants.include?(to_constant_name(const_desc))
 494      end
 495 
 496      # Mark the provided constant name for unloading. This constant will be
 497      # unloaded on each request, not just the next one.
 498      def mark_for_unload(const_desc)
 499        name = to_constant_name const_desc
 500        if explicitly_unloadable_constants.include? name
 501          return false
 502        else
 503          explicitly_unloadable_constants << name
 504          return true
 505        end
 506      end
 507 
 508      # Run the provided block and detect the new constants that were loaded during
 509      # its execution. Constants may only be regarded as 'new' once -- so if the
 510      # block calls +new_constants_in+ again, then the constants defined within the
 511      # inner call will not be reported in this one.
 512      #
 513      # If the provided block does not run to completion, and instead raises an
 514      # exception, any new constants are regarded as being only partially defined
 515      # and will be removed immediately.
 516      def new_constants_in(*descs)
 517        log_call(*descs)
 518 
 519        # Build the watch frames. Each frame is a tuple of
 520        #   [module_name_as_string, constants_defined_elsewhere]
 521        watch_frames = descs.collect do |desc|
 522          if desc.is_a? Module
 523            mod_name = desc.name
 524            initial_constants = desc.local_constant_names
 525          elsif desc.is_a?(String) || desc.is_a?(Symbol)
 526            mod_name = desc.to_s
 527 
 528            # Handle the case where the module has yet to be defined.
 529            initial_constants = if qualified_const_defined?(mod_name)
 530              mod_name.constantize.local_constant_names
 531            else
 532              []
 533            end
 534          else
 535            raise Argument, "#{desc.inspect} does not describe a module!"
 536          end
 537 
 538          [mod_name, initial_constants]
 539        end
 540 
 541        constant_watch_stack_mutex.synchronize do
 542          constant_watch_stack.concat watch_frames
 543        end
 544 
 545        aborting = true
 546        begin
 547          yield # Now yield to the code that is to define new constants.
 548          aborting = false
 549        ensure
 550          # Find the new constants.
 551          new_constants = watch_frames.collect do |mod_name, prior_constants|
 552            # Module still doesn't exist? Treat it as if it has no constants.
 553            next [] unless qualified_const_defined?(mod_name)
 554 
 555            mod = mod_name.constantize
 556            next [] unless mod.is_a? Module
 557            new_constants = mod.local_constant_names - prior_constants
 558 
 559            # Make sure no other frames takes credit for these constants.
 560            constant_watch_stack_mutex.synchronize do
 561              constant_watch_stack.each do |frame_name, constants|
 562                constants.concat new_constants if frame_name == mod_name
 563              end
 564            end
 565 
 566            new_constants.collect do |suffix|
 567              mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}"
 568            end
 569          end.flatten
 570 
 571          log "New constants: #{new_constants * ', '}"
 572 
 573          if aborting
 574            log "Error during loading, removing partially loaded constants "
 575            new_constants.each { |name| remove_constant name }
 576            new_constants.clear
 577          end
 578        end
 579 
 580        return new_constants
 581      ensure
 582        # Remove the stack frames that we added.
 583        if defined?(watch_frames) && ! watch_frames.blank?
 584          frame_ids = watch_frames.collect { |frame| frame.object_id }
 585          constant_watch_stack_mutex.synchronize do
 586            constant_watch_stack.delete_if do |watch_frame|
 587              frame_ids.include? watch_frame.object_id
 588            end
 589          end
 590        end
 591      end
 592 
 593      class LoadingModule #:nodoc:
 594        # Old style environment.rb referenced this method directly.  Please note, it doesn't
 595        # actually *do* anything any more.
 596        def self.root(*args)
 597          if defined?(Rails) && Rails.logger
 598            Rails.logger.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
 599            Rails.logger.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
 600          end
 601        end
 602      end
 603 
 604      # Convert the provided const desc to a qualified constant name (as a string).
 605      # A module, class, symbol, or string may be provided.
 606      def to_constant_name(desc) #:nodoc:
 607        name = case desc
 608          when String then desc.starts_with?('::') ? desc[2..-1] : desc
 609          when Symbol then desc.to_s
 610          when Module
 611            raise ArgumentError, "Anonymous modules have no name to be referenced by" if desc.name.blank?
 612            desc.name
 613          else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
 614        end
 615      end
 616 
 617      def remove_constant(const) #:nodoc:
 618        return false unless qualified_const_defined? const
 619 
 620        const = $1 if /\A::(.*)\Z/ =~ const.to_s
 621        names = const.to_s.split('::')
 622        if names.size == 1 # It's under Object
 623          parent = Object
 624        else
 625          parent = (names[0..-2] * '::').constantize
 626        end
 627 
 628        log "removing constant #{const}"
 629        parent.instance_eval { remove_const names.last }
 630        return true
 631      end
 632 
 633      protected
 634        def log_call(*args)
 635          if logger && log_activity
 636            arg_str = args.collect { |arg| arg.inspect } * ', '
 637            /in `([a-z_\?\!]+)'/ =~ caller(1).first
 638            selector = $1 || '<unknown>'
 639            log "called #{selector}(#{arg_str})"
 640          end
 641        end
 642 
 643        def log(msg)
 644          if logger && log_activity
 645            logger.debug "Dependencies: #{msg}"
 646          end
 647        end
 648    end
 649  end
 650 
 651  ActiveSupport::Dependencies.hook!