1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/ruby/language/method_spec.rb
2022-11-07 20:05:30 +01:00

1856 lines
43 KiB
Ruby

require_relative '../spec_helper'
describe "A method send" do
evaluate <<-ruby do
def m(a) a end
ruby
a = b = m 1
a.should == 1
b.should == 1
end
context "with a single splatted Object argument" do
before :all do
def m(a) a end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
m(*x).should equal(x)
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1])
m(*x).should == 1
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
m(*x).should == x
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { m(*x) }.should raise_error(TypeError)
end
end
context "with a leading splatted Object argument" do
before :all do
def m(a, b, *c, d, e) [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
m(*x, 1, 2, 3).should == [x, 1, [], 2, 3]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1])
m(*x, 2, 3, 4).should == [1, 2, [], 3, 4]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
m(*x, 2, 3, 4).should == [x, 2, [], 3, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { m(*x, 2, 3) }.should raise_error(TypeError)
end
end
context "with a middle splatted Object argument" do
before :all do
def m(a, b, *c, d, e) [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
m(1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([5, 6, 7])
m(1, 2, *x, 3).should == [1, 2, [5, 6], 7, 3]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
m(1, 2, *x, 4).should == [1, 2, [], x, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { m(1, *x, 2, 3) }.should raise_error(TypeError)
end
it "copies the splatted array" do
args = [3, 4]
m(1, 2, *args, 4, 5).should == [1, 2, [3, 4], 4, 5]
m(1, 2, *args, 4, 5)[2].should_not equal(args)
end
it "allows an array being splatted to be modified by another argument" do
args = [3, 4]
m(1, args.shift, *args, 4, 5).should == [1, 3, [4], 4, 5]
end
end
context "with a trailing splatted Object argument" do
before :all do
def m(a, *b, c) [a, b, c] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
m(1, 2, *x).should == [1, [2], x]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([5, 6, 7])
m(1, 2, *x).should == [1, [2, 5, 6], 7]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
m(1, 2, *x, 4).should == [1, [2, x], 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { m(1, 2, *x) }.should raise_error(TypeError)
end
end
context "with a block argument" do
before :all do
def m(x)
if block_given?
[true, yield(x + 'b')]
else
[false]
end
end
end
it "that refers to a proc passes the proc as the block" do
m('a', &-> y { y + 'c'}).should == [true, 'abc']
end
it "that is nil passes no block" do
m('a', &nil).should == [false]
end
end
end
describe "An element assignment method send" do
before :each do
ScratchPad.clear
end
context "with a single splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.[]=(a, b) ScratchPad.record [a, b] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o[*x] = 1).should == 1
ScratchPad.recorded.should == [x, 1]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1])
(@o[*x] = 2).should == 2
ScratchPad.recorded.should == [1, 2]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o[*x] = 1).should == 1
ScratchPad.recorded.should == [x, 1]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o[*x] = 1 }.should raise_error(TypeError)
end
end
context "with a leading splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o[*x, 2, 3, 4] = 1).should == 1
ScratchPad.recorded.should == [x, 2, [3], 4, 1]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1, 2, 3])
(@o[*x, 4, 5] = 6).should == 6
ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o[*x, 2, 3, 4] = 5).should == 5
ScratchPad.recorded.should == [x, 2, [3], 4, 5]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o[*x, 2, 3] = 4 }.should raise_error(TypeError)
end
end
context "with a middle splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o[1, *x, 2, 3] = 4).should == 4
ScratchPad.recorded.should == [1, x, [2], 3, 4]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([2, 3])
(@o[1, *x, 4] = 5).should == 5
ScratchPad.recorded.should == [1, 2, [3], 4, 5]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o[1, 2, *x, 3] = 4).should == 4
ScratchPad.recorded.should == [1, 2, [x], 3, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o[1, 2, *x, 3] = 4 }.should raise_error(TypeError)
end
end
context "with a trailing splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.[]=(a, b, *c, d, e) ScratchPad.record [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o[1, 2, 3, 4, *x] = 5).should == 5
ScratchPad.recorded.should == [1, 2, [3, 4], x, 5]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([4, 5])
(@o[1, 2, 3, *x] = 6).should == 6
ScratchPad.recorded.should == [1, 2, [3, 4], 5, 6]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o[1, 2, 3, *x] = 4).should == 4
ScratchPad.recorded.should == [1, 2, [3], x, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o[1, 2, 3, *x] = 4 }.should raise_error(TypeError)
end
end
end
describe "An attribute assignment method send" do
context "with a single splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.m=(a, b) [a, b] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o.send :m=, *x, 1).should == [x, 1]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1])
(@o.send :m=, *x, 2).should == [1, 2]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o.send :m=, *x, 1).should == [x, 1]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o.send :m=, *x, 1 }.should raise_error(TypeError)
end
end
context "with a leading splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o.send :m=, *x, 2, 3, 4, 1).should == [x, 2, [3], 4, 1]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([1, 2, 3])
(@o.send :m=, *x, 4, 5, 6).should == [1, 2, [3, 4], 5, 6]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o.send :m=, *x, 2, 3, 4, 5).should == [x, 2, [3], 4, 5]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o.send :m=, *x, 2, 3, 4 }.should raise_error(TypeError)
end
end
context "with a middle splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o.send :m=, 1, *x, 2, 3, 4).should == [1, x, [2], 3, 4]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([2, 3])
(@o.send :m=, 1, *x, 4, 5).should == [1, 2, [3], 4, 5]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o.send :m=, 1, 2, *x, 3, 4).should == [1, 2, [x], 3, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o.send :m=, 1, 2, *x, 3, 4 }.should raise_error(TypeError)
end
end
context "with a trailing splatted Object argument" do
before :all do
@o = mock("element set receiver")
def @o.m=(a, b, *c, d, e) [a, b, c, d, e] end
end
it "does not call #to_ary" do
x = mock("splat argument")
x.should_not_receive(:to_ary)
(@o.send :m=, 1, 2, 3, 4, *x, 5).should == [1, 2, [3, 4], x, 5]
end
it "calls #to_a" do
x = mock("splat argument")
x.should_receive(:to_a).and_return([4, 5])
(@o.send :m=, 1, 2, 3, *x, 6).should == [1, 2, [3, 4], 5, 6]
end
it "wraps the argument in an Array if #to_a returns nil" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(nil)
(@o.send :m=, 1, 2, 3, *x, 4).should == [1, 2, [3], x, 4]
end
it "raises a TypeError if #to_a does not return an Array" do
x = mock("splat argument")
x.should_receive(:to_a).and_return(1)
-> { @o.send :m=, 1, 2, 3, *x, 4 }.should raise_error(TypeError)
end
end
end
describe "A method" do
SpecEvaluate.desc = "for definition"
context "assigns no local variables" do
evaluate <<-ruby do
def m
end
ruby
m.should be_nil
end
evaluate <<-ruby do
def m()
end
ruby
m.should be_nil
end
end
context "assigns local variables from method parameters" do
evaluate <<-ruby do
def m(a) a end
ruby
m((args = 1, 2, 3)).should equal(args)
end
evaluate <<-ruby do
def m((a)) a end
ruby
m(1).should == 1
m([1, 2, 3]).should == 1
end
evaluate <<-ruby do
def m((*a, b)) [a, b] end
ruby
m(1).should == [[], 1]
m([1, 2, 3]).should == [[1, 2], 3]
end
evaluate <<-ruby do
def m(a=1) a end
ruby
m().should == 1
m(2).should == 2
end
evaluate <<-ruby do
def m() end
ruby
m().should be_nil
m(*[]).should be_nil
m(**{}).should be_nil
end
evaluate <<-ruby do
def m(*) end
ruby
m().should be_nil
m(1).should be_nil
m(1, 2, 3).should be_nil
end
evaluate <<-ruby do
def m(*a) a end
ruby
m().should == []
m(1).should == [1]
m(1, 2, 3).should == [1, 2, 3]
m(*[]).should == []
m(**{}).should == []
end
evaluate <<-ruby do
def m(a:) a end
ruby
-> { m() }.should raise_error(ArgumentError)
m(a: 1).should == 1
suppress_keyword_warning do
-> { m("a" => 1, a: 1) }.should raise_error(ArgumentError)
end
end
evaluate <<-ruby do
def m(a:, **kw) [a, kw] end
ruby
-> { m(b: 1) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(a: 1) a end
ruby
m().should == 1
m(a: 2).should == 2
end
evaluate <<-ruby do
def m(**) end
ruby
m().should be_nil
m(a: 1, b: 2).should be_nil
-> { m(1) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(**k) k end
ruby
m().should == {}
m(a: 1, b: 2).should == { a: 1, b: 2 }
m(*[]).should == {}
m(**{}).should == {}
suppress_warning {
eval "m(**{a: 1, b: 2}, **{a: 4, c: 7})"
}.should == { a: 4, b: 2, c: 7 }
-> { m(2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(**k); k end;
ruby
m("a" => 1).should == { "a" => 1 }
end
evaluate <<-ruby do
def m(&b) b end
ruby
m { }.should be_an_instance_of(Proc)
end
evaluate <<-ruby do
def m(a, b) [a, b] end
ruby
m(1, 2).should == [1, 2]
end
evaluate <<-ruby do
def m(a, (b, c)) [a, b, c] end
ruby
m(1, 2).should == [1, 2, nil]
m(1, [2, 3, 4]).should == [1, 2, 3]
end
evaluate <<-ruby do
def m((a), (b)) [a, b] end
ruby
m(1, 2).should == [1, 2]
m([1, 2], [3, 4]).should == [1, 3]
end
evaluate <<-ruby do
def m((*), (*)) end
ruby
m(2, 3).should be_nil
m([2, 3, 4], [5, 6]).should be_nil
-> { m a: 1 }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m((*a), (*b)) [a, b] end
ruby
m(1, 2).should == [[1], [2]]
m([1, 2], [3, 4]).should == [[1, 2], [3, 4]]
end
evaluate <<-ruby do
def m((a, b), (c, d))
[a, b, c, d]
end
ruby
m(1, 2).should == [1, nil, 2, nil]
m([1, 2, 3], [4, 5, 6]).should == [1, 2, 4, 5]
end
evaluate <<-ruby do
def m((a, *b), (*c, d))
[a, b, c, d]
end
ruby
m(1, 2).should == [1, [], [], 2]
m([1, 2, 3], [4, 5, 6]).should == [1, [2, 3], [4, 5], 6]
end
evaluate <<-ruby do
def m((a, b, *c, d), (*e, f, g), (*h))
[a, b, c, d, e, f, g, h]
end
ruby
m(1, 2, 3).should == [1, nil, [], nil, [], 2, nil, [3]]
result = m([1, 2, 3], [4, 5, 6, 7, 8], [9, 10])
result.should == [1, 2, [], 3, [4, 5, 6], 7, 8, [9, 10]]
end
evaluate <<-ruby do
def m(a, (b, (c, *d), *e))
[a, b, c, d, e]
end
ruby
m(1, 2).should == [1, 2, nil, [], []]
m(1, [2, [3, 4, 5], 6, 7, 8]).should == [1, 2, 3, [4, 5], [6, 7, 8]]
end
evaluate <<-ruby do
def m(a, (b, (c, *d, (e, (*f)), g), (h, (i, j))))
[a, b, c, d, e, f, g, h, i, j]
end
ruby
m(1, 2).should == [1, 2, nil, [], nil, [nil], nil, nil, nil, nil]
result = m(1, [2, [3, 4, 5, [6, [7, 8]], 9], [10, [11, 12]]])
result.should == [1, 2, 3, [4, 5], 6, [7, 8], 9, 10, 11, 12]
end
evaluate <<-ruby do
def m(a, b=1) [a, b] end
ruby
m(2).should == [2, 1]
m(1, 2).should == [1, 2]
end
evaluate <<-ruby do
def m(a, *) a end
ruby
m(1).should == 1
m(1, 2, 3).should == 1
end
evaluate <<-ruby do
def m(a, *b) [a, b] end
ruby
m(1).should == [1, []]
m(1, 2, 3).should == [1, [2, 3]]
end
evaluate <<-ruby do
def m(a, b:) [a, b] end
ruby
m(1, b: 2).should == [1, 2]
suppress_keyword_warning do
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
end
ruby_version_is ""..."3.0" do
evaluate <<-ruby do
def m(a, b: 1) [a, b] end
ruby
m(2).should == [2, 1]
m(1, b: 2).should == [1, 2]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, 1]
end
end
evaluate <<-ruby do
def m(a, **) a end
ruby
m(1).should == 1
m(1, a: 2, b: 3).should == 1
suppress_keyword_warning do
m("a" => 1, b: 2).should == {"a" => 1, b: 2}
end
end
evaluate <<-ruby do
def m(a, **k) [a, k] end
ruby
m(1).should == [1, {}]
m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [{"a" => 1, b: 2}, {}]
end
end
end
ruby_version_is "3.0" do
evaluate <<-ruby do
def m(a, b: 1) [a, b] end
ruby
m(2).should == [2, 1]
m(1, b: 2).should == [1, 2]
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(a, **) a end
ruby
m(1).should == 1
m(1, a: 2, b: 3).should == 1
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(a, **k) [a, k] end
ruby
m(1).should == [1, {}]
m(1, a: 2, b: 3).should == [1, {a: 2, b: 3}]
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
end
evaluate <<-ruby do
def m(a, &b) [a, b] end
ruby
m(1).should == [1, nil]
m(1, &(l = -> {})).should == [1, l]
end
evaluate <<-ruby do
def m(a=1, b) [a, b] end
ruby
m(2).should == [1, 2]
m(2, 3).should == [2, 3]
end
evaluate <<-ruby do
def m(a=1, *) a end
ruby
m().should == 1
m(2, 3, 4).should == 2
end
evaluate <<-ruby do
def m(a=1, *b) [a, b] end
ruby
m().should == [1, []]
m(2, 3, 4).should == [2, [3, 4]]
end
evaluate <<-ruby do
def m(a=1, (b, c)) [a, b, c] end
ruby
m(2).should == [1, 2, nil]
m(2, 3).should == [2, 3, nil]
m(2, [3, 4, 5]).should == [2, 3, 4]
end
evaluate <<-ruby do
def m(a=1, (b, (c, *d))) [a, b, c, d] end
ruby
m(2).should == [1, 2, nil, []]
m(2, 3).should == [2, 3, nil, []]
m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6]]
end
evaluate <<-ruby do
def m(a=1, (b, (c, *d), *e)) [a, b, c, d, e] end
ruby
m(2).should == [1, 2, nil, [], []]
m(2, [3, 4, 5, 6]).should == [2, 3, 4, [], [5, 6]]
m(2, [3, [4, 5, 6], 7]).should == [2, 3, 4, [5, 6], [7]]
end
evaluate <<-ruby do
def m(a=1, (b), (c)) [a, b, c] end
ruby
m(2, 3).should == [1, 2, 3]
m(2, 3, 4).should == [2, 3, 4]
m(2, [3, 4], [5, 6, 7]).should == [2, 3, 5]
end
evaluate <<-ruby do
def m(a=1, (*b), (*c)) [a, b, c] end
ruby
-> { m() }.should raise_error(ArgumentError)
-> { m(2) }.should raise_error(ArgumentError)
m(2, 3).should == [1, [2], [3]]
m(2, [3, 4], [5, 6]).should == [2, [3, 4], [5, 6]]
end
evaluate <<-ruby do
def m(a=1, (b, c), (d, e)) [a, b, c, d, e] end
ruby
m(2, 3).should == [1, 2, nil, 3, nil]
m(2, [3, 4, 5], [6, 7, 8]).should == [2, 3, 4, 6, 7]
end
evaluate <<-ruby do
def m(a=1, (b, *c), (*d, e))
[a, b, c, d, e]
end
ruby
m(1, 2).should == [1, 1, [], [], 2]
m(1, [2, 3], [4, 5, 6]).should == [1, 2, [3], [4, 5], 6]
end
evaluate <<-ruby do
def m(a=1, (b, *c), (d, (*e, f)))
[a, b, c, d, e, f]
end
ruby
m(1, 2).should == [1, 1, [], 2, [], nil]
m(nil, nil).should == [1, nil, [], nil, [], nil]
result = m([1, 2, 3], [4, 5, 6], [7, 8, 9])
result.should == [[1, 2, 3], 4, [5, 6], 7, [], 8]
end
ruby_version_is ""..."3.0" do
evaluate <<-ruby do
def m(a=1, b:) [a, b] end
ruby
m(b: 2).should == [1, 2]
m(2, b: 1).should == [2, 1]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [{"a" => 1}, 2]
end
end
evaluate <<-ruby do
def m(a=1, b: 2) [a, b] end
ruby
m().should == [1, 2]
m(2).should == [2, 2]
m(b: 3).should == [1, 3]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [{"a" => 1}, 2]
end
end
end
ruby_version_is "3.0" do
evaluate <<-ruby do
def m(a=1, b:) [a, b] end
ruby
m(b: 2).should == [1, 2]
m(2, b: 1).should == [2, 1]
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
evaluate <<-ruby do
def m(a=1, b: 2) [a, b] end
ruby
m().should == [1, 2]
m(2).should == [2, 2]
m(b: 3).should == [1, 3]
-> { m("a" => 1, b: 2) }.should raise_error(ArgumentError)
end
end
evaluate <<-ruby do
def m(a=1, **) a end
ruby
m().should == 1
m(2, a: 1, b: 0).should == 2
m("a" => 1, a: 2).should == 1
end
evaluate <<-ruby do
def m(a=1, **k) [a, k] end
ruby
m().should == [1, {}]
m(2, a: 1, b: 2).should == [2, {a: 1, b: 2}]
end
evaluate <<-ruby do
def m(a=1, &b) [a, b] end
ruby
m().should == [1, nil]
m(&(l = -> {})).should == [1, l]
p = -> {}
l = mock("to_proc")
l.should_receive(:to_proc).and_return(p)
m(&l).should == [1, p]
end
evaluate <<-ruby do
def m(*, a) a end
ruby
m(1).should == 1
m(1, 2, 3).should == 3
end
evaluate <<-ruby do
def m(*a, b) [a, b] end
ruby
m(1).should == [[], 1]
m(1, 2, 3).should == [[1, 2], 3]
end
ruby_version_is ""...'3.0' do
evaluate <<-ruby do
def m(*, a:) a end
ruby
m(a: 1).should == 1
m(1, 2, a: 3).should == 3
suppress_keyword_warning do
m("a" => 1, a: 2).should == 2
end
end
evaluate <<-ruby do
def m(*a, b:) [a, b] end
ruby
m(b: 1).should == [[], 1]
m(1, 2, b: 3).should == [[1, 2], 3]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
end
evaluate <<-ruby do
def m(*, a: 1) a end
ruby
m().should == 1
m(1, 2).should == 1
m(a: 2).should == 2
m(1, a: 2).should == 2
suppress_keyword_warning do
m("a" => 1, a: 2).should == 2
end
end
evaluate <<-ruby do
def m(*a, b: 1) [a, b] end
ruby
m().should == [[], 1]
m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
a = mock("splat")
a.should_not_receive(:to_ary)
m(*a).should == [[a], 1]
end
evaluate <<-ruby do
def m(*a, **) a end
ruby
m().should == []
m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
m("a" => 1, a: 1).should == []
m(1, **{a: 2}).should == [1]
h = mock("keyword splat")
h.should_receive(:to_hash)
-> { m(**h) }.should raise_error(TypeError)
end
evaluate <<-ruby do
def m(*, **k) k end
ruby
m().should == {}
m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
m("a" => 1, a: 1).should == {"a" => 1, a: 1}
h = mock("keyword splat")
h.should_receive(:to_hash).and_return({a: 1})
suppress_warning do
m(h).should == {a: 1}
end
end
evaluate <<-ruby do
def m(a = nil, **k) [a, k] end
ruby
m().should == [nil, {}]
m("a" => 1).should == [nil, {"a" => 1}]
m(a: 1).should == [nil, {a: 1}]
m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
suppress_warning do
m({a: 1}, {}).should == [{a: 1}, {}]
h = {"a" => 1, b: 2}
m(h).should == [{"a" => 1}, {b: 2}]
h.should == {"a" => 1, b: 2}
h = {"a" => 1}
m(h).first.should == h
h = {}
r = m(h)
r.first.should be_nil
r.last.should == {}
hh = {}
h = mock("keyword splat empty hash")
h.should_receive(:to_hash).and_return(hh)
r = m(h)
r.first.should be_nil
r.last.should == {}
h = mock("keyword splat")
h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
m(h).should == [{"a" => 1}, {a: 2}]
end
end
evaluate <<-ruby do
def m(*a, **k) [a, k] end
ruby
m().should == [[], {}]
m(1).should == [[1], {}]
m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
m("a" => 1).should == [[], {"a" => 1}]
m(a: 1).should == [[], {a: 1}]
m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
suppress_warning do
m({a: 1}, {}).should == [[{a: 1}], {}]
end
m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
bo = BasicObject.new
def bo.to_a; [1, 2, 3]; end
def bo.to_hash; {:b => 2, :c => 3}; end
m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
end
evaluate <<-ruby do
def m(*, a:) a end
ruby
m(a: 1).should == 1
m(1, 2, a: 3).should == 3
suppress_keyword_warning do
m("a" => 1, a: 2).should == 2
end
end
evaluate <<-ruby do
def m(*a, b:) [a, b] end
ruby
m(b: 1).should == [[], 1]
m(1, 2, b: 3).should == [[1, 2], 3]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
end
evaluate <<-ruby do
def m(*, a: 1) a end
ruby
m().should == 1
m(1, 2).should == 1
m(a: 2).should == 2
m(1, a: 2).should == 2
suppress_keyword_warning do
m("a" => 1, a: 2).should == 2
end
end
evaluate <<-ruby do
def m(*a, b: 1) [a, b] end
ruby
m().should == [[], 1]
m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
suppress_keyword_warning do
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
a = mock("splat")
a.should_not_receive(:to_ary)
m(*a).should == [[a], 1]
end
evaluate <<-ruby do
def m(*a, **) a end
ruby
m().should == []
m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
m("a" => 1, a: 1).should == []
m(1, **{a: 2}).should == [1]
h = mock("keyword splat")
h.should_receive(:to_hash)
-> { m(**h) }.should raise_error(TypeError)
end
evaluate <<-ruby do
def m(*, **k) k end
ruby
m().should == {}
m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
m("a" => 1, a: 1).should == {"a" => 1, a: 1}
h = mock("keyword splat")
h.should_receive(:to_hash).and_return({a: 1})
suppress_keyword_warning do
m(h).should == {a: 1}
end
end
evaluate <<-ruby do
def m(a = nil, **k) [a, k] end
ruby
m().should == [nil, {}]
m("a" => 1).should == [nil, {"a" => 1}]
m(a: 1).should == [nil, {a: 1}]
m("a" => 1, a: 1).should == [nil, {"a" => 1, a: 1}]
m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
suppress_keyword_warning do
m({a: 1}, {}).should == [{a: 1}, {}]
end
h = {"a" => 1, b: 2}
suppress_keyword_warning do
m(h).should == [{"a" => 1}, {b: 2}]
end
h.should == {"a" => 1, b: 2}
h = {"a" => 1}
m(h).first.should == h
h = {}
suppress_keyword_warning do
m(h).should == [nil, {}]
end
hh = {}
h = mock("keyword splat empty hash")
h.should_receive(:to_hash).and_return({a: 1})
suppress_keyword_warning do
m(h).should == [nil, {a: 1}]
end
h = mock("keyword splat")
h.should_receive(:to_hash).and_return({"a" => 1})
m(h).should == [h, {}]
end
evaluate <<-ruby do
def m(*a, **k) [a, k] end
ruby
m().should == [[], {}]
m(1).should == [[1], {}]
m(a: 1, b: 2).should == [[], {a: 1, b: 2}]
m(1, 2, 3, a: 2).should == [[1, 2, 3], {a: 2}]
m("a" => 1).should == [[], {"a" => 1}]
m(a: 1).should == [[], {a: 1}]
m("a" => 1, a: 1).should == [[], {"a" => 1, a: 1}]
m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
suppress_keyword_warning do
m({a: 1}, {}).should == [[{a: 1}], {}]
end
m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
bo = BasicObject.new
def bo.to_a; [1, 2, 3]; end
def bo.to_hash; {:b => 2, :c => 3}; end
m(*bo, **bo).should == [[1, 2, 3], {:b => 2, :c => 3}]
end
end
evaluate <<-ruby do
def m(*, &b) b end
ruby
m().should be_nil
m(1, 2, 3, 4).should be_nil
m(&(l = ->{})).should equal(l)
end
evaluate <<-ruby do
def m(*a, &b) [a, b] end
ruby
m().should == [[], nil]
m(1).should == [[1], nil]
m(1, 2, 3, &(l = -> {})).should == [[1, 2, 3], l]
end
evaluate <<-ruby do
def m(a:, b:) [a, b] end
ruby
m(a: 1, b: 2).should == [1, 2]
suppress_keyword_warning do
-> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
end
end
evaluate <<-ruby do
def m(a:, b: 1) [a, b] end
ruby
m(a: 1).should == [1, 1]
m(a: 1, b: 2).should == [1, 2]
suppress_keyword_warning do
-> { m("a" => 1, a: 1, b: 2) }.should raise_error(ArgumentError)
end
end
evaluate <<-ruby do
def m(a:, **) a end
ruby
m(a: 1).should == 1
m(a: 1, b: 2).should == 1
m("a" => 1, a: 1, b: 2).should == 1
end
evaluate <<-ruby do
def m(a:, **k) [a, k] end
ruby
m(a: 1).should == [1, {}]
m(a: 1, b: 2, c: 3).should == [1, {b: 2, c: 3}]
m("a" => 1, a: 1, b: 2).should == [1, {"a" => 1, b: 2}]
end
evaluate <<-ruby do
def m(a:, &b) [a, b] end
ruby
m(a: 1).should == [1, nil]
m(a: 1, &(l = ->{})).should == [1, l]
end
evaluate <<-ruby do
def m(a: 1, b:) [a, b] end
ruby
m(b: 0).should == [1, 0]
m(b: 2, a: 3).should == [3, 2]
end
evaluate <<-ruby do
def m(a: def m(a: 1) a end, b:)
[a, b]
end
ruby
m(a: 2, b: 3).should == [2, 3]
m(b: 1).should == [:m, 1]
# Note the default value of a: in the original method.
m().should == 1
end
evaluate <<-ruby do
def m(a: 1, b: 2) [a, b] end
ruby
m().should == [1, 2]
m(b: 3, a: 4).should == [4, 3]
end
evaluate <<-ruby do
def m(a: 1, **) a end
ruby
m().should == 1
m(a: 2, b: 1).should == 2
end
evaluate <<-ruby do
def m(a: 1, **k) [a, k] end
ruby
m(b: 2, c: 3).should == [1, {b: 2, c: 3}]
end
evaluate <<-ruby do
def m(a: 1, &b) [a, b] end
ruby
m(&(l = ->{})).should == [1, l]
m().should == [1, nil]
end
evaluate <<-ruby do
def m(**, &b) b end
ruby
m(a: 1, b: 2, &(l = ->{})).should == l
end
evaluate <<-ruby do
def m(**k, &b) [k, b] end
ruby
m(a: 1, b: 2).should == [{ a: 1, b: 2}, nil]
end
evaluate <<-ruby do
def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)
[a, b, c, d, e, f, g, h, k, l]
end
ruby
result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
result.should == [9, 8, [7], [], 6, 5, 4, 3, {}, l]
end
evaluate <<-ruby do
def m(a, b=1, *c, d, e:, f: 2, g:, **k, &l)
[a, b, c, d, e, f, g, k, l]
end
ruby
result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l]
end
evaluate <<-ruby do
def m(a, **nil); a end;
ruby
m({a: 1}).should == {a: 1}
m({"a" => 1}).should == {"a" => 1}
-> { m(a: 1) }.should raise_error(ArgumentError)
-> { m(**{a: 1}) }.should raise_error(ArgumentError)
-> { m("a" => 1) }.should raise_error(ArgumentError)
end
ruby_version_is ''...'3.0' do
evaluate <<-ruby do
def m(a, b = nil, c = nil, d, e: nil, **f)
[a, b, c, d, e, f]
end
ruby
result = m(1, 2)
result.should == [1, nil, nil, 2, nil, {}]
suppress_warning do
result = m(1, 2, {foo: :bar})
result.should == [1, nil, nil, 2, nil, {foo: :bar}]
end
result = m(1, {foo: :bar})
result.should == [1, nil, nil, {foo: :bar}, nil, {}]
end
end
ruby_version_is '3.0' do
evaluate <<-ruby do
def m(a, b = nil, c = nil, d, e: nil, **f)
[a, b, c, d, e, f]
end
ruby
result = m(1, 2)
result.should == [1, nil, nil, 2, nil, {}]
result = m(1, 2, {foo: :bar})
result.should == [1, 2, nil, {foo: :bar}, nil, {}]
result = m(1, {foo: :bar})
result.should == [1, nil, nil, {foo: :bar}, nil, {}]
end
end
end
context 'when passing an empty keyword splat to a method that does not accept keywords' do
evaluate <<-ruby do
def m(*a); a; end
ruby
h = {}
m(**h).should == []
end
end
ruby_version_is ''...'3.0' do
context 'when passing an empty keyword splat to a method that does not accept keywords' do
evaluate <<-ruby do
def m(a); a; end
ruby
h = {}
-> do
m(**h).should == {}
end.should complain(/warning: Passing the keyword argument as the last hash parameter is deprecated/)
end
end
end
ruby_version_is ''...'3.0' do
context "assigns keyword arguments from a passed Hash without modifying it" do
evaluate <<-ruby do
def m(a: nil); a; end
ruby
options = {a: 1}.freeze
-> do
suppress_warning do
m(options).should == 1
end
end.should_not raise_error
options.should == {a: 1}
end
end
end
ruby_version_is '3.0' do
context 'when passing an empty keyword splat to a method that does not accept keywords' do
evaluate <<-ruby do
def m(a); a; end
ruby
h = {}
-> do
m(**h).should == {}
end.should raise_error(ArgumentError)
end
end
context "raises ArgumentError if passing hash as keyword arguments" do
evaluate <<-ruby do
def m(a: nil); a; end
ruby
options = {a: 1}.freeze
-> do
m(options)
end.should raise_error(ArgumentError)
end
end
end
it "assigns the last Hash to the last optional argument if the Hash contains non-Symbol keys and is not passed as keywords" do
def m(a = nil, b = {}, v: false)
[a, b, v]
end
h = { "key" => "value" }
m(:a, h).should == [:a, h, false]
m(:a, h, v: true).should == [:a, h, true]
m(v: true).should == [nil, {}, true]
end
end
describe "A method call with a space between method name and parentheses" do
before(:each) do
def m(*args)
args
end
def n(value, &block)
[value, block.call]
end
end
context "when no arguments provided" do
it "assigns nil" do
args = m ()
args.should == [nil]
end
end
context "when a single argument provided" do
it "assigns it" do
args = m (1 == 1 ? true : false)
args.should == [true]
end
end
context "when 2+ arguments provided" do
it "raises a syntax error" do
-> {
eval("m (1, 2)")
}.should raise_error(SyntaxError)
-> {
eval("m (1, 2, 3)")
}.should raise_error(SyntaxError)
end
end
it "allows to pass a block with curly braces" do
args = n () { :block_value }
args.should == [nil, :block_value]
args = n (1) { :block_value }
args.should == [1, :block_value]
end
it "allows to pass a block with do/end" do
args = n () do
:block_value
end
args.should == [nil, :block_value]
args = n (1) do
:block_value
end
args.should == [1, :block_value]
end
end
describe "An array-dereference method ([])" do
SpecEvaluate.desc = "for definition"
context "received the passed-in block" do
evaluate <<-ruby do
def [](*, &b)
b.call
end
ruby
pr = proc {:ok}
self[&pr].should == :ok
self['foo', &pr].should == :ok
self.[](&pr).should == :ok
self.[]('foo', &pr).should == :ok
end
evaluate <<-ruby do
def [](*)
yield
end
ruby
pr = proc {:ok}
self[&pr].should == :ok
self['foo', &pr].should == :ok
self.[](&pr).should == :ok
self.[]('foo', &pr).should == :ok
end
end
end
ruby_version_is "3.0" do
describe "An endless method definition" do
context "without arguments" do
evaluate <<-ruby do
def m() = 42
ruby
m.should == 42
end
context "without parenthesis" do
evaluate <<-ruby do
def m = 42
ruby
m.should == 42
end
end
end
context "with arguments" do
evaluate <<-ruby do
def m(a, b) = a + b
ruby
m(1, 4).should == 5
end
end
context "with multiline body" do
evaluate <<-ruby do
def m(n) =
if n > 2
m(n - 2) + m(n - 1)
else
1
end
ruby
m(6).should == 8
end
end
context "with args forwarding" do
evaluate <<-ruby do
def mm(word, num:)
word * num
end
def m(...) = mm(...) + mm(...)
ruby
m("meow", num: 2).should == "meow" * 4
end
end
ruby_version_is ""..."3.0" do
context "inside 'endless' method definitions" do
it "does not allow method calls without parenthesis" do
-> {
eval("def greet(person) = 'Hi, '.concat person")
}.should raise_error(SyntaxError)
end
end
end
end
describe "Keyword arguments are now separated from positional arguments" do
context "when the method has only positional parameters" do
it "treats incoming keyword arguments as positional for compatibility" do
def foo(a, b, c, hsh)
hsh[:key]
end
foo(1, 2, 3, key: 42).should == 42
end
end
context "when the method takes a ** parameter" do
it "captures the passed literal keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
foo(1, 2, 3, key: 42).should == 42
end
it "captures the passed ** keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
h = { key: 42 }
foo(1, 2, 3, **h).should == 42
end
it "does not convert a positional Hash to keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
-> {
foo(1, 2, 3, { key: 42 })
}.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
end
end
context "when the method takes a key: parameter" do
context "when it's called with a positional Hash and no **" do
it "raises ArgumentError" do
def foo(a, b, c, key: 1)
key
end
-> {
foo(1, 2, 3, { key: 42 })
}.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
end
end
context "when it's called with **" do
it "captures the passed keyword arguments" do
def foo(a, b, c, key: 1)
key
end
h = { key: 42 }
foo(1, 2, 3, **h).should == 42
end
end
end
end
end
ruby_version_is "3.1" do
describe "kwarg with omitted value in a method call" do
context "accepts short notation 'kwarg' in method call" do
evaluate <<-ruby do
def call(*args, **kwargs) = [args, kwargs]
ruby
a, b, c = 1, 2, 3
arr, h = eval('call a:')
h.should == {a: 1}
arr.should == []
arr, h = eval('call(a:, b:, c:)')
h.should == {a: 1, b: 2, c: 3}
arr.should == []
arr, h = eval('call(a:, b: 10, c:)')
h.should == {a: 1, b: 10, c: 3}
arr.should == []
end
end
context "with methods and local variables" do
evaluate <<-ruby do
def call(*args, **kwargs) = [args, kwargs]
def bar
"baz"
end
def foo(val)
call bar:, val:
end
ruby
foo(1).should == [[], {bar: "baz", val: 1}]
end
end
end
describe "Inside 'endless' method definitions" do
it "allows method calls without parenthesis" do
eval <<-ruby
def greet(person) = "Hi, ".concat person
ruby
greet("Homer").should == "Hi, Homer"
end
end
end