From 7980d9dad1044261e6d95fdf6ccfc9f91f9ceeec Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Thu, 6 Jul 2023 08:15:23 +0100 Subject: [PATCH] Merge pull request #1552 from igor-drozdov/ruby2-keywords-for-missing-method-double Fix and_call_original for Ruby 3.2 --- lib/rspec/mocks/method_double.rb | 15 +++++++--- spec/rspec/mocks/and_call_original_spec.rb | 34 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/rspec/mocks/method_double.rb b/lib/rspec/mocks/method_double.rb index 39494f290..7417f65aa 100644 --- a/lib/rspec/mocks/method_double.rb +++ b/lib/rspec/mocks/method_double.rb @@ -26,10 +26,7 @@ def original_implementation_callable # handler of the object. This accounts for cases where the user has not # correctly defined `respond_to?`, and also 1.8 which does not provide # method handles for missing methods even if `respond_to?` is correct. - @original_implementation_callable ||= original_method || - Proc.new do |*args, &block| - @object.__send__(:method_missing, @method_name, *args, &block) - end + @original_implementation_callable ||= original_method || method_missing_block end alias_method :save_original_implementation_callable!, :original_implementation_callable @@ -40,6 +37,16 @@ def original_method @proxy.original_method_handle_for(method_name) end + # @private + def method_missing_block + block = Proc.new do |*args, &b| + @object.__send__(:method_missing, @method_name, *args, &b) + end + block.ruby2_keywords if block.respond_to?(:ruby2_keywords) + + block + end + # @private def visibility @proxy.visibility_for(@method_name) diff --git a/spec/rspec/mocks/and_call_original_spec.rb b/spec/rspec/mocks/and_call_original_spec.rb index c01b74c47..bd207d4e6 100644 --- a/spec/rspec/mocks/and_call_original_spec.rb +++ b/spec/rspec/mocks/and_call_original_spec.rb @@ -243,6 +243,40 @@ def instance.foo(bar: nil); bar; end CODE end + if RSpec::Support::RubyFeatures.required_kw_args_supported? + binding.eval(<<-RUBY, __FILE__, __LINE__) + context 'on an object with a method propagated by method_missing' do + before do + klass.class_exec do + private + + def call_method_with_kwarg(arg, kwarg:) + [arg, kwarg] + end + + def method_missing(name, *args, **kwargs) + if name.to_s == "method_with_kwarg" + call_method_with_kwarg(*args, **kwargs) + else + super + end + end + end + end + + it 'works for the method propagated by method missing' do + expect(instance).to receive(:method_with_kwarg).with(:arg, kwarg: 1).and_call_original + expect(instance.method_with_kwarg(:arg, kwarg: 1)).to eq([:arg, 1]) + end + + it 'works for the method of any_instance mock propagated by method missing' do + expect_any_instance_of(klass).to receive(:method_with_kwarg).with(:arg, kwarg: 1).and_call_original + expect(instance.method_with_kwarg(:arg, kwarg: 1)).to eq([:arg, 1]) + end + end + RUBY + end + context 'on an object that defines method_missing' do before do klass.class_exec do