Module: RSpec::Mocks::PartialClassDoubleProxyMethods Private
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Included In:
| |
Defined in: | rspec-mocks/lib/rspec/mocks/proxy.rb |
Overview
When we mock or stub a method on a class, we have to treat it a bit different, because normally singleton method definitions only affect the object on which they are defined, but on classes they affect subclasses, too. As a result, we need some special handling to get the original method.
Instance Method Summary
- #initialize(source_space, *args) Internal use only
-
#original_method_handle_for(message)
Internal use only
Consider this situation:
- #method_double_from_ancestor_for(message) protected Internal use only
- #original_unbound_method_handle_from_ancestor_for(message) protected Internal use only
- #superclass_proxy protected Internal use only
Instance Method Details
#initialize(source_space, *args)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 381
def initialize(source_space, *args) @source_space = source_space super(*args) end
#method_double_from_ancestor_for(message) (protected)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 434
def method_double_from_ancestor_for( ) @method_doubles.fetch( ) do # The fact that there is no method double for this message indicates # that it has not been redefined by rspec-mocks. We need to continue # looking up the ancestor chain. return superclass_proxy && superclass_proxy.method_double_from_ancestor_for( ) end end
#original_method_handle_for(message)
Consider this situation:
class A; end
class B < A; end
allow(A).to receive(:new)
expect(B).to receive(:new).and_call_original
When getting the original definition for B.new
, we cannot rely purely on using ‘B.method(:new)` before our redefinition is defined on B
, because B.method(:new)
will return a method that will execute the stubbed version of the method on A
since singleton methods on classes are in the lookup hierarchy.
To do it properly, we need to find the original definition of new
from A
from before A
was stubbed, and we need to rebind it to B
so that it will run with the proper self
.
That’s what this method (together with #original_unbound_method_handle_from_ancestor_for) does.
# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 406
def original_method_handle_for( ) unbound_method = superclass_proxy && superclass_proxy.original_unbound_method_handle_from_ancestor_for( .to_sym) return super unless unbound_method unbound_method.bind(object) # :nocov: rescue TypeError if RUBY_VERSION == '1.8.7' # In MRI 1.8.7, a singleton method on a class cannot be rebound to its subclass if unbound_method && unbound_method.owner.ancestors.first != unbound_method.owner # This is a singleton method; we can't do anything with it # But we can work around this using a different implementation double = method_double_from_ancestor_for( ) return object.method(double.method_stasher.stashed_method_name) end end raise # :nocov: end
#original_unbound_method_handle_from_ancestor_for(message) (protected)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 429
def original_unbound_method_handle_from_ancestor_for( ) double = method_double_from_ancestor_for( ) double && double.original_method.unbind end
#superclass_proxy (protected)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 444
def superclass_proxy return @superclass_proxy if defined?(@superclass_proxy) if (superclass = object.superclass) @superclass_proxy = @source_space.superclass_proxy_for(superclass) else @superclass_proxy = nil end end