Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix use of const_get/const_defined? to handle undefined consts like Foo::Hash #24

Merged
merged 1 commit into from

2 participants

@myronmarston
Collaborator

When you name a nested constant that ends in a name that matches a top-level constant (such as "Foo::Hash"), rspec-fire was verifying the presence of a stubbed method on ::Hash if "Foo::Hash" was not defined. 1.9's const_get/const_defined? accepts a flag argument to have it ignore inherited/top-level constants, but 1.8 doesn't accept this argument, so we have to conditionally define methods to handle this properly.

@xaviershay -- we were just bit by this bug. It'd be nice to get an official release with this fix out soonish--let me know if I can help with that.

@myronmarston myronmarston Fix use of const_get/const_defined? to handle undefined consts like F…
…oo::Hash.

When you name a nested constant that ends in a name that matches a top-level constant (such as "Foo::Hash"), rspec-fire was verifying the presence of a stubbed method on ::Hash if "Foo::Hash" was not defined. 1.9's const_get/const_defined? accepts a flag argument to have it ignore inherited/top-level constants, but 1.8 doesn't accept this argument, so we have to conditionally define methods to handle this properly.
b5fc940
@xaviershay xaviershay merged commit 1de8e57 into master
@xaviershay
Owner

Released in 1.1.2. Thanks!

@myronmarston
Collaborator

That was fast :). Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 11, 2012
  1. @myronmarston

    Fix use of const_get/const_defined? to handle undefined consts like F…

    myronmarston authored
    …oo::Hash.
    
    When you name a nested constant that ends in a name that matches a top-level constant (such as "Foo::Hash"), rspec-fire was verifying the presence of a stubbed method on ::Hash if "Foo::Hash" was not defined. 1.9's const_get/const_defined? accepts a flag argument to have it ignore inherited/top-level constants, but 1.8 doesn't accept this argument, so we have to conditionally define methods to handle this properly.
This page is out of date. Refresh to see the latest.
Showing with 48 additions and 3 deletions.
  1. +42 −3 lib/rspec/fire.rb
  2. +6 −0 spec/fire_double_spec.rb
View
45 lib/rspec/fire.rb
@@ -76,14 +76,53 @@ def arity_description
end
module RecursiveConstMethods
+ # We only want to consider constants that are defined directly on a
+ # particular module, and not include top-level/inherited constants.
+ # Unfortunately, the constant API changed between 1.8 and 1.9, so
+ # we need to conditionally define methods to ignore the top-level/inherited
+ # constants.
+ #
+ # Given `class A; end`:
+ #
+ # On 1.8:
+ # - A.const_get("Hash") # => ::Hash
+ # - A.const_defined?("Hash") # => false
+ # - Neither method accepts 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
+ if Module.method(:const_defined?).arity == 1
+ def const_defined_on?(mod, const_name)
+ mod.const_defined?(const_name)
+ end
+
+ def get_const_defined_on(mod, const_name)
+ if const_defined_on?(mod, const_name)
+ return mod.const_get(const_name)
+ end
+
+ raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
+ end
+ else
+ def const_defined_on?(mod, const_name)
+ mod.const_defined?(const_name, false)
+ end
+
+ def get_const_defined_on(mod, const_name)
+ mod.const_get(const_name, false)
+ end
+ end
+
def recursive_const_get name
- name.split('::').inject(Object) {|klass,name| klass.const_get name }
+ name.split('::').inject(Object) {|klass,name| get_const_defined_on(klass, name) }
end
def recursive_const_defined? name
!!name.split('::').inject(Object) {|klass,name|
- if klass && klass.const_defined?(name)
- klass.const_get name
+ if klass && const_defined_on?(klass, name)
+ get_const_defined_on(klass, name)
end
}
end
View
6 spec/fire_double_spec.rb
@@ -193,6 +193,12 @@ def fail_for_arguments(expected, actual)
double = fire_double("TestObject", :defined_method => 17)
double.defined_method.should eq(17)
end
+
+ it 'does not prevent stubbing methods on an unloaded nested constant with a name that matches a top-level constant' do
+ double = fire_double("TestObject::Hash")
+ double.stub(:foo).and_return("bar")
+ double.foo.should eq("bar")
+ end
end
describe '#fire_class_double' do
Something went wrong with that request. Please try again.