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: 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 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 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 end