Changeset 5386
- Timestamp:
- 11/01/06 23:21:13 (2 years ago)
- Files:
-
- trunk/activesupport/CHANGELOG (modified) (1 diff)
- trunk/activesupport/lib/active_support/dependencies.rb (modified) (5 diffs)
- trunk/activesupport/test/autoloading_fixtures/class_folder/nested_class.rb (modified) (1 diff)
- trunk/activesupport/test/autoloading_fixtures/multiple_constant_file.rb (added)
- trunk/activesupport/test/dependencies_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activesupport/CHANGELOG
r5363 r5386 1 1 *SVN* 2 3 * Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar] 2 4 3 5 * next_week respects DST changes. #6483 [marclove] trunk/activesupport/lib/active_support/dependencies.rb
r5307 r5386 47 47 mattr_accessor :log_activity 48 48 self.log_activity = false 49 50 # :nodoc: 51 # An internal stack used to record which constants are loaded by any block. 52 mattr_accessor :constant_watch_stack 53 self.constant_watch_stack = [] 49 54 50 55 def load? … … 189 194 log_call path, const_paths 190 195 const_paths = [const_paths].compact unless const_paths.is_a? Array 191 undefined_before = const_paths.reject(&method(:qualified_const_defined?)) 192 193 result = load path 194 195 newly_defined_paths = undefined_before.select(&method(:qualified_const_defined?)) 196 parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object } 197 198 result = nil 199 newly_defined_paths = new_constants_in(*parent_paths) do 200 result = load_without_new_constant_marking path 201 end 202 196 203 autoloaded_constants.concat newly_defined_paths 197 204 autoloaded_constants.uniq! … … 291 298 end 292 299 300 # Run the provided block and detect the new constants that were loaded during 301 # its execution. Constants may only be regarded as 'new' once -- so if the 302 # block calls +new_constants_in+ again, then the constants defined within the 303 # inner call will not be reported in this one. 304 def new_constants_in(*descs) 305 log_call(*descs) 306 307 # Build the watch frames. Each frame is a tuple of 308 # [module_name_as_string, constants_defined_elsewhere] 309 watch_frames = descs.collect do |desc| 310 if desc.is_a? Module 311 mod_name = desc.name 312 initial_constants = desc.constants 313 elsif desc.is_a?(String) || desc.is_a?(Symbol) 314 mod_name = desc.to_s 315 316 # Handle the case where the module has yet to be defined. 317 initial_constants = if qualified_const_defined?(mod_name) 318 mod_name.constantize.constants 319 else 320 [] 321 end 322 else 323 raise Argument, "#{desc.inspect} does not describe a module!" 324 end 325 326 [mod_name, initial_constants] 327 end 328 329 constant_watch_stack.concat watch_frames 330 331 yield # Now yield to the code that is to define new constants. 332 333 # Find the new constants. 334 new_constants = watch_frames.collect do |mod_name, prior_constants| 335 # Module still doesn't exist? Treat it as if it has no constants. 336 next [] unless qualified_const_defined?(mod_name) 337 338 mod = mod_name.constantize 339 next [] unless mod.is_a? Module 340 new_constants = mod.constants - prior_constants 341 342 # Make sure no other frames takes credit for these constants. 343 constant_watch_stack.each do |frame_name, constants| 344 constants.concat new_constants if frame_name == mod_name 345 end 346 347 new_constants.collect do |suffix| 348 mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}" 349 end 350 end.flatten 351 352 log "New constants: #{new_constants * ', '}" 353 return new_constants 354 ensure 355 # Remove the stack frames that we added. 356 if defined?(watch_frames) && ! watch_frames.empty? 357 frame_ids = watch_frames.collect(&:object_id) 358 constant_watch_stack.delete_if do |watch_frame| 359 frame_ids.include? watch_frame.object_id 360 end 361 end 362 end 363 293 364 class LoadingModule 294 365 # Old style environment.rb referenced this method directly. Please note, it doesn't … … 390 461 391 462 class Object #:nodoc: 463 464 alias_method :load_without_new_constant_marking, :load 465 392 466 def load(file, *extras) 393 super(file, *extras)467 Dependencies.new_constants_in(Object) { super(file, *extras) } 394 468 rescue Exception => exception # errors from loading file 395 469 exception.blame_file! file … … 398 472 399 473 def require(file, *extras) 400 super(file, *extras)474 Dependencies.new_constants_in(Object) { super(file, *extras) } 401 475 rescue Exception => exception # errors from required file 402 476 exception.blame_file! file trunk/activesupport/test/autoloading_fixtures/class_folder/nested_class.rb
r4769 r5386 2 2 class NestedClass 3 3 end 4 5 class SiblingClass 6 end 4 7 end trunk/activesupport/test/dependencies_test.rb
r5307 r5386 484 484 end 485 485 486 def test_new_contants_in_without_constants 487 assert_equal [], (Dependencies.new_constants_in(Object) { }) 488 assert Dependencies.constant_watch_stack.empty? 489 end 490 491 def test_new_constants_in_with_a_single_constant 492 assert_equal(["Hello"], (Dependencies.new_constants_in(Object) do 493 Object.const_set :Hello, 10 494 end)) 495 assert Dependencies.constant_watch_stack.empty? 496 ensure 497 Object.send :remove_const, :Hello rescue nil 498 end 499 500 def test_new_constants_in_with_nesting 501 outer = Dependencies.new_constants_in(Object) do 502 Object.const_set :OuterBefore, 10 503 504 inner = Dependencies.new_constants_in(Object) do 505 Object.const_set :Inner, 20 506 end 507 assert_equal ["Inner"], inner 508 509 Object.const_set :OuterAfter, 30 510 end 511 512 assert_equal ["OuterAfter", "OuterBefore"], outer.sort 513 assert Dependencies.constant_watch_stack.empty? 514 ensure 515 %w(OuterBefore Inner OuterAfter).each do |name| 516 Object.send :remove_const, name rescue nil 517 end 518 end 519 520 def test_new_constants_in_module 521 Object.const_set :M, Module.new 522 523 outer = Dependencies.new_constants_in(M) do 524 M.const_set :OuterBefore, 10 525 526 inner = Dependencies.new_constants_in(M) do 527 M.const_set :Inner, 20 528 end 529 assert_equal ["M::Inner"], inner 530 531 M.const_set :OuterAfter, 30 532 end 533 assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort 534 assert Dependencies.constant_watch_stack.empty? 535 ensure 536 Object.send :remove_const, :M rescue nil 537 end 538 539 def test_new_constants_in_module_using_name 540 outer = Dependencies.new_constants_in(:M) do 541 Object.const_set :M, Module.new 542 M.const_set :OuterBefore, 10 543 544 inner = Dependencies.new_constants_in(:M) do 545 M.const_set :Inner, 20 546 end 547 assert_equal ["M::Inner"], inner 548 549 M.const_set :OuterAfter, 30 550 end 551 assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort 552 assert Dependencies.constant_watch_stack.empty? 553 ensure 554 Object.send :remove_const, :M rescue nil 555 end 556 557 def test_file_with_multiple_constants_and_require_dependency 558 with_loading 'autoloading_fixtures' do 559 assert ! defined?(MultipleConstantFile) 560 assert ! defined?(SiblingConstant) 561 562 require_dependency 'multiple_constant_file' 563 assert defined?(MultipleConstantFile) 564 assert defined?(SiblingConstant) 565 assert Dependencies.autoloaded?(:MultipleConstantFile) 566 assert Dependencies.autoloaded?(:SiblingConstant) 567 568 Dependencies.clear 569 570 assert ! defined?(MultipleConstantFile) 571 assert ! defined?(SiblingConstant) 572 end 573 end 574 575 def test_file_with_multiple_constants_and_auto_loading 576 with_loading 'autoloading_fixtures' do 577 assert ! defined?(MultipleConstantFile) 578 assert ! defined?(SiblingConstant) 579 580 assert_equal 10, MultipleConstantFile 581 582 assert defined?(MultipleConstantFile) 583 assert defined?(SiblingConstant) 584 assert Dependencies.autoloaded?(:MultipleConstantFile) 585 assert Dependencies.autoloaded?(:SiblingConstant) 586 587 Dependencies.clear 588 589 assert ! defined?(MultipleConstantFile) 590 assert ! defined?(SiblingConstant) 591 end 592 end 593 594 def test_nested_file_with_multiple_constants_and_require_dependency 595 with_loading 'autoloading_fixtures' do 596 assert ! defined?(ClassFolder::NestedClass) 597 assert ! defined?(ClassFolder::SiblingClass) 598 599 require_dependency 'class_folder/nested_class' 600 601 assert defined?(ClassFolder::NestedClass) 602 assert defined?(ClassFolder::SiblingClass) 603 assert Dependencies.autoloaded?("ClassFolder::NestedClass") 604 assert Dependencies.autoloaded?("ClassFolder::SiblingClass") 605 606 Dependencies.clear 607 608 assert ! defined?(ClassFolder::NestedClass) 609 assert ! defined?(ClassFolder::SiblingClass) 610 end 611 end 612 613 def test_nested_file_with_multiple_constants_and_auto_loading 614 with_loading 'autoloading_fixtures' do 615 assert ! defined?(ClassFolder::NestedClass) 616 assert ! defined?(ClassFolder::SiblingClass) 617 618 assert_kind_of Class, ClassFolder::NestedClass 619 620 assert defined?(ClassFolder::NestedClass) 621 assert defined?(ClassFolder::SiblingClass) 622 assert Dependencies.autoloaded?("ClassFolder::NestedClass") 623 assert Dependencies.autoloaded?("ClassFolder::SiblingClass") 624 625 Dependencies.clear 626 627 assert ! defined?(ClassFolder::NestedClass) 628 assert ! defined?(ClassFolder::SiblingClass) 629 end 630 end 631 632 486 633 end