diff --git a/Changelog.md b/Changelog.md index c1b836fba..e1dd75f00 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,10 @@ ### Development [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.12.0...main) +Bug Fixes: + +* Fix keyword arguments delegation for `chain` in custom matchers. (Tyler Rick, #1374) + ### 3.12.0 / 2022-10-26 [Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.11.1...v3.12.0) diff --git a/lib/rspec/matchers/dsl.rb b/lib/rspec/matchers/dsl.rb index 9c6a48bf0..824309a57 100644 --- a/lib/rspec/matchers/dsl.rb +++ b/lib/rspec/matchers/dsl.rb @@ -306,6 +306,7 @@ def chain(method_name, *attr_names, &definition) @chained_method_clauses.push([method_name, args]) self end + ruby2_keywords method_name if respond_to?(:ruby2_keywords, true) end def assign_attributes(attr_names) diff --git a/spec/rspec/matchers/dsl_spec.rb b/spec/rspec/matchers/dsl_spec.rb index c085cdce8..d4433cc14 100644 --- a/spec/rspec/matchers/dsl_spec.rb +++ b/spec/rspec/matchers/dsl_spec.rb @@ -354,8 +354,17 @@ def new_matcher(name, *expected, &block) (to_match <= five) || greater_than_ceiling(to_match) && not_divisible_by_divisor?(to_match) end - chain :and_smaller_than do |ceiling| - @ceiling = ceiling + if RSpec::Support::RubyFeatures.kw_args_supported? + binding.eval(<<-CODE, __FILE__, __LINE__) + chain :and_smaller_than do |ceiling, inclusive: false| + @ceiling = ceiling + @ceiling_inclusive = inclusive + end + CODE + else + chain :and_smaller_than do |ceiling| + @ceiling = ceiling + end end chain :and_divisible_by do |divisor| @@ -364,8 +373,18 @@ def new_matcher(name, *expected, &block) private - def smaller_than_ceiling?(to_match) - to_match < @ceiling + if RSpec::Support::RubyFeatures.kw_args_supported? + def smaller_than_ceiling?(to_match) + if @ceiling_inclusive + to_match <= @ceiling + else + to_match < @ceiling + end + end + else + def smaller_than_ceiling?(to_match) + to_match < @ceiling + end end def greater_than_ceiling(to_match) @@ -373,11 +392,11 @@ def greater_than_ceiling(to_match) end def divisible_by_divisor?(to_match) - @divisor % to_match == 0 + to_match % @divisor == 0 end def not_divisible_by_divisor?(to_match) - @divisor % to_match != 0 + to_match % @divisor != 0 end end end @@ -397,7 +416,7 @@ def not_divisible_by_divisor?(to_match) expect { expect(8).to match }.to fail_with 'expected 8 to be bigger than 5' end - it "provides a default negative expectation failure message that does not include the any of the chained matchers's descriptions" do + it "provides a default negative expectation failure message that does not include any of the chained matchers' descriptions" do expect { expect(9).to_not match }.to fail_with 'expected 9 not to be bigger than 5' end end @@ -421,6 +440,15 @@ def not_divisible_by_divisor?(to_match) expect { expect(21).to_not matcher.and_smaller_than(29).and_divisible_by(3) }.to \ fail_with 'expected 21 not to be bigger than 5 and smaller than 29 and divisible by 3' end + + if RSpec::Support::RubyFeatures.kw_args_supported? + binding.eval(<<-CODE, __FILE__, __LINE__) + it "allows passing keyword args to chain block" do + match = matcher.and_smaller_than(10, inclusive: true).and_divisible_by(1) + expect(10).to match + end + CODE + end end it 'only decides if to include the chained clauses at the time description is invoked' do