Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit fefabc4

Browse files
authored
Merge pull request #1394 from mame/with-for-ruby3
Make `with` support Ruby 3 keywords
2 parents 7013c5f + 865ea3a commit fefabc4

File tree

6 files changed

+115
-44
lines changed

6 files changed

+115
-44
lines changed

lib/rspec/mocks/argument_list_matcher.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,30 @@ def initialize(*expected_args)
4646
@expected_args = expected_args
4747
ensure_expected_args_valid!
4848
end
49+
ruby2_keywords :initialize if Module.private_method_defined?(:ruby2_keywords)
4950

5051
# @api public
51-
# @param [Array] args
52+
# @param [Array] actual_args
5253
#
5354
# Matches each element in the `expected_args` against the element in the same
5455
# position of the arguments passed to `new`.
5556
#
5657
# @see #initialize
57-
def args_match?(*args)
58-
Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args)
58+
def args_match?(*actual_args)
59+
expected_args = resolve_expected_args_based_on(actual_args)
60+
61+
return false if expected_args.size != actual_args.size
62+
63+
if RUBY_VERSION >= "3"
64+
# if both arguments end with Hashes, and if one is a keyword hash and the other is not, they don't match
65+
if Hash === expected_args.last && Hash === actual_args.last
66+
if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last)
67+
return false
68+
end
69+
end
70+
end
71+
72+
Support::FuzzyMatcher.values_match?(expected_args, actual_args)
5973
end
6074

6175
# @private

lib/rspec/mocks/matchers/receive.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def setup_any_instance_allowance(subject, &block)
6262
@recorded_customizations << ExpectationCustomization.new(method, args, block)
6363
self
6464
end
65+
ruby2_keywords(method) if Module.private_method_defined?(:ruby2_keywords)
6566
end
6667

6768
private

lib/rspec/mocks/message_expectation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ def with(*args, &block)
364364
@argument_list_matcher = ArgumentListMatcher.new(*args)
365365
self
366366
end
367+
ruby2_keywords(:with) if Module.private_method_defined?(:ruby2_keywords)
367368

368369
# Expect messages to be received in a specific order.
369370
#

spec/rspec/mocks/argument_matchers_spec.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -381,16 +381,26 @@ def ==(other)
381381
a_double.random_call(:a => "a", :b => "b")
382382
end
383383

384-
it "matches against a hash submitted by reference and received by value" do
384+
it "matches against a hash submitted as keyword arguments a and received as a positional argument (in both Ruby 2 and Ruby 3)" do
385385
opts = {:a => "a", :b => "b"}
386386
expect(a_double).to receive(:random_call).with(opts)
387387
a_double.random_call(:a => "a", :b => "b")
388388
end
389389

390-
it "matches against a hash submitted by value and received by reference" do
391-
opts = {:a => "a", :b => "b"}
392-
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
393-
a_double.random_call(opts)
390+
if RUBY_VERSION >= "3"
391+
it "fails to matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 3.0 or later", :reset => true do
392+
opts = {:a => "a", :b => "b"}
393+
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
394+
expect do
395+
a_double.random_call(opts)
396+
end.to fail_with(/expected: \(\{(:a=>\"a\", :b=>\"b\"|:b=>\"b\", :a=>\"a\")\}\)/)
397+
end
398+
else
399+
it "matches against a hash submitted as a positional argument and received as keyword arguments in Ruby 2.7 or before" do
400+
opts = {:a => "a", :b => "b"}
401+
expect(a_double).to receive(:random_call).with(:a => "a", :b => "b")
402+
a_double.random_call(opts)
403+
end
394404
end
395405

396406
it "fails for a hash w/ wrong values", :reset => true do

spec/rspec/mocks/partial_double_spec.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,15 @@ def call(name)
8181
expect(object.foobar(:key => "value")).to equal(1)
8282
end
8383

84-
it "can accept an inner hash as a message argument" do
85-
hash = {:a => {:key => "value"}}
86-
expect(object).to receive(:foobar).with(:key => "value").and_return(1)
87-
expect(object.foobar(hash[:a])).to equal(1)
84+
if RSpec::Support::RubyFeatures.required_kw_args_supported?
85+
# Use eval to avoid syntax error on 1.8 and 1.9
86+
binding.eval(<<-CODE, __FILE__, __LINE__)
87+
it "can accept an inner hash as a message argument" do
88+
hash = {:a => {:key => "value"}}
89+
expect(object).to receive(:foobar).with(:key => "value").and_return(1)
90+
expect(object.foobar(**hash[:a])).to equal(1)
91+
end
92+
CODE
8893
end
8994

9095
it "can create a positive message expectation" do

spec/rspec/mocks/should_syntax_spec.rb

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -463,39 +463,79 @@ def use_rspec_mocks
463463
after(:all) { RSpec::Mocks.configuration.syntax = orig_syntax }
464464
before { RSpec::Mocks.configuration.reset_syntaxes_to_default }
465465

466-
let(:expected_arguments) {
467-
[
468-
/Using.*without explicitly enabling/,
466+
if RSpec::Support::RubyFeatures.required_kw_args_supported?
467+
let(:expected_arguments) {
468+
[
469+
/Using.*without explicitly enabling/,
470+
]
471+
}
472+
let(:expected_keywords) {
469473
{:replacement => "the new `:expect` syntax or explicitly enable `:should`"}
470-
]
471-
}
472-
473-
it "it warns about should once, regardless of how many times it is called" do
474-
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
475-
o = Object.new
476-
o2 = Object.new
477-
o.should_receive(:bees)
478-
o2.should_receive(:bees)
479-
480-
o.bees
481-
o2.bees
482-
end
483-
484-
it "warns about should not once, regardless of how many times it is called" do
485-
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
486-
o = Object.new
487-
o2 = Object.new
488-
o.should_not_receive(:bees)
489-
o2.should_not_receive(:bees)
490-
end
491-
492-
it "warns about stubbing once, regardless of how many times it is called" do
493-
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
494-
o = Object.new
495-
o2 = Object.new
496-
497-
o.stub(:faces)
498-
o2.stub(:faces)
474+
}
475+
it "it warns about should once, regardless of how many times it is called" do
476+
# Use eval to avoid syntax error on 1.8 and 1.9
477+
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
478+
o = Object.new
479+
o2 = Object.new
480+
o.should_receive(:bees)
481+
o2.should_receive(:bees)
482+
483+
o.bees
484+
o2.bees
485+
end
486+
487+
it "warns about should not once, regardless of how many times it is called" do
488+
# Use eval to avoid syntax error on 1.8 and 1.9
489+
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
490+
o = Object.new
491+
o2 = Object.new
492+
o.should_not_receive(:bees)
493+
o2.should_not_receive(:bees)
494+
end
495+
496+
it "warns about stubbing once, regardless of how many times it is called" do
497+
# Use eval to avoid syntax error on 1.8 and 1.9
498+
eval("expect(RSpec).to receive(:deprecate).with(*expected_arguments, **expected_keywords)")
499+
o = Object.new
500+
o2 = Object.new
501+
502+
o.stub(:faces)
503+
o2.stub(:faces)
504+
end
505+
else
506+
let(:expected_arguments) {
507+
[
508+
/Using.*without explicitly enabling/,
509+
{:replacement => "the new `:expect` syntax or explicitly enable `:should`"}
510+
]
511+
}
512+
it "it warns about should once, regardless of how many times it is called" do
513+
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
514+
o = Object.new
515+
o2 = Object.new
516+
o.should_receive(:bees)
517+
o2.should_receive(:bees)
518+
519+
o.bees
520+
o2.bees
521+
end
522+
523+
it "warns about should not once, regardless of how many times it is called" do
524+
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
525+
o = Object.new
526+
o2 = Object.new
527+
o.should_not_receive(:bees)
528+
o2.should_not_receive(:bees)
529+
end
530+
531+
it "warns about stubbing once, regardless of how many times it is called" do
532+
expect(RSpec).to receive(:deprecate).with(*expected_arguments)
533+
o = Object.new
534+
o2 = Object.new
535+
536+
o.stub(:faces)
537+
o2.stub(:faces)
538+
end
499539
end
500540

501541
it "warns about unstubbing once, regardless of how many times it is called" do

0 commit comments

Comments
 (0)