From ec244b00cac5ea2941ca0a1dffc7930322af03fb Mon Sep 17 00:00:00 2001 From: Josh Cheek Date: Wed, 14 Jul 2021 19:18:43 -0500 Subject: [PATCH 1/5] Fix keyword arguments in dynamic matchers Ruby 3 changed the way keyword arguments work. Currently, to forward arguments, the only way that works in Ruby 2.6, 2.7, and 3.0, is to call `ruby2_keywords :method_name` for the method that receives the arguments. This will cause Ruby to remember that the final hash was created by keyword arguments, and then when expanding out the arguments later, eg [here](https://github.com/rspec/rspec-expectations/blob/43bf64b01f8356979ffbc373b2e81d2ab1389b29/lib/rspec/matchers/built_in/has.rb#L67), it will see that the last element was created by keywords, and it will pass that into the keywords area of a method infocation. If we don't do this, it will consider it an ordinal argument and not line it up with the keywords. This typically causes a "wrong number of arguments" error, because it's passing an extra ordinal argument. Here is another example from within RSpec, where they have arrived at this solution for the same problem: https://github.com/rspec/rspec-expectations/blob/43bf64b01f8356979ffbc373b2e81d2ab1389b29/lib/rspec/matchers/dsl.rb#L538-L540 --- lib/rspec/rails/example/controller_example_group.rb | 1 + spec/rspec/rails/example/controller_example_group_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/rspec/rails/example/controller_example_group.rb b/lib/rspec/rails/example/controller_example_group.rb index 3150f3d469..636a194a11 100644 --- a/lib/rspec/rails/example/controller_example_group.rb +++ b/lib/rspec/rails/example/controller_example_group.rb @@ -176,6 +176,7 @@ def method_missing(method, *args, &block) super end end + ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true) included do subject { controller } diff --git a/spec/rspec/rails/example/controller_example_group_spec.rb b/spec/rspec/rails/example/controller_example_group_spec.rb index 56b53049d1..9e785a3902 100644 --- a/spec/rspec/rails/example/controller_example_group_spec.rb +++ b/spec/rspec/rails/example/controller_example_group_spec.rb @@ -19,6 +19,13 @@ def group_for(klass) expect(group.included_modules).to include(RSpec::Rails::Matchers::RoutingMatchers) end + it "handles dynamic matchers with keywords (big change from ruby 2 to ruby 3)" do + example = group.new + example.define_singleton_method(:has_val?) { |val:| val == 1 } + expect(example).to example.have_val(val: 1) + expect(example).to_not example.have_val(val: 2) + end + context "with implicit subject" do it "uses the controller as the subject" do controller = double('controller') From e4b3953561ae5193459685ec009e91ae3705debb Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Thu, 15 Jul 2021 22:05:13 +0300 Subject: [PATCH 2/5] Update controller_example_group_spec.rb --- spec/rspec/rails/example/controller_example_group_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/rails/example/controller_example_group_spec.rb b/spec/rspec/rails/example/controller_example_group_spec.rb index 9e785a3902..e1652f7db2 100644 --- a/spec/rspec/rails/example/controller_example_group_spec.rb +++ b/spec/rspec/rails/example/controller_example_group_spec.rb @@ -19,7 +19,7 @@ def group_for(klass) expect(group.included_modules).to include(RSpec::Rails::Matchers::RoutingMatchers) end - it "handles dynamic matchers with keywords (big change from ruby 2 to ruby 3)" do + it "handles dynamic matchers with keywords" do example = group.new example.define_singleton_method(:has_val?) { |val:| val == 1 } expect(example).to example.have_val(val: 1) From e2541f0240429c854fbc0c436cc4ec0c9dd1c35c Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Tue, 27 Jul 2021 23:07:12 +0300 Subject: [PATCH 3/5] Update spec/rspec/rails/example/controller_example_group_spec.rb Co-authored-by: Jon Rowe --- spec/rspec/rails/example/controller_example_group_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rspec/rails/example/controller_example_group_spec.rb b/spec/rspec/rails/example/controller_example_group_spec.rb index e1652f7db2..50569e2a8e 100644 --- a/spec/rspec/rails/example/controller_example_group_spec.rb +++ b/spec/rspec/rails/example/controller_example_group_spec.rb @@ -19,7 +19,7 @@ def group_for(klass) expect(group.included_modules).to include(RSpec::Rails::Matchers::RoutingMatchers) end - it "handles dynamic matchers with keywords" do + it "handles methods invoked via `method_missing` that use keywords" do example = group.new example.define_singleton_method(:has_val?) { |val:| val == 1 } expect(example).to example.have_val(val: 1) From cb7e2d4626a12529e288c47589ae9a02fdefd20a Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Tue, 27 Jul 2021 23:07:23 +0300 Subject: [PATCH 4/5] Update spec/rspec/rails/example/controller_example_group_spec.rb Co-authored-by: Jon Rowe --- spec/rspec/rails/example/controller_example_group_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/rspec/rails/example/controller_example_group_spec.rb b/spec/rspec/rails/example/controller_example_group_spec.rb index 50569e2a8e..94c858faba 100644 --- a/spec/rspec/rails/example/controller_example_group_spec.rb +++ b/spec/rspec/rails/example/controller_example_group_spec.rb @@ -21,9 +21,8 @@ def group_for(klass) it "handles methods invoked via `method_missing` that use keywords" do example = group.new - example.define_singleton_method(:has_val?) { |val:| val == 1 } - expect(example).to example.have_val(val: 1) - expect(example).to_not example.have_val(val: 2) + example.define_singleton_method(:keyword_value) { |value:| value } + expect(example.keyword_value(value: 42)).to eq 42 end context "with implicit subject" do From 0b74226259f9eeb90eedf0c97d43666787cec334 Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Tue, 27 Jul 2021 23:19:45 +0300 Subject: [PATCH 5/5] Revert the previous spec change --- spec/rspec/rails/example/controller_example_group_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/rspec/rails/example/controller_example_group_spec.rb b/spec/rspec/rails/example/controller_example_group_spec.rb index 94c858faba..50569e2a8e 100644 --- a/spec/rspec/rails/example/controller_example_group_spec.rb +++ b/spec/rspec/rails/example/controller_example_group_spec.rb @@ -21,8 +21,9 @@ def group_for(klass) it "handles methods invoked via `method_missing` that use keywords" do example = group.new - example.define_singleton_method(:keyword_value) { |value:| value } - expect(example.keyword_value(value: 42)).to eq 42 + example.define_singleton_method(:has_val?) { |val:| val == 1 } + expect(example).to example.have_val(val: 1) + expect(example).to_not example.have_val(val: 2) end context "with implicit subject" do