From 94c65252eed87d1768875812df65391818e14999 Mon Sep 17 00:00:00 2001 From: wasabigeek Date: Sat, 20 Aug 2022 18:23:13 +0800 Subject: [PATCH 01/14] Show proof of concept for keyword arg matching --- lib/mocha/expectation.rb | 1 + lib/mocha/mock.rb | 1 + .../last_positional_hash.rb | 26 +++++++++++++++++++ lib/mocha/parameters_matcher.rb | 7 ++++- test/unit/expectation_test.rb | 12 +++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 lib/mocha/parameter_matchers/last_positional_hash.rb diff --git a/lib/mocha/expectation.rb b/lib/mocha/expectation.rb index 4e71b555a..ce250c89c 100644 --- a/lib/mocha/expectation.rb +++ b/lib/mocha/expectation.rb @@ -222,6 +222,7 @@ def with(*expected_parameters, &matching_block) @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block) self end + ruby2_keywords(:with) if Module.respond_to?(:ruby2_keywords, true) # Modifies expectation so that the expected method must be called with a block. # diff --git a/lib/mocha/mock.rb b/lib/mocha/mock.rb index ea9f05e18..2425c5655 100644 --- a/lib/mocha/mock.rb +++ b/lib/mocha/mock.rb @@ -318,6 +318,7 @@ def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMis raise_unexpected_invocation_error(invocation, matching_expectation) end end + ruby2_keywords(:method_missing) if Module.respond_to?(:ruby2_keywords, true) # @private def respond_to_missing?(symbol, include_all) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb new file mode 100644 index 000000000..0c9db82e2 --- /dev/null +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -0,0 +1,26 @@ +require 'mocha/parameter_matchers/base' + +module Mocha + module ParameterMatchers + # Parameter matcher which matches when actual parameter equals expected value. + class LastPositionalHash < Base + # @private + def initialize(value) + @value = value + end + + # @private + def matches?(available_parameters) + parameter = available_parameters.shift + return false unless Hash.ruby2_keywords_hash?(parameter) && Hash.ruby2_keywords_hash?(@value) + + parameter == @value + end + + # @private + def mocha_inspect + @value.mocha_inspect + end + end + end +end diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb index 2b6ed8481..1c62839b7 100644 --- a/lib/mocha/parameters_matcher.rb +++ b/lib/mocha/parameters_matcher.rb @@ -1,5 +1,6 @@ require 'mocha/inspect' require 'mocha/parameter_matchers' +require 'mocha/parameter_matchers/last_positional_hash' module Mocha class ParametersMatcher @@ -28,7 +29,11 @@ def mocha_inspect end def matchers - @expected_parameters.map(&:to_matcher) + if (last_parameter = @expected_parameters.last).is_a?(Hash) + @expected_parameters[0...-1].map(&:to_matcher) + [ParameterMatchers::LastPositionalHash.new(last_parameter)] + else + @expected_parameters.map(&:to_matcher) + end end end end diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index 4384f78b0..95cd03538 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -67,6 +67,18 @@ def test_should_not_match_calls_to_same_method_with_parameters_not_constrained_a assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, 0, 3)) end + def test_should_match_keyword_args + expectation = new_expectation.with(1, a: 1) + assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) + end + + if RUBY_VERSION >= '3.0.0' + def test_should_not_match_keyword_args_with_last_positional_hashes + expectation = new_expectation.with(1, { a: 1 }) + assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) + end + end + def test_should_allow_invocations_until_expected_invocation_count_is_one_and_actual_invocation_count_would_be_two expectation = new_expectation.times(1) assert expectation.invocations_allowed? From 73c98e4fc57bcfffc4256a5cb1910d6810476871 Mon Sep 17 00:00:00 2001 From: wasabigeek Date: Sat, 20 Aug 2022 19:15:33 +0800 Subject: [PATCH 02/14] Fix handling of last positional args --- .../parameter_matchers/last_positional_hash.rb | 2 +- test/unit/expectation_test.rb | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb index 0c9db82e2..58e14f9f2 100644 --- a/lib/mocha/parameter_matchers/last_positional_hash.rb +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -12,7 +12,7 @@ def initialize(value) # @private def matches?(available_parameters) parameter = available_parameters.shift - return false unless Hash.ruby2_keywords_hash?(parameter) && Hash.ruby2_keywords_hash?(@value) + return false if Hash.ruby2_keywords_hash?(@value) && !Hash.ruby2_keywords_hash?(parameter) parameter == @value end diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index 95cd03538..11c98f5d8 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -67,16 +67,19 @@ def test_should_not_match_calls_to_same_method_with_parameters_not_constrained_a assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, 0, 3)) end + def test_should_match_last_positional_hash + expectation = new_expectation.with(1, { a: 1 }) + assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) + end + def test_should_match_keyword_args expectation = new_expectation.with(1, a: 1) assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) end - if RUBY_VERSION >= '3.0.0' - def test_should_not_match_keyword_args_with_last_positional_hashes - expectation = new_expectation.with(1, { a: 1 }) - assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) - end + def test_should_not_match_keyword_args_with_last_positional_hashes + expectation = new_expectation.with(1, a: 1) + assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) end def test_should_allow_invocations_until_expected_invocation_count_is_one_and_actual_invocation_count_would_be_two From 7b823bc093f94fce7a0f617b19e1d9acda8eba72 Mon Sep 17 00:00:00 2001 From: James Mead Date: Sat, 20 Aug 2022 17:29:34 +0100 Subject: [PATCH 03/14] WIP: Fix StubbaExampleTest I'm a bit worried that this is an indication that the keyword argument change will break a lot of existing tests. --- test/acceptance/stubba_example_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/stubba_example_test.rb b/test/acceptance/stubba_example_test.rb index b84ab62e4..8299e08d7 100644 --- a/test/acceptance/stubba_example_test.rb +++ b/test/acceptance/stubba_example_test.rb @@ -88,9 +88,9 @@ def should_stub_two_different_class_methods found_widgets = [Widget.new] created_widget = Widget.new Widget.expects(:find).with(:all).returns(found_widgets) - Widget.expects(:create).with(:model => 'wombat').returns(created_widget) + Widget.expects(:create).with({:model => 'wombat'}).returns(created_widget) assert_equal found_widgets, Widget.find(:all) - assert_equal created_widget, Widget.create(:model => 'wombat') + assert_equal created_widget, Widget.create({:model => 'wombat'}) end def should_stub_instance_method_on_any_instance_of_a_class From f3ba1d617c89d10f54a550c24d6f8fce45bd4eda Mon Sep 17 00:00:00 2001 From: James Mead Date: Sat, 20 Aug 2022 17:33:25 +0100 Subject: [PATCH 04/14] WIP: Fix support for older versions of Ruby --- .../parameter_matchers/last_positional_hash.rb | 4 +++- test/unit/expectation_test.rb | 16 ++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb index 58e14f9f2..faf2d0148 100644 --- a/lib/mocha/parameter_matchers/last_positional_hash.rb +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -12,7 +12,9 @@ def initialize(value) # @private def matches?(available_parameters) parameter = available_parameters.shift - return false if Hash.ruby2_keywords_hash?(@value) && !Hash.ruby2_keywords_hash?(parameter) + if Hash.respond_to?(:ruby2_keywords_hash?) + return false if Hash.ruby2_keywords_hash?(@value) && !Hash.ruby2_keywords_hash?(parameter) + end parameter == @value end diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index 11c98f5d8..d3c2117ec 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -72,14 +72,18 @@ def test_should_match_last_positional_hash assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) end - def test_should_match_keyword_args - expectation = new_expectation.with(1, a: 1) - assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) + if Hash.respond_to?(:ruby2_keywords_hash) + def test_should_match_keyword_args + expectation = new_expectation.with(1, a: 1) + assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) + end end - def test_should_not_match_keyword_args_with_last_positional_hashes - expectation = new_expectation.with(1, a: 1) - assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) + if Module.respond_to?(:ruby2_keywords, true) + def test_should_not_match_keyword_args_with_last_positional_hashes + expectation = new_expectation.with(1, a: 1) + assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) + end end def test_should_allow_invocations_until_expected_invocation_count_is_one_and_actual_invocation_count_would_be_two From d6da11e6a863bd5b2cf3021a6a011732e4d42163 Mon Sep 17 00:00:00 2001 From: James Mead Date: Sat, 20 Aug 2022 17:23:21 +0100 Subject: [PATCH 05/14] WIP: Fix rubocop issues --- .rubocop.yml | 4 ++++ test/acceptance/stubba_example_test.rb | 4 ++-- test/unit/expectation_test.rb | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 28074a966..d64f39453 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,10 @@ Style/Alias: Style/Documentation: Enabled: false +# Useful for distinuishing between positional & keyword arguments +Style/BracesAroundHashParameters: + Enabled: false + # Enumerable#each_with_object only available since Ruby v1.9 Style/EachWithObject: Enabled: false diff --git a/test/acceptance/stubba_example_test.rb b/test/acceptance/stubba_example_test.rb index 8299e08d7..64bc9153d 100644 --- a/test/acceptance/stubba_example_test.rb +++ b/test/acceptance/stubba_example_test.rb @@ -88,9 +88,9 @@ def should_stub_two_different_class_methods found_widgets = [Widget.new] created_widget = Widget.new Widget.expects(:find).with(:all).returns(found_widgets) - Widget.expects(:create).with({:model => 'wombat'}).returns(created_widget) + Widget.expects(:create).with({ :model => 'wombat' }).returns(created_widget) assert_equal found_widgets, Widget.find(:all) - assert_equal created_widget, Widget.create({:model => 'wombat'}) + assert_equal created_widget, Widget.create({ :model => 'wombat' }) end def should_stub_instance_method_on_any_instance_of_a_class diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index d3c2117ec..3d34a6344 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -68,21 +68,21 @@ def test_should_not_match_calls_to_same_method_with_parameters_not_constrained_a end def test_should_match_last_positional_hash - expectation = new_expectation.with(1, { a: 1 }) - assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) + expectation = new_expectation.with(1, { :a => 1 }) + assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { :a => 1 })) end if Hash.respond_to?(:ruby2_keywords_hash) def test_should_match_keyword_args - expectation = new_expectation.with(1, a: 1) - assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ a: 1 }))) + expectation = new_expectation.with(1, Hash.ruby2_keywords_hash({ :a => 1 })) + assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ :a => 1 }))) end end if Module.respond_to?(:ruby2_keywords, true) def test_should_not_match_keyword_args_with_last_positional_hashes - expectation = new_expectation.with(1, a: 1) - assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { a: 1 })) + expectation = new_expectation.with(1, Hash.ruby2_keywords_hash({ :a => 1 })) + assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { :a => 1 })) end end From 2da7951561d2e7ce8b9ff87411c9da49c8b8c0f7 Mon Sep 17 00:00:00 2001 From: James Mead Date: Sun, 21 Aug 2022 11:20:39 +0100 Subject: [PATCH 06/14] WIP: Fix Yardoc for LastPositionalHash Since this class is only used internally, we can make the whole class private from a documentation point of view. --- lib/mocha/parameter_matchers/last_positional_hash.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb index faf2d0148..a3a1af3a6 100644 --- a/lib/mocha/parameter_matchers/last_positional_hash.rb +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -2,14 +2,12 @@ module Mocha module ParameterMatchers - # Parameter matcher which matches when actual parameter equals expected value. + # @private class LastPositionalHash < Base - # @private def initialize(value) @value = value end - # @private def matches?(available_parameters) parameter = available_parameters.shift if Hash.respond_to?(:ruby2_keywords_hash?) @@ -19,7 +17,6 @@ def matches?(available_parameters) parameter == @value end - # @private def mocha_inspect @value.mocha_inspect end From 4667ce69610333a64cc3adadf227d4fd5fe86f48 Mon Sep 17 00:00:00 2001 From: James Mead Date: Sun, 21 Aug 2022 11:31:05 +0100 Subject: [PATCH 07/14] WIP: Centralize check for keyword argument support While it would be nicer to do some more fine-grained checking like in RSpec::Support::RubyFeatures [1], I think this is probably good enough for now. I think we are essentially using RUBY_V3_PLUS as a more generic version of RSpec::Support::RubyFeatures#distincts_kw_args_from_positional_hash? [1]: https://github.com/rspec/rspec-support/blob/528d88ce6fac5f83390bf430d1c47608e9d8d29a/lib/rspec/support/ruby_features.rb [2]: https://github.com/rspec/rspec-support/blob/528d88ce6fac5f83390bf430d1c47608e9d8d29a/lib/rspec/support/ruby_features.rb#L132-L134 --- lib/mocha/expectation.rb | 3 ++- lib/mocha/mock.rb | 3 ++- lib/mocha/parameter_matchers/last_positional_hash.rb | 3 ++- lib/mocha/ruby_version.rb | 1 + test/unit/expectation_test.rb | 5 ++--- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/mocha/expectation.rb b/lib/mocha/expectation.rb index ce250c89c..d2e6af425 100644 --- a/lib/mocha/expectation.rb +++ b/lib/mocha/expectation.rb @@ -1,3 +1,4 @@ +require 'mocha/ruby_version' require 'mocha/method_matcher' require 'mocha/parameters_matcher' require 'mocha/expectation_error' @@ -222,7 +223,7 @@ def with(*expected_parameters, &matching_block) @parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block) self end - ruby2_keywords(:with) if Module.respond_to?(:ruby2_keywords, true) + ruby2_keywords(:with) if RUBY_V3_PLUS # Modifies expectation so that the expected method must be called with a block. # diff --git a/lib/mocha/mock.rb b/lib/mocha/mock.rb index 2425c5655..23e3b82ae 100644 --- a/lib/mocha/mock.rb +++ b/lib/mocha/mock.rb @@ -1,3 +1,4 @@ +require 'mocha/ruby_version' require 'mocha/expectation' require 'mocha/expectation_list' require 'mocha/invocation' @@ -318,7 +319,7 @@ def method_missing(symbol, *arguments, &block) # rubocop:disable Style/MethodMis raise_unexpected_invocation_error(invocation, matching_expectation) end end - ruby2_keywords(:method_missing) if Module.respond_to?(:ruby2_keywords, true) + ruby2_keywords(:method_missing) if RUBY_V3_PLUS # @private def respond_to_missing?(symbol, include_all) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb index a3a1af3a6..6ba0b26b0 100644 --- a/lib/mocha/parameter_matchers/last_positional_hash.rb +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -1,4 +1,5 @@ require 'mocha/parameter_matchers/base' +require 'mocha/ruby_version' module Mocha module ParameterMatchers @@ -10,7 +11,7 @@ def initialize(value) def matches?(available_parameters) parameter = available_parameters.shift - if Hash.respond_to?(:ruby2_keywords_hash?) + if RUBY_V3_PLUS return false if Hash.ruby2_keywords_hash?(@value) && !Hash.ruby2_keywords_hash?(parameter) end diff --git a/lib/mocha/ruby_version.rb b/lib/mocha/ruby_version.rb index 01a74d05b..5cd14521e 100644 --- a/lib/mocha/ruby_version.rb +++ b/lib/mocha/ruby_version.rb @@ -1,3 +1,4 @@ module Mocha RUBY_V2_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2') + RUBY_V3_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('3') end diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index 3d34a6344..0dc29ed92 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -1,4 +1,5 @@ require File.expand_path('../../test_helper', __FILE__) +require 'mocha/ruby_version' require 'mocha/expectation' require 'mocha/invocation' require 'mocha/sequence' @@ -72,14 +73,12 @@ def test_should_match_last_positional_hash assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { :a => 1 })) end - if Hash.respond_to?(:ruby2_keywords_hash) + if RUBY_V3_PLUS def test_should_match_keyword_args expectation = new_expectation.with(1, Hash.ruby2_keywords_hash({ :a => 1 })) assert expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ :a => 1 }))) end - end - if Module.respond_to?(:ruby2_keywords, true) def test_should_not_match_keyword_args_with_last_positional_hashes expectation = new_expectation.with(1, Hash.ruby2_keywords_hash({ :a => 1 })) assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { :a => 1 })) From 2b1067bc4df35b79435865f8baceae1a1bf93f1b Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Tue, 23 Aug 2022 23:45:45 +0800 Subject: [PATCH 08/14] Handle last positional arg in with statement --- lib/mocha/parameter_matchers/last_positional_hash.rb | 2 +- test/unit/expectation_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/mocha/parameter_matchers/last_positional_hash.rb b/lib/mocha/parameter_matchers/last_positional_hash.rb index 6ba0b26b0..3e4b045e2 100644 --- a/lib/mocha/parameter_matchers/last_positional_hash.rb +++ b/lib/mocha/parameter_matchers/last_positional_hash.rb @@ -12,7 +12,7 @@ def initialize(value) def matches?(available_parameters) parameter = available_parameters.shift if RUBY_V3_PLUS - return false if Hash.ruby2_keywords_hash?(@value) && !Hash.ruby2_keywords_hash?(parameter) + return false unless Hash.ruby2_keywords_hash?(@value) == Hash.ruby2_keywords_hash?(parameter) end parameter == @value diff --git a/test/unit/expectation_test.rb b/test/unit/expectation_test.rb index 0dc29ed92..fbb29b6fb 100644 --- a/test/unit/expectation_test.rb +++ b/test/unit/expectation_test.rb @@ -83,6 +83,11 @@ def test_should_not_match_keyword_args_with_last_positional_hashes expectation = new_expectation.with(1, Hash.ruby2_keywords_hash({ :a => 1 })) assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, { :a => 1 })) end + + def test_should_not_match_last_positional_hashes_with_keyword_args + expectation = new_expectation.with(1, { :a => 1 }) + assert !expectation.match?(Invocation.new(:irrelevant, :expected_method, 1, Hash.ruby2_keywords_hash({ :a => 1 }))) + end end def test_should_allow_invocations_until_expected_invocation_count_is_one_and_actual_invocation_count_would_be_two From c1d3b2cfd209f9df7ee0daf387a37a5da6a15840 Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Thu, 25 Aug 2022 00:10:19 +0800 Subject: [PATCH 09/14] Revert stubba_example_test changes --- test/acceptance/stubba_example_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/acceptance/stubba_example_test.rb b/test/acceptance/stubba_example_test.rb index 64bc9153d..b84ab62e4 100644 --- a/test/acceptance/stubba_example_test.rb +++ b/test/acceptance/stubba_example_test.rb @@ -88,9 +88,9 @@ def should_stub_two_different_class_methods found_widgets = [Widget.new] created_widget = Widget.new Widget.expects(:find).with(:all).returns(found_widgets) - Widget.expects(:create).with({ :model => 'wombat' }).returns(created_widget) + Widget.expects(:create).with(:model => 'wombat').returns(created_widget) assert_equal found_widgets, Widget.find(:all) - assert_equal created_widget, Widget.create({ :model => 'wombat' }) + assert_equal created_widget, Widget.create(:model => 'wombat') end def should_stub_instance_method_on_any_instance_of_a_class From 96e71ad16d21606b2d6f3f32b24982ee28eda1f5 Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Thu, 25 Aug 2022 22:34:29 +0800 Subject: [PATCH 10/14] Add more ruby2_keywords --- lib/mocha/invocation.rb | 1 + lib/mocha/stubbed_method.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/mocha/invocation.rb b/lib/mocha/invocation.rb index b2030c660..6e8115720 100644 --- a/lib/mocha/invocation.rb +++ b/lib/mocha/invocation.rb @@ -18,6 +18,7 @@ def initialize(mock, method_name, *arguments, &block) @yields = [] @result = nil end + ruby2_keywords(:initialize) if RUBY_V3_PLUS def call(yield_parameters = YieldParameters.new, return_values = ReturnValues.new) yield_parameters.next_invocation.each do |yield_args| diff --git a/lib/mocha/stubbed_method.rb b/lib/mocha/stubbed_method.rb index 95b64f5fa..348b6d8f0 100644 --- a/lib/mocha/stubbed_method.rb +++ b/lib/mocha/stubbed_method.rb @@ -59,6 +59,7 @@ def define_new_method stub_method_owner.send(:define_method, method_name) do |*args, &block| self_in_scope.mock.method_missing(method_name_in_scope, *args, &block) end + stub_method_owner.send(:ruby2_keywords, method_name) if RUBY_V3_PLUS retain_original_visibility(stub_method_owner) end From cf19d366fc57a94ddd8e4d79ab585b5cddd529fd Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Fri, 26 Aug 2022 01:33:39 +0800 Subject: [PATCH 11/14] Attempt to polymorphise to_matchers --- lib/mocha/parameter_matchers/base.rb | 2 +- lib/mocha/parameter_matchers/instance_methods.rb | 12 +++++++++++- lib/mocha/parameters_matcher.rb | 6 +----- test/unit/parameter_matchers/stub_matcher.rb | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/mocha/parameter_matchers/base.rb b/lib/mocha/parameter_matchers/base.rb index 0afac05f6..430876b09 100644 --- a/lib/mocha/parameter_matchers/base.rb +++ b/lib/mocha/parameter_matchers/base.rb @@ -3,7 +3,7 @@ module ParameterMatchers # @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}. class Base # @private - def to_matcher + def to_matcher(*) self end diff --git a/lib/mocha/parameter_matchers/instance_methods.rb b/lib/mocha/parameter_matchers/instance_methods.rb index 5bb229cd4..b0870c846 100644 --- a/lib/mocha/parameter_matchers/instance_methods.rb +++ b/lib/mocha/parameter_matchers/instance_methods.rb @@ -5,7 +5,7 @@ module ParameterMatchers # @private module InstanceMethods # @private - def to_matcher + def to_matcher(*) Mocha::ParameterMatchers::Equals.new(self) end end @@ -16,3 +16,13 @@ def to_matcher class Object include Mocha::ParameterMatchers::InstanceMethods end + +# @private +class Hash + def to_matcher(last_argument: false) + return Mocha::ParameterMatchers::LastPositionalHash.new(self) if last_argument + + super + end +end + diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb index 1c62839b7..49cb598c1 100644 --- a/lib/mocha/parameters_matcher.rb +++ b/lib/mocha/parameters_matcher.rb @@ -29,11 +29,7 @@ def mocha_inspect end def matchers - if (last_parameter = @expected_parameters.last).is_a?(Hash) - @expected_parameters[0...-1].map(&:to_matcher) + [ParameterMatchers::LastPositionalHash.new(last_parameter)] - else - @expected_parameters.map(&:to_matcher) - end + @expected_parameters[0...-1].map(&:to_matcher) + [@expected_parameters.last.to_matcher(last_argument: true)] end end end diff --git a/test/unit/parameter_matchers/stub_matcher.rb b/test/unit/parameter_matchers/stub_matcher.rb index 79eb0f434..21acc85d3 100644 --- a/test/unit/parameter_matchers/stub_matcher.rb +++ b/test/unit/parameter_matchers/stub_matcher.rb @@ -16,7 +16,7 @@ def mocha_inspect "matcher(#{@matches})" end - def to_matcher + def to_matcher(*) self end end From a89cf154dcbfd226430438c46b49fb4462a67a72 Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Fri, 26 Aug 2022 01:42:50 +0800 Subject: [PATCH 12/14] Attempt to set last argument on the object itself --- lib/mocha/parameter_matchers/base.rb | 2 +- lib/mocha/parameter_matchers/instance_methods.rb | 15 ++++++++++++--- lib/mocha/parameters_matcher.rb | 3 ++- test/unit/parameter_matchers/stub_matcher.rb | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/mocha/parameter_matchers/base.rb b/lib/mocha/parameter_matchers/base.rb index 430876b09..0afac05f6 100644 --- a/lib/mocha/parameter_matchers/base.rb +++ b/lib/mocha/parameter_matchers/base.rb @@ -3,7 +3,7 @@ module ParameterMatchers # @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}. class Base # @private - def to_matcher(*) + def to_matcher self end diff --git a/lib/mocha/parameter_matchers/instance_methods.rb b/lib/mocha/parameter_matchers/instance_methods.rb index b0870c846..3cf9e0e68 100644 --- a/lib/mocha/parameter_matchers/instance_methods.rb +++ b/lib/mocha/parameter_matchers/instance_methods.rb @@ -5,9 +5,17 @@ module ParameterMatchers # @private module InstanceMethods # @private - def to_matcher(*) + def to_matcher Mocha::ParameterMatchers::Equals.new(self) end + + def mark_last_argument + @is_last_argument = true + end + + def is_last_argument? + !!@is_last_argument + end end end end @@ -19,8 +27,9 @@ class Object # @private class Hash - def to_matcher(last_argument: false) - return Mocha::ParameterMatchers::LastPositionalHash.new(self) if last_argument + # @private + def to_matcher + return Mocha::ParameterMatchers::LastPositionalHash.new(self) if is_last_argument? super end diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb index 49cb598c1..90e1c0288 100644 --- a/lib/mocha/parameters_matcher.rb +++ b/lib/mocha/parameters_matcher.rb @@ -29,7 +29,8 @@ def mocha_inspect end def matchers - @expected_parameters[0...-1].map(&:to_matcher) + [@expected_parameters.last.to_matcher(last_argument: true)] + @expected_parameters.last.mark_last_argument + @expected_parameters.map(&:to_matcher) end end end diff --git a/test/unit/parameter_matchers/stub_matcher.rb b/test/unit/parameter_matchers/stub_matcher.rb index 21acc85d3..79eb0f434 100644 --- a/test/unit/parameter_matchers/stub_matcher.rb +++ b/test/unit/parameter_matchers/stub_matcher.rb @@ -16,7 +16,7 @@ def mocha_inspect "matcher(#{@matches})" end - def to_matcher(*) + def to_matcher self end end From 41e9cc70d7007a2dc5e19aeb0b9f482714720e6d Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Fri, 26 Aug 2022 01:43:22 +0800 Subject: [PATCH 13/14] Revert "Attempt to set last argument on the object itself" This reverts commit a89cf154dcbfd226430438c46b49fb4462a67a72. --- lib/mocha/parameter_matchers/base.rb | 2 +- lib/mocha/parameter_matchers/instance_methods.rb | 15 +++------------ lib/mocha/parameters_matcher.rb | 3 +-- test/unit/parameter_matchers/stub_matcher.rb | 2 +- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/mocha/parameter_matchers/base.rb b/lib/mocha/parameter_matchers/base.rb index 0afac05f6..430876b09 100644 --- a/lib/mocha/parameter_matchers/base.rb +++ b/lib/mocha/parameter_matchers/base.rb @@ -3,7 +3,7 @@ module ParameterMatchers # @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}. class Base # @private - def to_matcher + def to_matcher(*) self end diff --git a/lib/mocha/parameter_matchers/instance_methods.rb b/lib/mocha/parameter_matchers/instance_methods.rb index 3cf9e0e68..b0870c846 100644 --- a/lib/mocha/parameter_matchers/instance_methods.rb +++ b/lib/mocha/parameter_matchers/instance_methods.rb @@ -5,17 +5,9 @@ module ParameterMatchers # @private module InstanceMethods # @private - def to_matcher + def to_matcher(*) Mocha::ParameterMatchers::Equals.new(self) end - - def mark_last_argument - @is_last_argument = true - end - - def is_last_argument? - !!@is_last_argument - end end end end @@ -27,9 +19,8 @@ class Object # @private class Hash - # @private - def to_matcher - return Mocha::ParameterMatchers::LastPositionalHash.new(self) if is_last_argument? + def to_matcher(last_argument: false) + return Mocha::ParameterMatchers::LastPositionalHash.new(self) if last_argument super end diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb index 90e1c0288..49cb598c1 100644 --- a/lib/mocha/parameters_matcher.rb +++ b/lib/mocha/parameters_matcher.rb @@ -29,8 +29,7 @@ def mocha_inspect end def matchers - @expected_parameters.last.mark_last_argument - @expected_parameters.map(&:to_matcher) + @expected_parameters[0...-1].map(&:to_matcher) + [@expected_parameters.last.to_matcher(last_argument: true)] end end end diff --git a/test/unit/parameter_matchers/stub_matcher.rb b/test/unit/parameter_matchers/stub_matcher.rb index 79eb0f434..21acc85d3 100644 --- a/test/unit/parameter_matchers/stub_matcher.rb +++ b/test/unit/parameter_matchers/stub_matcher.rb @@ -16,7 +16,7 @@ def mocha_inspect "matcher(#{@matches})" end - def to_matcher + def to_matcher(*) self end end From 44e7d4161678fcdc766bb36210611058f9298d6f Mon Sep 17 00:00:00 2001 From: Nicholas Koh Date: Fri, 26 Aug 2022 01:44:10 +0800 Subject: [PATCH 14/14] Revert "Attempt to polymorphise to_matchers" This reverts commit cf19d366fc57a94ddd8e4d79ab585b5cddd529fd. --- lib/mocha/parameter_matchers/base.rb | 2 +- lib/mocha/parameter_matchers/instance_methods.rb | 12 +----------- lib/mocha/parameters_matcher.rb | 6 +++++- test/unit/parameter_matchers/stub_matcher.rb | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/mocha/parameter_matchers/base.rb b/lib/mocha/parameter_matchers/base.rb index 430876b09..0afac05f6 100644 --- a/lib/mocha/parameter_matchers/base.rb +++ b/lib/mocha/parameter_matchers/base.rb @@ -3,7 +3,7 @@ module ParameterMatchers # @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}. class Base # @private - def to_matcher(*) + def to_matcher self end diff --git a/lib/mocha/parameter_matchers/instance_methods.rb b/lib/mocha/parameter_matchers/instance_methods.rb index b0870c846..5bb229cd4 100644 --- a/lib/mocha/parameter_matchers/instance_methods.rb +++ b/lib/mocha/parameter_matchers/instance_methods.rb @@ -5,7 +5,7 @@ module ParameterMatchers # @private module InstanceMethods # @private - def to_matcher(*) + def to_matcher Mocha::ParameterMatchers::Equals.new(self) end end @@ -16,13 +16,3 @@ def to_matcher(*) class Object include Mocha::ParameterMatchers::InstanceMethods end - -# @private -class Hash - def to_matcher(last_argument: false) - return Mocha::ParameterMatchers::LastPositionalHash.new(self) if last_argument - - super - end -end - diff --git a/lib/mocha/parameters_matcher.rb b/lib/mocha/parameters_matcher.rb index 49cb598c1..1c62839b7 100644 --- a/lib/mocha/parameters_matcher.rb +++ b/lib/mocha/parameters_matcher.rb @@ -29,7 +29,11 @@ def mocha_inspect end def matchers - @expected_parameters[0...-1].map(&:to_matcher) + [@expected_parameters.last.to_matcher(last_argument: true)] + if (last_parameter = @expected_parameters.last).is_a?(Hash) + @expected_parameters[0...-1].map(&:to_matcher) + [ParameterMatchers::LastPositionalHash.new(last_parameter)] + else + @expected_parameters.map(&:to_matcher) + end end end end diff --git a/test/unit/parameter_matchers/stub_matcher.rb b/test/unit/parameter_matchers/stub_matcher.rb index 21acc85d3..79eb0f434 100644 --- a/test/unit/parameter_matchers/stub_matcher.rb +++ b/test/unit/parameter_matchers/stub_matcher.rb @@ -16,7 +16,7 @@ def mocha_inspect "matcher(#{@matches})" end - def to_matcher(*) + def to_matcher self end end