Class: RSpec::Mocks::AnyInstance::Recorder Private
Relationships & Source Files | |
Inherits: | Object |
Defined in: | rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb |
Overview
Given a class TheClass
, TheClass.any_instance
returns a Recorder
, which records stubs and message expectations for later playback on instances of TheClass
.
Further constraints are stored in instances of [Chain](Chain).
Class Method Summary
- .new(klass) ⇒ Recorder constructor Internal use only
Instance Attribute Summary
- #klass readonly Internal use only Internal use only
- #message_chains readonly Internal use only Internal use only
- #stubs readonly Internal use only Internal use only
Instance Method Summary
- #already_observing?(method_name) ⇒ Boolean Internal use only Internal use only
- #build_alias_method_name(method_name) Internal use only Internal use only
- #expect_chain(*method_names_and_optional_return_values, &block) Internal use only Internal use only
- #instance_that_received(method_name) Internal use only Internal use only
- #notify_received_message(_object, message, args, _blk) Internal use only Internal use only
- #playback!(instance, method_name) Internal use only Internal use only
-
#should_not_receive(method_name, &block)
Internal use only
The opposite of #should_receive
-
#should_receive(method_name, &block)
Internal use only
Initializes the recording a message expectation to be played back against any instance of this object that invokes the submitted method.
- #stop_all_observation! Internal use only Internal use only
-
#stub(method_name, &block)
Internal use only
Initializes the recording a stub to be played back against any instance of this object that invokes the submitted method.
-
#stub_chain(*method_names_and_optional_return_values, &block)
Internal use only
Initializes the recording a stub chain to be played back against any instance of this object that invokes the method matching the first argument.
-
#unstub(method_name)
Internal use only
Removes any previously recorded stubs, stub_chains or message expectations that use
method_name
. -
#verify
Internal use only
Internal use only
Used internally to verify that message expectations have been fulfilled.
- #stop_observing!(method_name) protected Internal use only
-
#allow_no_prepended_module_definition_of(_method_name)
private
See additional method definition at line 284.
- #ancestor_is_an_observer?(ancestor, method_name) ⇒ Boolean private Internal use only
- #backup_method!(method_name) private Internal use only
- #mark_invoked!(method_name) private Internal use only
- #normalize_chain(*args) {|args.first, args| ... } private Internal use only
- #observe!(method_name) private Internal use only
- #public_protected_or_private_method_defined?(method_name) ⇒ Boolean private Internal use only
- #received_expected_message!(method_name) private Internal use only
- #remove_dummy_method!(method_name) private Internal use only
- #restore_method!(method_name) private Internal use only
- #restore_original_method!(method_name) private Internal use only
- #super_class_observers_for(method_name) private Internal use only
- #super_class_observing?(method_name) ⇒ Boolean private Internal use only
Constructor Details
.new(klass) ⇒ Recorder
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 16
def initialize(klass) @message_chains = MessageChains.new @stubs = Hash.new { |hash, key| hash[key] = [] } @observed_methods = [] @played_methods = {} @backed_up_method_owner = {} @klass = klass @expectation_set = false return unless RSpec::Mocks.configuration.verify_partial_doubles? RSpec::Mocks.configuration. .each do |block| block.call(ObjectReference.for(klass)) end end
Instance Attribute Details
#klass (readonly)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 14
attr_reader :, :stubs, :klass
#message_chains (readonly)
#stubs (readonly)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 14
attr_reader :, :stubs, :klass
Instance Method Details
#allow_no_prepended_module_definition_of(_method_name) (private)
See additional method definition at line 284.
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 292
def allow_no_prepended_module_definition_of(method_name) prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass) problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) } return unless problem_mod AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod) end
#already_observing?(method_name) ⇒ Boolean
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 127
def already_observing?(method_name) @observed_methods.include?(method_name) || super_class_observing?(method_name) end
#ancestor_is_an_observer?(ancestor, method_name) ⇒ Boolean
(private)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 159
def ancestor_is_an_observer?(ancestor, method_name) return if ancestor == @klass ::RSpec::Mocks.space. any_instance_recorder_for(ancestor).already_observing?(method_name) end
#backup_method!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 235
def backup_method!(method_name) return unless public_protected_or_private_method_defined?(method_name) alias_method_name = build_alias_method_name(method_name) @backed_up_method_owner[method_name.to_sym] ||= @klass.instance_method(method_name).owner @klass.class_exec do alias_method alias_method_name, method_name end end
#build_alias_method_name(method_name)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 122
def build_alias_method_name(method_name) "__#{method_name}_without_any_instance__" end
#expect_chain(*method_names_and_optional_return_values, &block)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 53
def expect_chain(*method_names_and_optional_return_values, &block) @expectation_set = true normalize_chain(*method_names_and_optional_return_values) do |method_name, args| observe!(method_name) .add(method_name, ExpectChainChain.new(self, *args, &block)) end end
#instance_that_received(method_name)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 117
def instance_that_received(method_name) @played_methods[method_name] end
#mark_invoked!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 271
def mark_invoked!(method_name) backup_method!(method_name) recorder = self @klass.__send__(:define_method, method_name) do |*_args, &_blk| invoked_instance = recorder.instance_that_received(method_name) inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>" AnyInstance.error_generator. ( method_name, inspect, invoked_instance ) end end
#normalize_chain(*args) {|args.first, args| ... } (private)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 178
def normalize_chain(*args) args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a } yield args.first, args end
#notify_received_message(_object, message, args, _blk)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 132
def (_object, , args, _blk) has_expectation = false .each_unfulfilled_expectation_matching(, *args) do |expectation| has_expectation = true expectation.expectation_fulfilled! end return unless has_expectation restore_method!( ) mark_invoked!( ) end
#observe!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 249
def observe!(method_name) allow_no_prepended_module_definition_of(method_name) if RSpec::Mocks.configuration.verify_partial_doubles? && !Mocks.configuration.temporarily_suppress_partial_double_verification unless public_protected_or_private_method_defined?(method_name) AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name) end end stop_observing!(method_name) if already_observing?(method_name) @observed_methods << method_name backup_method!(method_name) recorder = self method_was_private = @klass.private_method_defined?(method_name) @klass.__send__(:define_method, method_name) do |*args, &blk| recorder.playback!(self, method_name) __send__(method_name, *args, &blk) end @klass.__send__(:private, method_name) if method_was_private @klass.__send__(:ruby2_keywords, method_name) if @klass.respond_to?(:ruby2_keywords, true) end
#playback!(instance, method_name)
#public_protected_or_private_method_defined?(method_name) ⇒ Boolean
(private)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 245
def public_protected_or_private_method_defined?(method_name) MethodReference.method_defined_at_any_visibility?(@klass, method_name) end
#received_expected_message!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 183
def (method_name) . (method_name) restore_method!(method_name) mark_invoked!(method_name) end
#remove_dummy_method!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 229
def remove_dummy_method!(method_name) @klass.class_exec do remove_method method_name end end
#restore_method!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 189
def restore_method!(method_name) if public_protected_or_private_method_defined?(build_alias_method_name(method_name)) restore_original_method!(method_name) else remove_dummy_method!(method_name) end end
#restore_original_method!(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 197
def restore_original_method!(method_name) return unless @klass.instance_method(method_name).owner == @klass alias_method_name = build_alias_method_name(method_name) @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner| remove_method method_name # A @klass can have methods implemented (see Method#owner) in @klass # or inherited from a superclass. In ruby 2.2 and earlier, we can copy # a method regardless of the 'owner' and restore it to @klass after # because a call to 'super' from @klass's copied method would end up # calling the original class's superclass's method. # # With the commit below, available starting in 2.3.0, ruby changed # this behavior and a call to 'super' from the method copied to @klass # will call @klass's superclass method, which is the original # implementer of this method! This leads to very strange errors # if @klass's copied method calls 'super', since it would end up # calling itself, the original method implemented in @klass's # superclass. # # For ruby 2.3 and above, we need to only restore methods that # @klass originally owned. # # https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81 if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self alias_method method_name, alias_method_name end remove_method alias_method_name end end
#should_not_receive(method_name, &block)
The opposite of #should_receive
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 75
def should_not_receive(method_name, &block) should_receive(method_name, &block).never end
#should_receive(method_name, &block)
Initializes the recording a message expectation to be played back against any instance of this object that invokes the submitted method.
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 66
def should_receive(method_name, &block) @expectation_set = true observe!(method_name) .add(method_name, PositiveExpectationChain.new(self, method_name, &block)) end
#stop_all_observation!
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 104
def stop_all_observation! @observed_methods.each { |method_name| restore_method!(method_name) } end
#stop_observing!(method_name) (protected)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 148
def stop_observing!(method_name) restore_method!(method_name) @observed_methods.delete(method_name) super_class_observers_for(method_name).each do |ancestor| ::RSpec::Mocks.space. any_instance_recorder_for(ancestor).stop_observing!(method_name) end end
#stub(method_name, &block)
Initializes the recording a stub to be played back against any instance of this object that invokes the submitted method.
#stub_chain(*method_names_and_optional_return_values, &block)
Initializes the recording a stub chain to be played back against any instance of this object that invokes the method matching the first argument.
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 45
def stub_chain(*method_names_and_optional_return_values, &block) normalize_chain(*method_names_and_optional_return_values) do |method_name, args| observe!(method_name) .add(method_name, StubChainChain.new(self, *args, &block)) end end
#super_class_observers_for(method_name) (private)
[ GitHub ]# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 166
def super_class_observers_for(method_name) @klass.ancestors.select do |ancestor| ancestor_is_an_observer?(ancestor, method_name) end end
#super_class_observing?(method_name) ⇒ Boolean
(private)
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 172
def super_class_observing?(method_name) @klass.ancestors.any? do |ancestor| ancestor_is_an_observer?(ancestor, method_name) end end
#unstub(method_name)
Removes any previously recorded stubs, stub_chains or message expectations that use method_name
.
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 83
def unstub(method_name) unless @observed_methods.include?(method_name.to_sym) AnyInstance.error_generator.raise_method_not_stubbed_error(method_name) end .remove_stub_chains_for!(method_name) stubs[method_name].clear stop_observing!(method_name) unless .has_expectation?(method_name) end
#verify
Used internally to verify that message expectations have been fulfilled.
# File 'rspec-mocks/lib/rspec/mocks/any_instance/recorder.rb', line 96
def verify return unless @expectation_set return if .all_expectations_fulfilled? AnyInstance.error_generator. ( .unfulfilled_expectations) end