summaryrefslogtreecommitdiff
path: root/spec/ruby/language/keyword_arguments_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/language/keyword_arguments_spec.rb')
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb532
1 files changed, 283 insertions, 249 deletions
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
index 0c72f59d38..ffb5b1fab0 100644
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ b/spec/ruby/language/keyword_arguments_spec.rb
@@ -1,298 +1,353 @@
require_relative '../spec_helper'
-ruby_version_is "3.0" do
- describe "Keyword arguments" do
- def target(*args, **kwargs)
+describe "Keyword arguments" do
+ def target(*args, **kwargs)
+ [args, kwargs]
+ end
+
+ it "are separated from positional arguments" do
+ def m(*args, **kwargs)
[args, kwargs]
end
- it "are separated from positional arguments" do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ it "when the receiving method has not keyword parameters it treats kwargs as positional" do
+ def m(*a)
+ a
end
- it "when the receiving method has not keyword parameters it treats kwargs as positional" do
+ m(a: 1).should == [{a: 1}]
+ m({a: 1}).should == [{a: 1}]
+ end
+
+ context "empty kwargs are treated as if they were not passed" do
+ it "when calling a method" do
def m(*a)
a
end
- m(a: 1).should == [{a: 1}]
- m({a: 1}).should == [{a: 1}]
+ empty = {}
+ m(**empty).should == []
+ m(empty).should == [{}]
end
- context "empty kwargs are treated as if they were not passed" do
- it "when calling a method" do
- def m(*a)
- a
- end
-
- empty = {}
- m(**empty).should == []
- m(empty).should == [{}]
+ it "when yielding to a block" do
+ def y(*args, **kwargs)
+ yield(*args, **kwargs)
end
- it "when yielding to a block" do
- def y(*args, **kwargs)
- yield(*args, **kwargs)
- end
+ empty = {}
+ y(**empty) { |*a| a }.should == []
+ y(empty) { |*a| a }.should == [{}]
+ end
+ end
- empty = {}
- y(**empty) { |*a| a }.should == []
- y(empty) { |*a| a }.should == [{}]
- end
+ it "extra keywords are not allowed without **kwrest" do
+ def m(*a, kw:)
+ a
end
- it "extra keywords are not allowed without **kwrest" do
- def m(*a, kw:)
- a
- end
+ m(kw: 1).should == []
+ -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
+ -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
+ -> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
+ end
- m(kw: 1).should == []
- -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
- -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
+ it "raises ArgumentError exception when required keyword argument is not passed" do
+ def m(a:, b:, c:)
+ [a, b, c]
end
- it "handle * and ** at the same call site" do
- def m(*a)
- a
- end
+ -> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
+ -> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
+ end
- m(*[], **{}).should == []
- m(*[], 42, **{}).should == [42]
+ it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
+ def m(a:)
+ a
end
- context "**" do
- it "does not copy a non-empty Hash for a method taking (*args)" do
+ -> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
+ end
+
+ it "handle * and ** at the same call site" do
+ def m(*a)
+ a
+ end
+
+ m(*[], **{}).should == []
+ m(*[], 42, **{}).should == [42]
+ end
+
+ context "**" do
+ ruby_version_is "3.3" do
+ it "copies a non-empty Hash for a method taking (*args)" do
def m(*args)
args[0]
end
h = {a: 1}
- m(**h).should.equal?(h)
+ m(**h).should_not.equal?(h)
+ h.should == {a: 1}
end
+ end
- it "copies the given Hash for a method taking (**kwargs)" do
- def m(**kw)
- kw
- end
+ it "copies the given Hash for a method taking (**kwargs)" do
+ def m(**kw)
+ kw
+ end
- empty = {}
- m(**empty).should == empty
- m(**empty).should_not.equal?(empty)
+ empty = {}
+ m(**empty).should == empty
+ m(**empty).should_not.equal?(empty)
- h = {a: 1}
- m(**h).should == h
- m(**h).should_not.equal?(h)
- end
+ h = {a: 1}
+ m(**h).should == h
+ m(**h).should_not.equal?(h)
end
+ end
- context "delegation" do
- it "works with (*args, **kwargs)" do
- def m(*args, **kwargs)
- target(*args, **kwargs)
- end
+ context "delegation" do
+ it "works with (*args, **kwargs)" do
+ def m(*args, **kwargs)
+ target(*args, **kwargs)
+ end
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
+
+ it "works with proc { |*args, **kwargs| }" do
+ m = proc do |*args, **kwargs|
+ target(*args, **kwargs)
end
- it "works with proc { |*args, **kwargs| }" do
- m = proc do |*args, **kwargs|
- target(*args, **kwargs)
- end
+ empty = {}
+ m.(**empty).should == [[], {}]
+ m.(empty).should == [[{}], {}]
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
+ m.(a: 1).should == [[], {a: 1}]
+ m.({a: 1}).should == [[{a: 1}], {}]
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
+ # no autosplatting for |*args, **kwargs|
+ m.([1, 2]).should == [[[1, 2]], {}]
+ end
- # no autosplatting for |*args, **kwargs|
- m.([1, 2]).should == [[[1, 2]], {}]
+ it "works with -> (*args, **kwargs) {}" do
+ m = -> *args, **kwargs do
+ target(*args, **kwargs)
end
- it "works with -> (*args, **kwargs) {}" do
- m = -> *args, **kwargs do
- target(*args, **kwargs)
- end
+ empty = {}
+ m.(**empty).should == [[], {}]
+ m.(empty).should == [[{}], {}]
- empty = {}
- m.(**empty).should == [[], {}]
- m.(empty).should == [[{}], {}]
+ m.(a: 1).should == [[], {a: 1}]
+ m.({a: 1}).should == [[{a: 1}], {}]
+ end
- m.(a: 1).should == [[], {a: 1}]
- m.({a: 1}).should == [[{a: 1}], {}]
+ it "works with (...)" do
+ instance_eval <<~DEF
+ def m(...)
+ target(...)
end
+ DEF
- it "works with (...)" do
- instance_eval <<~DEF
- def m(...)
- target(...)
- end
- DEF
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ it "works with call(*ruby2_keyword_args)" do
+ class << self
+ ruby2_keywords def m(*args)
+ target(*args)
+ end
end
- it "works with call(*ruby2_keyword_args)" do
- class << self
- ruby2_keywords def m(*args)
- target(*args)
- end
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with super(*ruby2_keyword_args)" do
+ parent = Class.new do
+ def m(*args, **kwargs)
+ [args, kwargs]
+ end
end
- it "works with super(*ruby2_keyword_args)" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ child = Class.new(parent) do
+ ruby2_keywords def m(*args)
+ super(*args)
end
+ end
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super(*args)
- end
- end
+ obj = child.new
- obj = child.new
+ empty = {}
+ obj.m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(a: 1).should == [[], {a: 1}]
+ obj.m({a: 1}).should == [[{a: 1}], {}]
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ obj.m(**kw).should == [[], {a: 1}]
+ obj.m(**kw)[1].should == kw
+ obj.m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
+ obj.m(kw).should == [[{a: 1}], {}]
+ obj.m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with zsuper" do
+ parent = Class.new do
+ def m(*args, **kwargs)
+ [args, kwargs]
+ end
end
- it "works with zsuper" do
- parent = Class.new do
- def m(*args, **kwargs)
- [args, kwargs]
- end
+ child = Class.new(parent) do
+ ruby2_keywords def m(*args)
+ super
end
+ end
- child = Class.new(parent) do
- ruby2_keywords def m(*args)
- super
- end
- end
+ obj = child.new
- obj = child.new
+ empty = {}
+ obj.m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- empty = {}
- obj.m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- obj.m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ obj.m(a: 1).should == [[], {a: 1}]
+ obj.m({a: 1}).should == [[{a: 1}], {}]
- obj.m(a: 1).should == [[], {a: 1}]
- obj.m({a: 1}).should == [[{a: 1}], {}]
+ kw = {a: 1}
- kw = {a: 1}
+ obj.m(**kw).should == [[], {a: 1}]
+ obj.m(**kw)[1].should == kw
+ obj.m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
- obj.m(**kw).should == [[], {a: 1}]
- obj.m(**kw)[1].should == kw
- obj.m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
+ obj.m(kw).should == [[{a: 1}], {}]
+ obj.m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
- obj.m(kw).should == [[{a: 1}], {}]
- obj.m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ it "works with yield(*ruby2_keyword_args)" do
+ class << self
+ def y(args)
+ yield(*args)
+ end
+
+ ruby2_keywords def m(*outer_args)
+ y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
+ end
end
- it "works with yield(*ruby2_keyword_args)" do
- class << self
- def y(args)
- yield(*args)
- end
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- ruby2_keywords def m(*outer_args)
- y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
- end
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
+
+ kw = {a: 1}
+
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ end
+
+ it "does not work with (*args)" do
+ class << self
+ def m(*args)
+ target(*args)
end
+ end
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ empty = {}
+ m(**empty).should == [[], {}]
+ m(empty).should == [[{}], {}]
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[{a: 1}], {}]
+ m({a: 1}).should == [[{a: 1}], {}]
+ end
- kw = {a: 1}
+ ruby_version_is "3.1" do
+ describe "omitted values" do
+ it "accepts short notation 'key' for 'key: value' syntax" do
+ def m(a:, b:)
+ [a, b]
+ end
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ a = 1
+ b = 2
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
+ eval('m(a:, b:).should == [1, 2]')
+ end
end
+ end
- it "does not work with (*args)" do
+ ruby_version_is "3.2" do
+ it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
class << self
- def m(*args)
+ def n(*args) # Note the missing ruby2_keywords here
target(*args)
end
+
+ ruby2_keywords def m(*args)
+ n(*args)
+ end
end
empty = {}
@@ -302,62 +357,41 @@ ruby_version_is "3.0" do
m(a: 1).should == [[{a: 1}], {}]
m({a: 1}).should == [[{a: 1}], {}]
end
+ end
- ruby_version_is "3.2" do
- it "does not work with call(*ruby2_keyword_args) with missing ruby2_keywords in between" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
-
- ruby2_keywords def m(*args)
- n(*args)
- end
+ ruby_version_is ""..."3.2" do
+ # https://bugs.ruby-lang.org/issues/18625
+ it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
+ class << self
+ def n(*args) # Note the missing ruby2_keywords here
+ target(*args)
end
- empty = {}
- m(**empty).should == [[], {}]
- m(empty).should == [[{}], {}]
-
- m(a: 1).should == [[{a: 1}], {}]
- m({a: 1}).should == [[{a: 1}], {}]
- end
- end
-
- ruby_version_is ""..."3.2" do
- # https://bugs.ruby-lang.org/issues/18625
- it "works with call(*ruby2_keyword_args) with missing ruby2_keywords in between due to CRuby bug #18625" do
- class << self
- def n(*args) # Note the missing ruby2_keywords here
- target(*args)
- end
-
- ruby2_keywords def m(*args)
- n(*args)
- end
+ ruby2_keywords def m(*args)
+ n(*args)
end
+ end
- empty = {}
- m(**empty).should == [[], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
- m(empty).should == [[{}], {}]
- Hash.ruby2_keywords_hash?(empty).should == false
+ empty = {}
+ m(**empty).should == [[], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
+ m(empty).should == [[{}], {}]
+ Hash.ruby2_keywords_hash?(empty).should == false
- m(a: 1).should == [[], {a: 1}]
- m({a: 1}).should == [[{a: 1}], {}]
+ m(a: 1).should == [[], {a: 1}]
+ m({a: 1}).should == [[{a: 1}], {}]
- kw = {a: 1}
+ kw = {a: 1}
- m(**kw).should == [[], {a: 1}]
- m(**kw)[1].should == kw
- m(**kw)[1].should_not.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
+ m(**kw).should == [[], {a: 1}]
+ m(**kw)[1].should == kw
+ m(**kw)[1].should_not.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
+ Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
- m(kw).should == [[{a: 1}], {}]
- m(kw)[0][0].should.equal?(kw)
- Hash.ruby2_keywords_hash?(kw).should == false
- end
+ m(kw).should == [[{a: 1}], {}]
+ m(kw)[0][0].should.equal?(kw)
+ Hash.ruby2_keywords_hash?(kw).should == false
end
end
end