Skip to content
This repository
Browse code

Dependencies cleanup. Fixes #4221.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4060 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 9efca53908c09b7f188183aec6c0a4a2df347316 1 parent 2440349
Nicholas Seckar authored March 27, 2006
6  activesupport/CHANGELOG
... ...
@@ -1,5 +1,11 @@
1 1
 *SVN*
2 2
 
  3
+* When possible, avoid incorrectly obtaining constants from parent modules. Fixes #4221. [Nicholas Seckar]
  4
+
  5
+* Add more tests for dependencies; refactor existing cases. [Nicholas Seckar]
  6
+
  7
+* Move Module#parent and Module#as_load_path into core_ext. Add Module#parent. [Nicholas Seckar]
  8
+
3 9
 * Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. [Nicholas Seckar]
4 10
 
5 11
 * Remove a hack intended to avoid unloading the same class twice, but which would not work anyways. [Nicholas Seckar]
4  activesupport/lib/active_support/core_ext/module.rb
... ...
@@ -1,3 +1,5 @@
1 1
 require File.dirname(__FILE__) + '/module/inclusion'
2 2
 require File.dirname(__FILE__) + '/module/attribute_accessors'
3  
-require File.dirname(__FILE__) + '/module/delegation'
  3
+require File.dirname(__FILE__) + '/module/delegation'
  4
+require File.dirname(__FILE__) + '/module/introspection'
  5
+require File.dirname(__FILE__) + '/module/loading'
21  activesupport/lib/active_support/core_ext/module/introspection.rb
... ...
@@ -0,0 +1,21 @@
  1
+class Module
  2
+  # Return the module which contains this one; if this is a root module, such as
  3
+  # +::MyModule+, then Object is returned.
  4
+  def parent
  5
+    parent_name = name.split('::')[0..-2] * '::'
  6
+    parent_name.empty? ? Object : parent_name.constantize
  7
+  end
  8
+  
  9
+  # Return all the parents of this module, ordered from nested outwards. The
  10
+  # receiver is not contained within the result.
  11
+  def parents
  12
+    parents = []
  13
+    parts = name.split('::')[0..-2]
  14
+    until parts.empty?
  15
+      parents << (parts * '::').constantize
  16
+      parts.pop
  17
+    end
  18
+    parents << Object unless parents.include? Object
  19
+    parents
  20
+  end
  21
+end
13  activesupport/lib/active_support/core_ext/module/loading.rb
... ...
@@ -0,0 +1,13 @@
  1
+class Module
  2
+  def as_load_path
  3
+    if self == Object || self == Kernel
  4
+      ''
  5
+    elsif is_a? Class
  6
+      parent == self ? '' : parent.as_load_path
  7
+    else
  8
+      name.split('::').collect do |word|
  9
+        word.underscore
  10
+      end * '/'
  11
+    end
  12
+  end
  13
+end
23  activesupport/lib/active_support/dependencies.rb
@@ -78,23 +78,6 @@ class Module #:nodoc:
78 78
   # Rename the original handler so we can chain it to the new one
79 79
   alias :rails_original_const_missing :const_missing
80 80
   
81  
-  def parent
82  
-    parent_name = name.split('::')[0..-2] * '::'
83  
-    parent_name.empty? ? Object : parent_name.constantize
84  
-  end
85  
-  
86  
-  def as_load_path
87  
-    if self == Object || self == Kernel
88  
-      ''
89  
-    elsif is_a? Class
90  
-      parent == self ? '' : parent.as_load_path
91  
-    else
92  
-      name.split('::').collect do |word|
93  
-        word.underscore
94  
-      end * '/'
95  
-    end
96  
-  end
97  
-  
98 81
   # Use const_missing to autoload associations so we don't have to
99 82
   # require_association when using single-table inheritance.
100 83
   def const_missing(class_id)
@@ -116,7 +99,11 @@ def const_missing(class_id)
116 99
         return mod
117 100
       end
118 101
       
119  
-      if parent && parent != self
  102
+      # Attempt to access the name from the parent, unless we don't have a valid
  103
+      # parent, or the constant is already defined in the parent. If the latter
  104
+      # is the case, then we are being queried via self::class_id, and we should
  105
+      # avoid returning the constant from the parent if possible.
  106
+      if parent && parent != self && ! parents.any? { |p| p.const_defined?(class_id) }
120 107
         suppress(NameError) do
121 108
           return parent.send(:const_missing, class_id)
122 109
         end
2  activesupport/test/autoloading_fixtures/module_folder/nested_class.rb
... ...
@@ -0,0 +1,2 @@
  1
+class ModuleFolder::NestedClass
  2
+end
2  activesupport/test/autoloading_fixtures/module_folder/nested_sibling.rb
... ...
@@ -0,0 +1,2 @@
  1
+class ModuleFolder::NestedSibling
  2
+end
16  activesupport/test/core_ext/module_test.rb
@@ -82,4 +82,20 @@ def test_missing_delegation_target
82 82
     assert_raises(ArgumentError) { eval($nowhere) }
83 83
     assert_raises(ArgumentError) { eval($noplace) }
84 84
   end
  85
+  
  86
+  def test_parent
  87
+    assert_equal Yz::Zy, Yz::Zy::Cd.parent
  88
+    assert_equal Yz, Yz::Zy.parent
  89
+    assert_equal Object, Yz.parent
  90
+  end
  91
+  
  92
+  def test_parents
  93
+    assert_equal [Yz::Zy, Yz, Object], Yz::Zy::Cd.parents
  94
+    assert_equal [Yz, Object], Yz::Zy.parents
  95
+  end
  96
+  
  97
+  def test_as_load_path
  98
+    assert_equal 'yz/zy', Yz::Zy.as_load_path
  99
+    assert_equal 'yz', Yz.as_load_path
  100
+  end
85 101
 end
175  activesupport/test/dependencies_test.rb
@@ -7,6 +7,17 @@ class DependenciesTest < Test::Unit::TestCase
7 7
   def teardown
8 8
     Dependencies.clear
9 9
   end
  10
+  
  11
+  def with_loading(from_dir = nil)
  12
+    prior_path = $LOAD_PATH.clone
  13
+    $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/#{from_dir}" if from_dir
  14
+    old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
  15
+    yield
  16
+  ensure
  17
+    $LOAD_PATH.clear
  18
+    $LOAD_PATH.concat prior_path
  19
+    Dependencies.mechanism = old_mechanism
  20
+  end
10 21
 
11 22
   def test_tracking_loaded_files
12 23
     require_dependency(File.dirname(__FILE__) + "/dependencies/service_one")
@@ -29,76 +40,68 @@ def test_missing_association_raises_nothing
29 40
   end
30 41
 
31 42
   def test_dependency_which_raises_exception_isnt_added_to_loaded_set
32  
-    old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
  43
+    with_loading do
  44
+      filename = "#{File.dirname(__FILE__)}/dependencies/raises_exception"
  45
+      $raises_exception_load_count = 0
33 46
 
34  
-    filename = "#{File.dirname(__FILE__)}/dependencies/raises_exception"
35  
-    $raises_exception_load_count = 0
  47
+      5.times do |count|
  48
+        assert_raises(RuntimeError) { require_dependency filename }
  49
+        assert_equal count + 1, $raises_exception_load_count
36 50
 
37  
-    5.times do |count|
38  
-      assert_raises(RuntimeError) { require_dependency filename }
39  
-      assert_equal count + 1, $raises_exception_load_count
40  
-
41  
-      assert !Dependencies.loaded.include?(filename)
42  
-      assert !Dependencies.history.include?(filename)
  51
+        assert !Dependencies.loaded.include?(filename)
  52
+        assert !Dependencies.history.include?(filename)
  53
+      end
43 54
     end
44  
-  ensure
45  
-    Dependencies.mechanism = old_mechanism
46 55
   end
47 56
 
48 57
   def test_warnings_should_be_enabled_on_first_load
49  
-    old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
50  
-    old_warnings, Dependencies.warnings_on_first_load = Dependencies.warnings_on_first_load, true
  58
+    with_loading do
  59
+      old_warnings, Dependencies.warnings_on_first_load = Dependencies.warnings_on_first_load, true
51 60
 
52  
-    filename = "#{File.dirname(__FILE__)}/dependencies/check_warnings"
53  
-    $check_warnings_load_count = 0
  61
+      filename = "#{File.dirname(__FILE__)}/dependencies/check_warnings"
  62
+      $check_warnings_load_count = 0
54 63
 
55  
-    assert !Dependencies.loaded.include?(filename)
56  
-    assert !Dependencies.history.include?(filename)
  64
+      assert !Dependencies.loaded.include?(filename)
  65
+      assert !Dependencies.history.include?(filename)
57 66
 
58  
-    silence_warnings { require_dependency filename }
59  
-    assert_equal 1, $check_warnings_load_count
60  
-    assert_equal true, $checked_verbose, 'On first load warnings should be enabled.'
  67
+      silence_warnings { require_dependency filename }
  68
+      assert_equal 1, $check_warnings_load_count
  69
+      assert_equal true, $checked_verbose, 'On first load warnings should be enabled.'
61 70
 
62  
-    assert Dependencies.loaded.include?(filename)
63  
-    Dependencies.clear
64  
-    assert !Dependencies.loaded.include?(filename)
65  
-    assert Dependencies.history.include?(filename)
  71
+      assert Dependencies.loaded.include?(filename)
  72
+      Dependencies.clear
  73
+      assert !Dependencies.loaded.include?(filename)
  74
+      assert Dependencies.history.include?(filename)
66 75
 
67  
-    silence_warnings { require_dependency filename }
68  
-    assert_equal 2, $check_warnings_load_count
69  
-    assert_equal nil, $checked_verbose, 'After first load warnings should be left alone.'
  76
+      silence_warnings { require_dependency filename }
  77
+      assert_equal 2, $check_warnings_load_count
  78
+      assert_equal nil, $checked_verbose, 'After first load warnings should be left alone.'
70 79
 
71  
-    assert Dependencies.loaded.include?(filename)
72  
-    Dependencies.clear
73  
-    assert !Dependencies.loaded.include?(filename)
74  
-    assert Dependencies.history.include?(filename)
  80
+      assert Dependencies.loaded.include?(filename)
  81
+      Dependencies.clear
  82
+      assert !Dependencies.loaded.include?(filename)
  83
+      assert Dependencies.history.include?(filename)
75 84
 
76  
-    enable_warnings { require_dependency filename }
77  
-    assert_equal 3, $check_warnings_load_count
78  
-    assert_equal true, $checked_verbose, 'After first load warnings should be left alone.'
  85
+      enable_warnings { require_dependency filename }
  86
+      assert_equal 3, $check_warnings_load_count
  87
+      assert_equal true, $checked_verbose, 'After first load warnings should be left alone.'
79 88
 
80  
-    assert Dependencies.loaded.include?(filename)
81  
-  ensure
82  
-    Dependencies.mechanism = old_mechanism
83  
-    Dependencies.warnings_on_first_load = old_warnings
  89
+      assert Dependencies.loaded.include?(filename)
  90
+    end
84 91
   end
85 92
 
86 93
   def test_mutual_dependencies_dont_infinite_loop
87  
-    $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/dependencies"
88  
-    old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
  94
+    with_loading 'dependencies' do
  95
+      $mutual_dependencies_count = 0
  96
+      assert_nothing_raised { require_dependency 'mutual_one' }
  97
+      assert_equal 2, $mutual_dependencies_count
89 98
 
90  
-    $mutual_dependencies_count = 0
91  
-    assert_nothing_raised { require_dependency 'mutual_one' }
92  
-    assert_equal 2, $mutual_dependencies_count
  99
+      Dependencies.clear
93 100
 
94  
-    Dependencies.clear
95  
-
96  
-    $mutual_dependencies_count = 0
97  
-    assert_nothing_raised { require_dependency 'mutual_two' }
98  
-    assert_equal 2, $mutual_dependencies_count
99  
-  ensure
100  
-    $LOAD_PATH.shift
101  
-    Dependencies.mechanism = old_mechanism
  101
+      $mutual_dependencies_count = 0
  102
+      assert_nothing_raised { require_dependency 'mutual_two' }
  103
+      assert_equal 2, $mutual_dependencies_count
  104
+    end
102 105
   end
103 106
   
104 107
   def test_as_load_path
@@ -106,43 +109,53 @@ def test_as_load_path
106 109
   end
107 110
   
108 111
   def test_module_loading
109  
-    begin
110  
-      $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/autoloading_fixtures"
111  
-      old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
112  
-    
  112
+    with_loading 'autoloading_fixtures' do
113 113
       assert_kind_of Module, A
114 114
       assert_kind_of Class, A::B
115 115
       assert_kind_of Class, A::C::D
116 116
       assert_kind_of Class, A::C::E::F
117  
-    ensure
118  
-      $LOAD_PATH.shift
119  
-      Dependencies.mechanism = old_mechanism
120 117
     end
121 118
   end
122 119
   
123  
-  def test_non_existing_cost_raises_nameerrror
124  
-    begin
125  
-      $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/autoloading_fixtures"
126  
-      old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load
127  
-      assert_raises(NameError) do
128  
-        DoesNotExist
129  
-      end
130  
-    
131  
-      assert_raises(NameError) do
132  
-        NoModule::DoesNotExist
133  
-      end
134  
-    
135  
-      assert_raises(NameError) do
136  
-        A::DoesNotExist
137  
-      end
138  
-
139  
-      assert_raises(NameError) do
140  
-        A::B::DoesNotExist
141  
-      end
142  
-    ensure
143  
-      $LOAD_PATH.shift
144  
-      Dependencies.mechanism = old_mechanism
  120
+  def test_non_existing_const_raises_name_error
  121
+    with_loading 'autoloading_fixtures' do
  122
+      assert_raises(NameError) { DoesNotExist }
  123
+      assert_raises(NameError) { NoModule::DoesNotExist }
  124
+      assert_raises(NameError) { A::DoesNotExist }
  125
+      assert_raises(NameError) { A::B::DoesNotExist }
145 126
     end
146  
-    
147 127
   end
  128
+  
  129
+  def test_directories_should_manifest_as_modules
  130
+    with_loading 'autoloading_fixtures' do
  131
+      assert_kind_of Module, ModuleFolder
  132
+      Object.send :remove_const, :ModuleFolder
  133
+    end
  134
+  end
  135
+  
  136
+  def test_nested_class_access
  137
+    with_loading 'autoloading_fixtures' do
  138
+      assert_kind_of Class, ModuleFolder::NestedClass
  139
+      Object.send :remove_const, :ModuleFolder
  140
+    end
  141
+  end
  142
+  
  143
+  def test_nested_class_can_access_sibling
  144
+    with_loading 'autoloading_fixtures' do
  145
+      sibling = ModuleFolder::NestedClass.class_eval "NestedSibling"
  146
+      assert defined?(ModuleFolder::NestedSibling)
  147
+      assert_equal ModuleFolder::NestedSibling, sibling
  148
+      Object.send :remove_const, :ModuleFolder
  149
+    end
  150
+  end
  151
+  
  152
+  def failing_test_access_thru_and_upwards_fails
  153
+    with_loading 'autoloading_fixtures' do
  154
+      assert ! defined?(ModuleFolder)
  155
+      assert_raises(NameError) { ModuleFolder::Object }
  156
+      assert_raises(NameError) { ModuleFolder::NestedClass::Object }
  157
+      Object.send :remove_const, :ModuleFolder
  158
+    end
  159
+  end
  160
+  
148 161
 end

0 notes on commit 9efca53

Please sign in to comment.
Something went wrong with that request. Please try again.