Skip to content
This repository
Browse code

Fix transfer_nested_constants option of stub_const.

My changes in deec990 caused it to blow up in the face of inherited constants.
  • Loading branch information...
commit 39ae1f3d2c555781071f8537753697a5ffc3c997 1 parent e7bd234
Myron Marston authored
8  Changelog.md
Source Rendered
... ...
@@ -1,3 +1,11 @@
  1
+### dev
  2
+[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.2...master)
  3
+
  4
+Bug fixes
  5
+
  6
+* Fix `:transfer_nested_constants` option of `stub_const` so that it
  7
+  doesn't blow up when there are inherited constants. (Myron Marston)
  8
+
1 9
 ### 2.11.2 / 2012-08-11
2 10
 [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2)
3 11
 
35  lib/rspec/mocks/stub_const.rb
@@ -10,17 +10,22 @@ module RecursiveConstMethods
10 10
       # we need to conditionally define methods to ignore the top-level/inherited
11 11
       # constants.
12 12
       #
13  
-      # Given `class A; end`:
  13
+      # Given:
  14
+      #   class A; B = 1; end
  15
+      #   class C < A; end
14 16
       #
15 17
       # On 1.8:
16  
-      #   - A.const_get("Hash") # => ::Hash
17  
-      #   - A.const_defined?("Hash") # => false
18  
-      #   - Neither method accepts the extra `inherit` argument
  18
+      #   - C.const_get("Hash") # => ::Hash
  19
+      #   - C.const_defined?("Hash") # => false
  20
+      #   - C.constants # => ["A"]
  21
+      #   - None of these methods accept the extra `inherit` argument
19 22
       # On 1.9:
20  
-      #   - A.const_get("Hash") # => ::Hash
21  
-      #   - A.const_defined?("Hash") # => true
22  
-      #   - A.const_get("Hash", false) # => raises NameError
23  
-      #   - A.const_defined?("Hash", false) # => false
  23
+      #   - C.const_get("Hash") # => ::Hash
  24
+      #   - C.const_defined?("Hash") # => true
  25
+      #   - C.const_get("Hash", false) # => raises NameError
  26
+      #   - C.const_defined?("Hash", false) # => false
  27
+      #   - C.constants # => [:A]
  28
+      #   - C.constants(false) #=> []
24 29
       if Module.method(:const_defined?).arity == 1
25 30
         def const_defined_on?(mod, const_name)
26 31
           mod.const_defined?(const_name)
@@ -33,6 +38,10 @@ def get_const_defined_on(mod, const_name)
33 38
 
34 39
           raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
35 40
         end
  41
+
  42
+        def constants_defined_on(mod)
  43
+          mod.constants.select { |c| const_defined_on?(mod, c) }
  44
+        end
36 45
       else
37 46
         def const_defined_on?(mod, const_name)
38 47
           mod.const_defined?(const_name, false)
@@ -41,6 +50,10 @@ def const_defined_on?(mod, const_name)
41 50
         def get_const_defined_on(mod, const_name)
42 51
           mod.const_get(const_name, false)
43 52
         end
  53
+
  54
+        def constants_defined_on(mod)
  55
+          mod.constants(false)
  56
+        end
44 57
       end
45 58
 
46 59
       def recursive_const_get(const_name)
@@ -217,10 +230,10 @@ def verify_constants_to_transfer!
217 230
 
218 231
           if @transfer_nested_constants.is_a?(Array)
219 232
             @transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
220  
-            undefined_constants = @transfer_nested_constants - @original_value.constants
  233
+            undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value)
221 234
 
222 235
             if undefined_constants.any?
223  
-              available_constants = @original_value.constants - @transfer_nested_constants
  236
+              available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
224 237
               raise ArgumentError,
225 238
                 "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " +
226 239
                 "for #{@full_constant_name} since they are not defined. Did you mean " +
@@ -229,7 +242,7 @@ def verify_constants_to_transfer!
229 242
 
230 243
             @transfer_nested_constants
231 244
           else
232  
-            @original_value.constants
  245
+            constants_defined_on(@original_value)
233 246
           end
234 247
         end
235 248
       end
22  spec/rspec/mocks/stub_const_spec.rb
@@ -12,6 +12,10 @@ class NestedEvenMore
12 12
   end
13 13
 end
14 14
 
  15
+class TestSubClass < TestClass
  16
+  P = :p
  17
+end
  18
+
15 19
 module RSpec
16 20
   module Mocks
17 21
     describe "Constant Stubbing" do
@@ -121,6 +125,24 @@ def change_const_value_to(value)
121 125
             stub::Nested.should be(tc_nested)
122 126
           end
123 127
 
  128
+          it 'does not transfer nested constants that are inherited from a superclass' do
  129
+            stub = Module.new
  130
+            stub_const("TestSubClass", stub, :transfer_nested_constants => true)
  131
+            stub::P.should eq(:p)
  132
+            defined?(stub::M).should be_false
  133
+            defined?(stub::N).should be_false
  134
+          end
  135
+
  136
+          it 'raises an error when asked to transfer a nested inherited constant' do
  137
+            original_tsc = TestSubClass
  138
+
  139
+            expect {
  140
+              stub_const("TestSubClass", Module.new, :transfer_nested_constants => [:M])
  141
+            }.to raise_error(ArgumentError)
  142
+
  143
+            TestSubClass.should be(original_tsc)
  144
+          end
  145
+
124 146
           it 'allows nested constants to be selectively transferred to a stub module' do
125 147
             stub = Module.new
126 148
             stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])

0 notes on commit 39ae1f3

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