Permalink
Browse files

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...
1 parent e7bd234 commit 39ae1f3d2c555781071f8537753697a5ffc3c997 @myronmarston myronmarston committed Aug 14, 2012
Showing with 54 additions and 11 deletions.
  1. +8 −0 Changelog.md
  2. +24 −11 lib/rspec/mocks/stub_const.rb
  3. +22 −0 spec/rspec/mocks/stub_const_spec.rb
View
@@ -1,3 +1,11 @@
+### dev
+[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.2...master)
+
+Bug fixes
+
+* Fix `:transfer_nested_constants` option of `stub_const` so that it
+ doesn't blow up when there are inherited constants. (Myron Marston)
+
### 2.11.2 / 2012-08-11
[full changelog](http://github.com/rspec/rspec-mocks/compare/v2.11.1...v2.11.2)
@@ -10,17 +10,22 @@ module RecursiveConstMethods
# we need to conditionally define methods to ignore the top-level/inherited
# constants.
#
- # Given `class A; end`:
+ # Given:
+ # class A; B = 1; end
+ # class C < A; end
#
# On 1.8:
- # - A.const_get("Hash") # => ::Hash
- # - A.const_defined?("Hash") # => false
- # - Neither method accepts the extra `inherit` argument
+ # - C.const_get("Hash") # => ::Hash
+ # - C.const_defined?("Hash") # => false
+ # - C.constants # => ["A"]
+ # - None of these methods accept the extra `inherit` argument
# On 1.9:
- # - A.const_get("Hash") # => ::Hash
- # - A.const_defined?("Hash") # => true
- # - A.const_get("Hash", false) # => raises NameError
- # - A.const_defined?("Hash", false) # => false
+ # - C.const_get("Hash") # => ::Hash
+ # - C.const_defined?("Hash") # => true
+ # - C.const_get("Hash", false) # => raises NameError
+ # - C.const_defined?("Hash", false) # => false
+ # - C.constants # => [:A]
+ # - C.constants(false) #=> []
if Module.method(:const_defined?).arity == 1
def const_defined_on?(mod, const_name)
mod.const_defined?(const_name)
@@ -33,6 +38,10 @@ def get_const_defined_on(mod, const_name)
raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
end
+
+ def constants_defined_on(mod)
+ mod.constants.select { |c| const_defined_on?(mod, c) }
+ end
else
def const_defined_on?(mod, const_name)
mod.const_defined?(const_name, false)
@@ -41,6 +50,10 @@ def const_defined_on?(mod, const_name)
def get_const_defined_on(mod, const_name)
mod.const_get(const_name, false)
end
+
+ def constants_defined_on(mod)
+ mod.constants(false)
+ end
end
def recursive_const_get(const_name)
@@ -217,10 +230,10 @@ def verify_constants_to_transfer!
if @transfer_nested_constants.is_a?(Array)
@transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
- undefined_constants = @transfer_nested_constants - @original_value.constants
+ undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value)
if undefined_constants.any?
- available_constants = @original_value.constants - @transfer_nested_constants
+ available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
raise ArgumentError,
"Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " +
"for #{@full_constant_name} since they are not defined. Did you mean " +
@@ -229,7 +242,7 @@ def verify_constants_to_transfer!
@transfer_nested_constants
else
- @original_value.constants
+ constants_defined_on(@original_value)
end
end
end
@@ -12,6 +12,10 @@ class NestedEvenMore
end
end
+class TestSubClass < TestClass
+ P = :p
+end
+
module RSpec
module Mocks
describe "Constant Stubbing" do
@@ -121,6 +125,24 @@ def change_const_value_to(value)
stub::Nested.should be(tc_nested)
end
+ it 'does not transfer nested constants that are inherited from a superclass' do
+ stub = Module.new
+ stub_const("TestSubClass", stub, :transfer_nested_constants => true)
+ stub::P.should eq(:p)
+ defined?(stub::M).should be_false
+ defined?(stub::N).should be_false
+ end
+
+ it 'raises an error when asked to transfer a nested inherited constant' do
+ original_tsc = TestSubClass
+
+ expect {
+ stub_const("TestSubClass", Module.new, :transfer_nested_constants => [:M])
+ }.to raise_error(ArgumentError)
+
+ TestSubClass.should be(original_tsc)
+ end
+
it 'allows nested constants to be selectively transferred to a stub module' do
stub = Module.new
stub_const("TestClass", stub, :transfer_nested_constants => [:M, :N])

0 comments on commit 39ae1f3

Please sign in to comment.