Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 5386

Show
Ignore:
Timestamp:
11/01/06 23:21:13 (2 years ago)
Author:
ulysses
Message:

Update dependencies to allow constants to be defined alongside their siblings.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activesupport/CHANGELOG

    r5363 r5386  
    11*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] 
    24 
    35* next_week respects DST changes.  #6483 [marclove] 
  • trunk/activesupport/lib/active_support/dependencies.rb

    r5307 r5386  
    4747  mattr_accessor :log_activity 
    4848  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 = [] 
    4954   
    5055  def load? 
     
    189194    log_call path, const_paths 
    190195    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     
    196203    autoloaded_constants.concat newly_defined_paths 
    197204    autoloaded_constants.uniq! 
     
    291298  end 
    292299   
     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   
    293364  class LoadingModule 
    294365    # Old style environment.rb referenced this method directly.  Please note, it doesn't 
     
    390461 
    391462class Object #:nodoc: 
     463   
     464  alias_method :load_without_new_constant_marking, :load 
     465   
    392466  def load(file, *extras) 
    393     super(file, *extras) 
     467    Dependencies.new_constants_in(Object) { super(file, *extras) } 
    394468  rescue Exception => exception  # errors from loading file 
    395469    exception.blame_file! file 
     
    398472 
    399473  def require(file, *extras) 
    400     super(file, *extras) 
     474    Dependencies.new_constants_in(Object) { super(file, *extras) } 
    401475  rescue Exception => exception  # errors from required file 
    402476    exception.blame_file! file 
  • trunk/activesupport/test/autoloading_fixtures/class_folder/nested_class.rb

    r4769 r5386  
    22  class NestedClass 
    33  end 
     4   
     5  class SiblingClass 
     6  end 
    47end 
  • trunk/activesupport/test/dependencies_test.rb

    r5307 r5386  
    484484  end 
    485485   
     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   
    486633end