Skip to content

Commit

Permalink
Allow string keys for method kwarg splats
Browse files Browse the repository at this point in the history
  • Loading branch information
malcolmohare committed Jan 15, 2024
1 parent 8a25122 commit bd26d93
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 9 deletions.
6 changes: 3 additions & 3 deletions lib/rspec/support/method_signature_verifier.rb
Expand Up @@ -84,7 +84,7 @@ def invalid_kw_args_from(given_kw_args)
def has_kw_args_in?(args)
Hash === args.last &&
could_contain_kw_args?(args) &&
(args.last.empty? || args.last.keys.any? { |x| x.is_a?(Symbol) })
(args.last.empty? || args.last.keys.any? { |x| x.is_a?(Symbol) || x.is_a?(String) })
end

# Without considering what the last arg is, could it
Expand Down Expand Up @@ -365,12 +365,12 @@ def unlimited_args?
def split_args(*args)
kw_args = if @signature.has_kw_args_in?(args)
last = args.pop
non_kw_args = last.reject { |k, _| k.is_a?(Symbol) }
non_kw_args = last.reject { |k, _| k.is_a?(Symbol) || k.is_a?(String) }
if non_kw_args.empty?
last.keys
else
args << non_kw_args
last.select { |k, _| k.is_a?(Symbol) }.keys
last.select { |k, _| k.is_a?(Symbol) || k.is_a(String) }.keys
end
else
[]
Expand Down
54 changes: 48 additions & 6 deletions spec/rspec/support/method_signature_verifier_spec.rb
Expand Up @@ -384,17 +384,21 @@ def arity_kw(x, y = {}, z:2); end
expect(valid?(nil, :a => 1)).to eq(false)
end

it 'treats symbols as keyword arguments and the rest as optional argument' do
expect(valid?(nil, 'a' => 1)).to eq(true)
expect(valid?(nil, 'a' => 1, :z => 3)).to eq(true)
expect(valid?(nil, 'a' => 1, :b => 3)).to eq(false)
expect(valid?(nil, 'a' => 1, :b => 2, :z => 3)).to eq(false)
it 'treats symbols as keyword arguments' do
expect(valid?(nil, :z => 3)).to eq(true)
expect(valid?(nil, :b => 3)).to eq(false)
expect(valid?(nil, :b => 2, :z => 3)).to eq(false)
end

it 'treats string keys as invalid keyword arguments' do
expect(valid?(nil, 'a' => 1)).to eq(false)
expect(valid?(nil, 'a' => 1, :z => 3)).to eq(false)
end

it 'mentions the invalid keyword args in the error', :pending => RSpec::Support::Ruby.jruby? && !RSpec::Support::Ruby.jruby_9000? do
expect(error_for(1, 2, :a => 0)).to eq("Invalid keyword arguments provided: a")
expect(error_for(1, :a => 0)).to eq("Invalid keyword arguments provided: a")
expect(error_for(1, 'a' => 0, :b => 0)).to eq("Invalid keyword arguments provided: b")
expect(error_for(1, 'a' => 0, :b => 0)).to eq("Invalid keyword arguments provided: a, b")
end

it 'describes invalid arity precisely' do
Expand Down Expand Up @@ -656,6 +660,41 @@ def arity_required_kw_splat(w, *x, y:, z:, a: 'default'); end
end
end

describe 'a method with only a keyword arg splat' do
eval <<-RUBY
def arity_kw_arg_splat(**rest); end
RUBY

let(:test_method) { method(:arity_kw_arg_splat) }

it 'allows undeclared keyword args' do
expect(valid?(:x => 1)).to eq(true)
expect(valid?(:x => 1, 'y' => 2)).to eq(true)
end

it 'mentions the required kw args and keyword splat in the description' do
expect(signature_description).to \
eq("any additional keyword args")
end

describe 'with an expectation object' do
it 'allows zero non-kw args' do
expect(validate_expectation 0).to eq(true)
expect(validate_expectation 1).to eq(false)
expect(validate_expectation 0, 0).to eq(true)
expect(validate_expectation 0, 1).to eq(false)
end

it 'does not match unlimited arguments' do
expect(validate_expectation :unlimited_args).to eq(false)
end

it 'matches arbitrary keywords' do
expect(validate_expectation :arbitrary_kw_args).to eq(true)
end
end
end

describe 'a method with required keyword arguments and a keyword arg splat' do
eval <<-RUBY
def arity_kw_arg_splat(x:, **rest); end
Expand All @@ -666,6 +705,7 @@ def arity_kw_arg_splat(x:, **rest); end
it 'allows extra undeclared keyword args' do
expect(valid?(:x => 1)).to eq(true)
expect(valid?(:x => 1, :y => 2)).to eq(true)
expect(valid?(:x => 1, :y => 2, 'z' => 3)).to eq(true)
end

it 'mentions missing required keyword args in the error' do
Expand Down Expand Up @@ -730,7 +770,9 @@ def arity_kw_arg_splat(x, **rest); end
it 'allows a single arg and any number of keyword args' do
expect(valid?(nil)).to eq(true)
expect(valid?(nil, :x => 1)).to eq(true)
expect(valid?(nil, 'x' => 1)).to eq(true)
expect(valid?(nil, :x => 1, :y => 2)).to eq(true)
expect(valid?(nil, :x => 1, :y => 2, 'z' => 3)).to eq(true)
expect(valid?(:x => 1)).to eq(true)
expect(valid?(nil, {})).to eq(true)

Expand Down

0 comments on commit bd26d93

Please sign in to comment.