1 module ActiveSupport
#:nodoc:
2 module Dependencies
#:nodoc:
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
9 # All files ever loaded.
10 mattr_accessor
:history
11 self.history
= Set
.new
13 # All files currently loaded.
14 mattr_accessor
:loaded
17 # Should we load files or require them?
18 mattr_accessor
:mechanism
19 self.mechanism
= :load
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
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
= []
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
= []
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
= []
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
46 # Set to true to enable logging of const_missing and file loads
47 mattr_accessor
:log_activity
48 self.log_activity
= false
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
= []
54 # Module includes this module
55 module ModuleConstMissing
#:nodoc:
56 def self.included(base
) #:nodoc:
58 unless defined? const_missing_without_dependencies
59 alias_method_chain
:const_missing, :dependencies
64 def self.excluded(base
) #:nodoc:
66 if defined? const_missing_without_dependencies
67 undef_method
:const_missing
68 alias_method
:const_missing, :const_missing_without_dependencies
69 undef_method
:const_missing_without_dependencies
74 # Use const_missing to autoload associations so we don't have to
75 # require_association when using single-table inheritance.
76 def const_missing_with_dependencies(class_id
)
77 ActiveSupport
::Dependencies.load_missing_constant
self, class_id
80 def unloadable(const_desc
= self)
85 # Class includes this module
86 module ClassConstMissing
#:nodoc:
87 def const_missing(const_name
)
88 if [Object
, Kernel
].include?(self) || parent
== self
93 Dependencies
.load_missing_constant
self, const_name
95 parent
.send
:const_missing, const_name
98 # Make sure that the name we are missing is the one that caused the error
99 parent_qualified_name
= Dependencies
.qualified_name_for parent
, const_name
100 raise unless e
.missing_name
? parent_qualified_name
101 qualified_name
= Dependencies
.qualified_name_for
self, const_name
102 raise NameError
.new("uninitialized constant #{qualified_name}").copy_blame
!(e
)
108 # Object includes this module
109 module Loadable
#:nodoc:
110 def self.included(base
) #:nodoc:
112 unless defined? load_without_new_constant_marking
113 alias_method_chain
:load, :new_constant_marking
118 def self.excluded(base
) #:nodoc:
120 if defined? load_without_new_constant_marking
122 alias_method
:load, :load_without_new_constant_marking
123 undef_method
:load_without_new_constant_marking
128 def require_or_load(file_name
)
129 Dependencies
.require_or_load(file_name
)
132 def require_dependency(file_name
)
133 Dependencies
.depend_on(file_name
)
136 def require_association(file_name
)
137 Dependencies
.associate_with(file_name
)
140 def load_with_new_constant_marking(file
, *extras
) #:nodoc:
141 if Dependencies
.load
?
142 Dependencies
.new_constants_in(Object
) { load_without_new_constant_marking(file
, *extras
) }
144 load_without_new_constant_marking(file
, *extras
)
146 rescue Exception
=> exception
# errors from loading file
147 exception
.blame_file
! file
151 def require(file
, *extras
) #:nodoc:
152 if Dependencies
.load
?
153 Dependencies
.new_constants_in(Object
) { super }
157 rescue Exception
=> exception
# errors from required file
158 exception
.blame_file
! file
162 # Mark the given constant as unloadable. Unloadable constants are removed each
163 # time dependencies are cleared.
165 # Note that marking a constant for unloading need only be done once. Setup
166 # or init scripts may list each unloadable constant that may need unloading;
167 # each constant will be removed for every subsequent clear, as opposed to for
170 # The provided constant descriptor may be a (non-anonymous) module or class,
171 # or a qualified constant name as a string or symbol.
173 # Returns true if the constant was not previously marked for unloading, false
175 def unloadable(const_desc
)
176 Dependencies
.mark_for_unload const_desc
180 # Exception file-blaming
181 module Blamable
#:nodoc:
182 def blame_file
!(file
)
183 (@blamed_files ||= []).unshift file
191 return nil if blamed_files
.empty
?
192 "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
196 @blamed_files = exc
.blamed_files
.clone
202 Object
.instance_eval
{ include Loadable
}
203 Module
.instance_eval
{ include ModuleConstMissing
}
204 Class
.instance_eval
{ include ClassConstMissing
}
205 Exception
.instance_eval
{ include Blamable
}
210 ModuleConstMissing
.excluded(Module
)
211 Loadable
.excluded(Object
)
219 def depend_on(file_name
, swallow_load_errors
= false)
220 path
= search_for_file(file_name
)
221 require_or_load(path
|| file_name
)
223 raise unless swallow_load_errors
226 def associate_with(file_name
)
227 depend_on(file_name
, true)
233 remove_unloadable_constants
!
236 def require_or_load(file_name
, const_path
= nil)
237 log_call file_name
, const_path
238 file_name
= $1 if file_name
=~
/^(.*)\.rb$/
239 expanded
= File
.expand_path(file_name
)
240 return if loaded
.include?(expanded
)
242 # Record that we've seen this file *before* loading it to avoid an
243 # infinite loop with mutual dependencies.
248 log
"loading #{file_name}"
250 # Enable warnings iff this file has not been loaded before and
251 # warnings_on_first_load is set.
252 load_args
= ["#{file_name}.rb"]
253 load_args
<< const_path
unless const_path
.nil?
255 if !warnings_on_first_load
or history
.include?(expanded
)
256 result
= load_file(*load_args
)
258 enable_warnings
{ result
= load_file(*load_args
) }
261 log
"requiring #{file_name}"
262 result
= require file_name
265 loaded
.delete expanded
269 # Record history *after* loading so first load gets warnings.
274 # Is the provided constant path defined?
275 def qualified_const_defined
?(path
)
276 raise NameError
, "#{path.inspect} is not a valid constant name!" unless
277 /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path
279 names
= path
.to_s
.split('::')
280 names
.shift
if names
.first
.empty
?
282 # We can't use defined? because it will invoke const_missing for the parent
283 # of the name we are checking.
284 names
.inject(Object
) do |mod
, name
|
285 return false unless uninherited_const_defined
?(mod
, name
)
291 if Module
.method(:const_defined?).arity
== 1
292 # Does this module define this constant?
293 # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9
294 def uninherited_const_defined
?(mod
, const
)
295 mod
.const_defined
?(const
)
298 def uninherited_const_defined
?(mod
, const
) #:nodoc:
299 mod
.const_defined
?(const
, false)
303 # Given +path+, a filesystem path to a ruby file, return an array of constant
304 # paths which would cause Dependencies to attempt to load this file.
305 def loadable_constants_for_path(path
, bases
= load_paths
)
306 path
= $1 if path
=~
/\A(.*)\.rb\Z/
307 expanded_path
= File
.expand_path(path
)
309 bases
.collect
do |root
|
310 expanded_root
= File
.expand_path(root
)
311 next unless %r
{\A
#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
313 nesting
= expanded_path
[(expanded_root
.size
)..-1]
314 nesting
= nesting
[1..-1] if nesting
&& nesting
[0] == ?/
315 next if nesting
.blank
?
316 nesting_camel
= nesting
.camelize
318 qualified_const_defined
?(nesting_camel
)
325 # Special case: application.rb might define ApplicationControlller.
326 ('ApplicationController' if nesting
== 'application')
328 end.flatten
.compact
.uniq
331 # Search for a file in load_paths matching the provided suffix.
332 def search_for_file(path_suffix
)
333 path_suffix
= path_suffix
+ '.rb' unless path_suffix
.ends_with
? '.rb'
334 load_paths
.each
do |root
|
335 path
= File
.join(root
, path_suffix
)
336 return path
if File
.file
? path
338 nil # Gee, I sure wish we had first_match ;-)
341 # Does the provided path_suffix correspond to an autoloadable module?
342 # Instead of returning a boolean, the autoload base for this module is returned.
343 def autoloadable_module
?(path_suffix
)
344 load_paths
.each
do |load_path
|
345 return load_path
if File
.directory
? File
.join(load_path
, path_suffix
)
350 def load_once_path
?(path
)
351 load_once_paths
.any
? { |base
| path
.starts_with
? base
}
354 # Attempt to autoload the provided module name by searching for a directory
355 # matching the expect path suffix. If found, the module is created and assigned
356 # to +into+'s constants with the name +const_name+. Provided that the directory
357 # was loaded from a reloadable base path, it is added to the set of constants
358 # that are to be unloaded.
359 def autoload_module
!(into
, const_name
, qualified_name
, path_suffix
)
360 return nil unless base_path
= autoloadable_module
?(path_suffix
)
362 into
.const_set const_name
, mod
363 autoloaded_constants
<< qualified_name
unless load_once_paths
.include?(base_path
)
367 # Load the file at the provided path. +const_paths+ is a set of qualified
368 # constant names. When loading the file, Dependencies will watch for the
369 # addition of these constants. Each that is defined will be marked as
370 # autoloaded, and will be removed when Dependencies.clear is next called.
372 # If the second parameter is left off, then Dependencies will construct a set
373 # of names that the file at +path+ may define. See
374 # +loadable_constants_for_path+ for more details.
375 def load_file(path
, const_paths
= loadable_constants_for_path(path
))
376 log_call path
, const_paths
377 const_paths
= [const_paths
].compact
unless const_paths
.is_a
? Array
378 parent_paths
= const_paths
.collect
{ |const_path
| /(.*)::[^:]+\Z/ =~ const_path
? $1 : :Object }
381 newly_defined_paths
= new_constants_in(*parent_paths
) do
382 result
= load_without_new_constant_marking path
385 autoloaded_constants
.concat newly_defined_paths
unless load_once_path
?(path
)
386 autoloaded_constants
.uniq
!
387 log
"loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths
.empty
?
391 # Return the constant path for the provided parent and constant name.
392 def qualified_name_for(mod
, name
)
393 mod_name
= to_constant_name mod
394 (%w(Object Kernel
).include? mod_name
) ? name
.to_s
: "#{mod_name}::#{name}"
397 # Load the constant named +const_name+ which is missing from +from_mod+. If
398 # it is not possible to load the constant into from_mod, try its parent module
399 # using const_missing.
400 def load_missing_constant(from_mod
, const_name
)
401 log_call from_mod
, const_name
402 if from_mod
== Kernel
403 if ::Object.const_defined
?(const_name
)
404 log
"Returning Object::#{const_name} for Kernel::#{const_name}"
405 return ::Object.const_get(const_name
)
407 log
"Substituting Object for Kernel"
412 # If we have an anonymous module, all we can do is attempt to load from Object.
413 from_mod
= Object
if from_mod
.name
.blank
?
415 unless qualified_const_defined
?(from_mod
.name
) && from_mod
.name
.constantize
.object_id
== from_mod
.object_id
416 raise ArgumentError
, "A copy of #{from_mod} has been removed from the module tree but is still active!"
419 raise ArgumentError
, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined
?(from_mod
, const_name
)
421 qualified_name
= qualified_name_for from_mod
, const_name
422 path_suffix
= qualified_name
.underscore
423 name_error
= NameError
.new("uninitialized constant #{qualified_name}")
425 file_path
= search_for_file(path_suffix
)
426 if file_path
&& ! loaded
.include?(File
.expand_path(file_path
)) # We found a matching file to load
427 require_or_load file_path
428 raise LoadError
, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined
?(from_mod
, const_name
)
429 return from_mod
.const_get(const_name
)
430 elsif mod
= autoload_module
!(from_mod
, const_name
, qualified_name
, path_suffix
)
432 elsif (parent
= from_mod
.parent
) && parent
!= from_mod
&&
433 ! from_mod
.parents
.any
? { |p
| uninherited_const_defined
?(p
, const_name
) }
434 # If our parents do not have a constant named +const_name+ then we are free
435 # to attempt to load upwards. If they do have such a constant, then this
436 # const_missing must be due to from_mod::const_name, which should not
437 # return constants from from_mod's parents.
439 return parent
.const_missing(const_name
)
440 rescue NameError
=> e
441 raise unless e
.missing_name
? qualified_name_for(parent
, const_name
)
449 # Remove the constants that have been autoloaded, and those that have been
450 # marked for unloading.
451 def remove_unloadable_constants
!
452 autoloaded_constants
.each
{ |const
| remove_constant const
}
453 autoloaded_constants
.clear
454 explicitly_unloadable_constants
.each
{ |const
| remove_constant const
}
457 # Determine if the given constant has been automatically loaded.
458 def autoloaded
?(desc
)
459 # No name => anonymous module.
460 return false if desc
.is_a
?(Module
) && desc
.name
.blank
?
461 name
= to_constant_name desc
462 return false unless qualified_const_defined
? name
463 return autoloaded_constants
.include?(name
)
466 # Will the provided constant descriptor be unloaded?
467 def will_unload
?(const_desc
)
468 autoloaded
?(const_desc
) ||
469 explicitly_unloadable_constants
.include?(to_constant_name(const_desc
))
472 # Mark the provided constant name for unloading. This constant will be
473 # unloaded on each request, not just the next one.
474 def mark_for_unload(const_desc
)
475 name
= to_constant_name const_desc
476 if explicitly_unloadable_constants
.include? name
479 explicitly_unloadable_constants
<< name
484 # Run the provided block and detect the new constants that were loaded during
485 # its execution. Constants may only be regarded as 'new' once -- so if the
486 # block calls +new_constants_in+ again, then the constants defined within the
487 # inner call will not be reported in this one.
489 # If the provided block does not run to completion, and instead raises an
490 # exception, any new constants are regarded as being only partially defined
491 # and will be removed immediately.
492 def new_constants_in(*descs
)
495 # Build the watch frames. Each frame is a tuple of
496 # [module_name_as_string, constants_defined_elsewhere]
497 watch_frames
= descs
.collect
do |desc
|
500 initial_constants
= desc
.local_constant_names
501 elsif desc
.is_a
?(String
) || desc
.is_a
?(Symbol
)
504 # Handle the case where the module has yet to be defined.
505 initial_constants
= if qualified_const_defined
?(mod_name
)
506 mod_name
.constantize
.local_constant_names
511 raise Argument
, "#{desc.inspect} does not describe a module!"
514 [mod_name
, initial_constants
]
517 constant_watch_stack
.concat watch_frames
521 yield # Now yield to the code that is to define new constants.
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
)
529 mod
= mod_name
.constantize
530 next [] unless mod
.is_a
? Module
531 new_constants
= mod
.local_constant_names
- prior_constants
533 # Make sure no other frames takes credit for these constants.
534 constant_watch_stack
.each
do |frame_name
, constants
|
535 constants
.concat new_constants
if frame_name
== mod_name
538 new_constants
.collect
do |suffix
|
539 mod_name
== "Object" ? suffix
: "#{mod_name}::#{suffix}"
543 log
"New constants: #{new_constants * ', '}"
546 log
"Error during loading, removing partially loaded constants "
547 new_constants
.each
{ |name
| remove_constant name
}
554 # Remove the stack frames that we added.
555 if defined?(watch_frames
) && ! watch_frames
.blank
?
556 frame_ids
= watch_frames
.collect
{ |frame
| frame
.object_id
}
557 constant_watch_stack
.delete_if
do |watch_frame
|
558 frame_ids
.include? watch_frame
.object_id
563 class LoadingModule
#:nodoc:
564 # Old style environment.rb referenced this method directly. Please note, it doesn't
565 # actually *do* anything any more.
567 if defined?(RAILS_DEFAULT_LOGGER
)
568 RAILS_DEFAULT_LOGGER
.warn
"Your environment.rb uses the old syntax, it may not continue to work in future releases."
569 RAILS_DEFAULT_LOGGER
.warn
"For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
574 # Convert the provided const desc to a qualified constant name (as a string).
575 # A module, class, symbol, or string may be provided.
576 def to_constant_name(desc
) #:nodoc:
578 when String
then desc
.starts_with
?('::') ? desc
[2..-1] : desc
579 when Symbol
then desc
.to_s
581 raise ArgumentError
, "Anonymous modules have no name to be referenced by" if desc
.name
.blank
?
583 else raise TypeError
, "Not a valid constant descriptor: #{desc.inspect}"
587 def remove_constant(const
) #:nodoc:
588 return false unless qualified_const_defined
? const
590 const
= $1 if /\A::(.*)\Z/ =~ const
.to_s
591 names
= const
.to_s
.split('::')
592 if names
.size
== 1 # It's under Object
595 parent
= (names
[0..-2] * '::').constantize
598 log
"removing constant #{const}"
599 parent
.instance_eval
{ remove_const names
.last
}
605 if logger
&& log_activity
606 arg_str
= args
.collect
{ |arg
| arg
.inspect
} * ', '
607 /in `([a-z_\?\!]+)'/ =~
caller(1).first
608 selector
= $1 || '<unknown>'
609 log
"called #{selector}(#{arg_str})"
614 if logger
&& log_activity
615 logger
.debug
"Dependencies: #{msg}"
621 ActiveSupport
::Dependencies.hook
!