Changeset 9143
- Timestamp:
- 03/30/08 05:02:25 (5 months ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activesupport/lib/active_support/dependencies.rb
r8482 r9143 22 22 mattr_accessor :mechanism 23 23 self.mechanism = :load 24 24 25 25 # The set of directories from which we may automatically load files. Files 26 26 # under these directories will be reloaded on each request in development mode, … … 28 28 mattr_accessor :load_paths 29 29 self.load_paths = [] 30 30 31 31 # The set of directories from which automatically loaded constants are loaded 32 32 # only once. All directories in this set must also be present in +load_paths+. 33 33 mattr_accessor :load_once_paths 34 34 self.load_once_paths = [] 35 35 36 36 # An array of qualified constant names that have been loaded. Adding a name to 37 37 # this array will cause it to be unloaded the next time Dependencies are cleared. 38 38 mattr_accessor :autoloaded_constants 39 39 self.autoloaded_constants = [] 40 40 41 41 # An array of constant names that need to be unloaded on every request. Used 42 42 # to allow arbitrary constants to be marked for unloading. 43 43 mattr_accessor :explicitly_unloadable_constants 44 44 self.explicitly_unloadable_constants = [] 45 45 46 46 # Set to true to enable logging of const_missing and file loads 47 47 mattr_accessor :log_activity 48 48 self.log_activity = false 49 49 50 50 # An internal stack used to record which constants are loaded by any block. 51 51 mattr_accessor :constant_watch_stack 52 52 self.constant_watch_stack = [] 53 53 54 54 def load? 55 55 mechanism == :load … … 82 82 # infinite loop with mutual dependencies. 83 83 loaded << expanded 84 84 85 85 if load? 86 86 log "loading #{file_name}" … … 90 90 load_args = ["#{file_name}.rb"] 91 91 load_args << const_path unless const_path.nil? 92 92 93 93 if !warnings_on_first_load or history.include?(expanded) 94 94 result = load_file(*load_args) … … 109 109 return result 110 110 end 111 111 112 112 # Is the provided constant path defined? 113 113 def qualified_const_defined?(path) 114 114 raise NameError, "#{path.inspect} is not a valid constant name!" unless 115 115 /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path 116 116 117 117 names = path.to_s.split('::') 118 118 names.shift if names.first.empty? 119 119 120 120 # We can't use defined? because it will invoke const_missing for the parent 121 121 # of the name we are checking. … … 126 126 return true 127 127 end 128 128 129 129 # Given +path+, a filesystem path to a ruby file, return an array of constant 130 130 # paths which would cause Dependencies to attempt to load this file. 131 # 131 # 132 132 def loadable_constants_for_path(path, bases = load_paths) 133 133 path = $1 if path =~ /\A(.*)\.rb\Z/ 134 134 expanded_path = File.expand_path(path) 135 135 136 136 bases.collect do |root| 137 137 expanded_root = File.expand_path(root) 138 138 next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path 139 139 140 140 nesting = expanded_path[(expanded_root.size)..-1] 141 141 nesting = nesting[1..-1] if nesting && nesting[0] == ?/ 142 142 next if nesting.blank? 143 143 144 144 [ 145 145 nesting.camelize, … … 149 149 end.flatten.compact.uniq 150 150 end 151 151 152 152 # Search for a file in load_paths matching the provided suffix. 153 153 def search_for_file(path_suffix) … … 159 159 nil # Gee, I sure wish we had first_match ;-) 160 160 end 161 161 162 162 # Does the provided path_suffix correspond to an autoloadable module? 163 # Instead of returning a boolean, the autoload base for this module is returned. 163 # Instead of returning a boolean, the autoload base for this module is returned. 164 164 def autoloadable_module?(path_suffix) 165 165 load_paths.each do |load_path| … … 168 168 nil 169 169 end 170 170 171 171 def load_once_path?(path) 172 172 load_once_paths.any? { |base| path.starts_with? base } 173 173 end 174 174 175 175 # Attempt to autoload the provided module name by searching for a directory 176 176 # matching the expect path suffix. If found, the module is created and assigned … … 185 185 return mod 186 186 end 187 187 188 188 # Load the file at the provided path. +const_paths+ is a set of qualified 189 189 # constant names. When loading the file, Dependencies will watch for the 190 190 # addition of these constants. Each that is defined will be marked as 191 191 # autoloaded, and will be removed when Dependencies.clear is next called. 192 # 192 # 193 193 # If the second parameter is left off, then Dependencies will construct a set 194 194 # of names that the file at +path+ may define. See … … 198 198 const_paths = [const_paths].compact unless const_paths.is_a? Array 199 199 parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object } 200 200 201 201 result = nil 202 202 newly_defined_paths = new_constants_in(*parent_paths) do 203 203 result = load_without_new_constant_marking path 204 204 end 205 205 206 206 autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) 207 207 autoloaded_constants.uniq! … … 209 209 return result 210 210 end 211 211 212 212 # Return the constant path for the provided parent and constant name. 213 213 def qualified_name_for(mod, name) … … 215 215 (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}" 216 216 end 217 217 218 218 # Load the constant named +const_name+ which is missing from +from_mod+. If 219 219 # it is not possible to load the constant into from_mod, try its parent module … … 239 239 240 240 raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name) 241 241 242 242 qualified_name = qualified_name_for from_mod, const_name 243 243 path_suffix = qualified_name.underscore 244 244 name_error = NameError.new("uninitialized constant #{qualified_name}") 245 245 246 246 file_path = search_for_file(path_suffix) 247 247 if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load … … 267 267 end 268 268 end 269 269 270 270 # Remove the constants that have been autoloaded, and those that have been 271 271 # marked for unloading. … … 275 275 explicitly_unloadable_constants.each { |const| remove_constant const } 276 276 end 277 277 278 278 # Determine if the given constant has been automatically loaded. 279 279 def autoloaded?(desc) … … 284 284 return autoloaded_constants.include?(name) 285 285 end 286 286 287 287 # Will the provided constant descriptor be unloaded? 288 288 def will_unload?(const_desc) … … 290 290 explicitly_unloadable_constants.include?(to_constant_name(const_desc)) 291 291 end 292 292 293 293 # Mark the provided constant name for unloading. This constant will be 294 294 # unloaded on each request, not just the next one. … … 302 302 end 303 303 end 304 304 305 305 # Run the provided block and detect the new constants that were loaded during 306 306 # its execution. Constants may only be regarded as 'new' once -- so if the 307 307 # block calls +new_constants_in+ again, then the constants defined within the 308 308 # inner call will not be reported in this one. 309 # 309 # 310 310 # If the provided block does not run to completion, and instead raises an 311 311 # exception, any new constants are regarded as being only partially defined … … 313 313 def new_constants_in(*descs) 314 314 log_call(*descs) 315 315 316 316 # Build the watch frames. Each frame is a tuple of 317 317 # [module_name_as_string, constants_defined_elsewhere] … … 322 322 elsif desc.is_a?(String) || desc.is_a?(Symbol) 323 323 mod_name = desc.to_s 324 324 325 325 # Handle the case where the module has yet to be defined. 326 326 initial_constants = if qualified_const_defined?(mod_name) … … 332 332 raise Argument, "#{desc.inspect} does not describe a module!" 333 333 end 334 334 335 335 [mod_name, initial_constants] 336 336 end 337 337 338 338 constant_watch_stack.concat watch_frames 339 339 340 340 aborting = true 341 341 begin … … 347 347 # Module still doesn't exist? Treat it as if it has no constants. 348 348 next [] unless qualified_const_defined?(mod_name) 349 349 350 350 mod = mod_name.constantize 351 351 next [] unless mod.is_a? Module 352 352 new_constants = mod.local_constant_names - prior_constants 353 353 354 354 # Make sure no other frames takes credit for these constants. 355 355 constant_watch_stack.each do |frame_name, constants| 356 356 constants.concat new_constants if frame_name == mod_name 357 357 end 358 358 359 359 new_constants.collect do |suffix| 360 360 mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}" 361 361 end 362 362 end.flatten 363 363 364 364 log "New constants: #{new_constants * ', '}" 365 365 366 366 if aborting 367 367 log "Error during loading, removing partially loaded constants " … … 370 370 end 371 371 end 372 372 373 373 return new_constants 374 374 ensure … … 381 381 end 382 382 end 383 383 384 384 class LoadingModule #:nodoc: 385 385 # Old style environment.rb referenced this method directly. Please note, it doesn't … … 426 426 arg_str = args.collect(&:inspect) * ', ' 427 427 /in `([a-z_\?\!]+)'/ =~ caller(1).first 428 selector = $1 || '<unknown>' 428 selector = $1 || '<unknown>' 429 429 log "called #{selector}(#{arg_str})" 430 430 end 431 431 432 432 def log(msg) 433 433 if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity … … 435 435 end 436 436 end 437 437 438 438 end 439 439 … … 447 447 # Rename the original handler so we can chain it to the new one 448 448 alias :rails_original_const_missing :const_missing 449 449 450 450 # Use const_missing to autoload associations so we don't have to 451 451 # require_association when using single-table inheritance. … … 453 453 Dependencies.load_missing_constant self, class_id 454 454 end 455 455 456 456 def unloadable(const_desc = self) 457 457 super(const_desc) 458 458 end 459 459 460 460 end 461 461 … … 483 483 484 484 class Object 485 486 485 alias_method :load_without_new_constant_marking, :load 487 486 488 487 def load(file, *extras) #:nodoc: 489 Dependencies.new_constants_in(Object) { super (file, *extras)}488 Dependencies.new_constants_in(Object) { super } 490 489 rescue Exception => exception # errors from loading file 491 490 exception.blame_file! file … … 494 493 495 494 def require(file, *extras) #:nodoc: 496 Dependencies.new_constants_in(Object) { super (file, *extras)}495 Dependencies.new_constants_in(Object) { super } 497 496 rescue Exception => exception # errors from required file 498 497 exception.blame_file! file … … 502 501 # Mark the given constant as unloadable. Unloadable constants are removed each 503 502 # time dependencies are cleared. 504 # 503 # 505 504 # Note that marking a constant for unloading need only be done once. Setup 506 505 # or init scripts may list each unloadable constant that may need unloading; 507 506 # each constant will be removed for every subsequent clear, as opposed to for 508 507 # the first clear. 509 # 508 # 510 509 # The provided constant descriptor may be a (non-anonymous) module or class, 511 510 # or a qualified constant name as a string or symbol. 512 # 511 # 513 512 # Returns true if the constant was not previously marked for unloading, false 514 513 # otherwise. … … 516 515 Dependencies.mark_for_unload const_desc 517 516 end 518 519 517 end 520 518