123456789_123456789_123456789_123456789_123456789_

Module: RSpec::Mocks::PartialClassDoubleProxyMethods Private

Do not use. This module is for internal use only.
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

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(message)
  @method_doubles.fetch(message) 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(message)
  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.

[ GitHub ]

  
# File 'rspec-mocks/lib/rspec/mocks/proxy.rb', line 406

def original_method_handle_for(message)
  unbound_method = superclass_proxy &&
    superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.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(message)
      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(message)
  double = method_double_from_ancestor_for(message)
  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