module RSpec module Mocks module AnyInstance # @private # The `AnyInstance::Recorder` is responsible for redefining the klass's # instance method in order to add any stubs/expectations the first time # the method is called. It's not capable of updating a stub on an instance # that's already been previously stubbed (either directly, or via # `any_instance`). # # This proxy sits in front of the recorder and delegates both to it # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed # instance of the class, in order to propagates changes to the instances. # # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless # and is not persisted in `RSpec::Mocks.space`. # # Proxying for the message expectation fluent interface (typically chained # off of the return value of one of these methods) is provided by the # `FluentInterfaceProxy` class below. class Proxy def initialize(recorder, target_proxies) @recorder = recorder @target_proxies = target_proxies end def klass @recorder.klass end def stub(method_name_or_method_map, &block) if Hash === method_name_or_method_map method_name_or_method_map.each do |method_name, return_value| stub(method_name).and_return(return_value) end else perform_proxying(__method__, [method_name_or_method_map], block) do |proxy| proxy.add_stub(method_name_or_method_map, &block) end end end def unstub(method_name) perform_proxying(__method__, [method_name], nil) do |proxy| proxy.remove_stub_if_present(method_name) end end def stub_chain(*chain, &block) perform_proxying(__method__, chain, block) do |proxy| Mocks::StubChain.stub_chain_on(proxy.object, *chain, &block) end end def expect_chain(*chain, &block) perform_proxying(__method__, chain, block) do |proxy| Mocks::ExpectChain.expect_chain_on(proxy.object, *chain, &block) end end def should_receive(method_name, &block) perform_proxying(__method__, [method_name], block) do |proxy| # Yeah, this is a bit odd...but if we used `add_message_expectation` # then it would act like `expect_every_instance_of(klass).to receive`. # The any_instance recorder takes care of validating that an instance # received the message. proxy.add_stub(method_name, &block) end end def should_not_receive(method_name, &block) perform_proxying(__method__, [method_name], block) do |proxy| proxy.add_message_expectation(method_name, &block).never end end private def perform_proxying(method_name, args, block, &target_proxy_block) recorder_value = @recorder.__send__(method_name, *args, &block) proxy_values = @target_proxies.map(&target_proxy_block) FluentInterfaceProxy.new([recorder_value] + proxy_values) end end # @private # Delegates messages to each of the given targets in order to # provide the fluent interface that is available off of message # expectations when dealing with `any_instance`. # # `targets` will typically contain 1 of the `AnyInstance::Recorder` # return values and N `MessageExpectation` instances (one per instance # of the `any_instance` klass). class FluentInterfaceProxy def initialize(targets) @targets = targets end if RUBY_VERSION.to_f > 1.8 def respond_to_missing?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end else def respond_to?(method_name, include_private=false) super || @targets.first.respond_to?(method_name, include_private) end end def method_missing(*args, &block) return_values = @targets.map { |t| t.__send__(*args, &block) } FluentInterfaceProxy.new(return_values) end end end end end