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 mattr_accessor
:constant_watch_stack_mutex
55 self.constant_watch_stack_mutex
= Mutex
.new
57 # Module includes this module
58 module ModuleConstMissing
#:nodoc:
59 def self.included(base
) #:nodoc:
61 unless defined? const_missing_without_dependencies
62 alias_method_chain
:const_missing, :dependencies
67 def self.excluded(base
) #:nodoc:
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
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
83 def unloadable(const_desc
= self)
88 # Class includes this module
89 module ClassConstMissing
#:nodoc:
90 def const_missing(const_name
)
91 if [Object
, Kernel
].include?(self) || parent
== self
96 Dependencies
.load_missing_constant
self, const_name
98 parent
.send
:const_missing, const_name
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
)
111 # Object includes this module
112 module Loadable
#:nodoc:
113 def self.included(base
) #:nodoc:
115 unless defined? load_without_new_constant_marking
116 alias_method_chain
:load, :new_constant_marking
121 def self.excluded(base
) #:nodoc:
123 if defined? load_without_new_constant_marking
125 alias_method
:load, :load_without_new_constant_marking
126 undef_method
:load_without_new_constant_marking
131 def require_or_load(file_name
)
132 Dependencies
.require_or_load(file_name
)
135 def require_dependency(file_name
)
136 Dependencies
.depend_on(file_name
)
139 def require_association(file_name
)
140 Dependencies
.associate_with(file_name
)
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
) }
147 load_without_new_constant_marking(file
, *extras
)
149 rescue Exception
=> exception
# errors from loading file
150 exception
.blame_file
! file
154 def require(file
, *extras
) #:nodoc:
155 if Dependencies
.load
?
156 Dependencies
.new_constants_in(Object
) { super }
160 rescue Exception
=> exception
# errors from required file
161 exception
.blame_file
! file
165 # Mark the given constant as unloadable. Unloadable constants are removed each
166 # time dependencies are cleared.
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
173 # The provided constant descriptor may be a (non-anonymous) module or class,
174 # or a qualified constant name as a string or symbol.
176 # Returns true if the constant was not previously marked for unloading, false
178 def unloadable(const_desc
)
179 Dependencies
.mark_for_unload const_desc
183 # Exception file-blaming
184 module Blamable
#:nodoc:
185 def blame_file
!(file
)
186 (@blamed_files ||= []).unshift file
194 return nil if blamed_files
.empty
?
195 "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
199 @blamed_files = exc
.blamed_files
.clone
205 Object
.instance_eval
{ include Loadable
}
206 Module
.instance_eval
{ include ModuleConstMissing
}
207 Class
.instance_eval
{ include ClassConstMissing
}
208 Exception
.instance_eval
{ include Blamable
}
213 ModuleConstMissing
.excluded(Module
)
214 Loadable
.excluded(Object
)
222 def depend_on(file_name
, swallow_load_errors
= false)
223 path
= search_for_file(file_name
)
224 require_or_load(path
|| file_name
)
226 raise unless swallow_load_errors
229 def associate_with(file_name
)
230 depend_on(file_name
, true)
236 remove_unloadable_constants
!
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
)
245 # Record that we've seen this file *before* loading it to avoid an
246 # infinite loop with mutual dependencies.
251 log
"loading #{file_name}"
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?
258 if !warnings_on_first_load
or history
.include?(expanded
)
259 result
= load_file(*load_args
)
261 enable_warnings
{ result
= load_file(*load_args
) }
264 log
"requiring #{file_name}"
265 result
= require file_name
268 loaded
.delete expanded
272 # Record history *after* loading so first load gets warnings.
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
282 names
= path
.to_s
.split('::')
283 names
.shift
if names
.first
.empty
?
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
)
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
)
301 def uninherited_const_defined
?(mod
, const
) #:nodoc:
302 mod
.const_defined
?(const
, false)
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
)
312 bases
.collect
do |root
|
313 expanded_root
= File
.expand_path(root
)
314 next unless %r
{\A
#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
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
321 qualified_const_defined
?(nesting_camel
)
326 end.flatten
.compact
.uniq
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
336 nil # Gee, I sure wish we had first_match ;-)
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
)
348 def load_once_path
?(path
)
349 load_once_paths
.any
? { |base
| path
.starts_with
? base
}
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
)
360 into
.const_set const_name
, mod
361 autoloaded_constants
<< qualified_name
unless load_once_paths
.include?(base_path
)
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.
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 }
379 newly_defined_paths
= new_constants_in(*parent_paths
) do
380 result
= load_without_new_constant_marking path
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
?
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}"
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
)
405 log
"Substituting Object for Kernel"
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
?
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!"
417 raise ArgumentError
, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined
?(from_mod
, const_name
)
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}")
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
)
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.
437 return parent
.const_missing(const_name
)
438 rescue NameError
=> e
439 raise unless e
.missing_name
? qualified_name_for(parent
, const_name
)
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
}
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
)
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
))
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
477 explicitly_unloadable_constants
<< name
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.
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
)
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
|
498 initial_constants
= desc
.local_constant_names
499 elsif desc
.is_a
?(String
) || desc
.is_a
?(Symbol
)
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
509 raise Argument
, "#{desc.inspect} does not describe a module!"
512 [mod_name
, initial_constants
]
515 constant_watch_stack_mutex
.synchronize
do
516 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_mutex
.synchronize
do
535 constant_watch_stack
.each
do |frame_name
, constants
|
536 constants
.concat new_constants
if frame_name
== mod_name
540 new_constants
.collect
do |suffix
|
541 mod_name
== "Object" ? suffix
: "#{mod_name}::#{suffix}"
545 log
"New constants: #{new_constants * ', '}"
548 log
"Error during loading, removing partially loaded constants "
549 new_constants
.each
{ |name
| remove_constant name
}
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
567 class LoadingModule
#:nodoc:
568 # Old style environment.rb referenced this method directly. Please note, it doesn't
569 # actually *do* anything any more.
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"
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:
582 when String
then desc
.starts_with
?('::') ? desc
[2..-1] : desc
583 when Symbol
then desc
.to_s
585 raise ArgumentError
, "Anonymous modules have no name to be referenced by" if desc
.name
.blank
?
587 else raise TypeError
, "Not a valid constant descriptor: #{desc.inspect}"
591 def remove_constant(const
) #:nodoc:
592 return false unless qualified_const_defined
? const
594 const
= $1 if /\A::(.*)\Z/ =~ const
.to_s
595 names
= const
.to_s
.split('::')
596 if names
.size
== 1 # It's under Object
599 parent
= (names
[0..-2] * '::').constantize
602 log
"removing constant #{const}"
603 parent
.instance_eval
{ remove_const names
.last
}
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})"
618 if logger
&& log_activity
619 logger
.debug
"Dependencies: #{msg}"
625 ActiveSupport
::Dependencies.hook
!