~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activesupport/lib/active_support/dependencies.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

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