mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Split Arguments class by functionality
Code changes for https://github.com/icy-arctic-fox/spectator/issues/47 caused a drastic increase in compilation times. This improves compilation times by splitting concerns for arguments. In one case, arguments are used for matching. In the other, arguments are captured for comparison. The second case has been moved to a FormalArguments class. Theoretically, this reduces the complexity and combinations the compiler might be iterating.
This commit is contained in:
parent
015d36ea4c
commit
8efd38fbdd
7 changed files with 682 additions and 267 deletions
|
@ -1,13 +1,11 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
Spectator.describe Spectator::Arguments do
|
||||
subject(arguments) { Spectator::Arguments.new({42, "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
subject(arguments) { Spectator::Arguments.new({42, "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "stores the arguments" do
|
||||
expect(arguments).to have_attributes(
|
||||
args: {42, "foo"},
|
||||
splat_name: :splat,
|
||||
splat: {:x, :y, :z},
|
||||
kwargs: {bar: "baz", qux: 123}
|
||||
)
|
||||
end
|
||||
|
@ -27,33 +25,6 @@ Spectator.describe Spectator::Arguments do
|
|||
expect(arguments[1]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns splat arguments" do
|
||||
aggregate_failures do
|
||||
expect(arguments[2]).to eq(:x)
|
||||
expect(arguments[3]).to eq(:y)
|
||||
expect(arguments[4]).to eq(:z)
|
||||
end
|
||||
end
|
||||
|
||||
context "with named positional arguments" do
|
||||
subject(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "returns a positional argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[0]).to eq(42)
|
||||
expect(arguments[1]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns splat arguments" do
|
||||
aggregate_failures do
|
||||
expect(arguments[2]).to eq(:x)
|
||||
expect(arguments[3]).to eq(:y)
|
||||
expect(arguments[4]).to eq(:z)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#[](symbol)" do
|
||||
|
@ -63,31 +34,13 @@ Spectator.describe Spectator::Arguments do
|
|||
expect(arguments[:qux]).to eq(123)
|
||||
end
|
||||
end
|
||||
|
||||
context "with named positional arguments" do
|
||||
subject(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "returns a positional argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[:arg1]).to eq(42)
|
||||
expect(arguments[:arg2]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns a keyword argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[:bar]).to eq("baz")
|
||||
expect(arguments[:qux]).to eq(123)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_s" do
|
||||
subject { arguments.to_s }
|
||||
|
||||
it "formats the arguments" do
|
||||
is_expected.to eq("(42, \"foo\", *splat: {:x, :y, :z}, bar: \"baz\", qux: 123)")
|
||||
is_expected.to eq("(42, \"foo\", bar: \"baz\", qux: 123)")
|
||||
end
|
||||
|
||||
context "when empty" do
|
||||
|
@ -97,172 +50,235 @@ Spectator.describe Spectator::Arguments do
|
|||
is_expected.to eq("(no args)")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a splat and no arguments" do
|
||||
let(arguments) { Spectator::Arguments.build(NamedTuple.new, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "omits the splat name" do
|
||||
is_expected.to eq("(:x, :y, :z, bar: \"baz\", qux: 123)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#==" do
|
||||
subject { arguments == other }
|
||||
|
||||
context "with equal arguments" do
|
||||
let(other) { arguments }
|
||||
context "with Arguments" do
|
||||
context "with equal arguments" do
|
||||
let(other) { arguments }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(other) { Spectator::Arguments.new({123, :foo, "bar"}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, {qux: 123, bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, {bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with an extra kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, {bar: "baz", qux: 123, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(other) { Spectator::Arguments.new({123, :foo, "bar"}, :splat, {1, 2, 3}, {opt: "foobar"}) }
|
||||
context "with FormalArguments" do
|
||||
context "with equal arguments" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {qux: 123, bar: "baz"}) }
|
||||
context "with different arguments" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 123, arg2: :foo, arg3: "bar"}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: "baz"}) }
|
||||
context "with the same kwargs in a different order" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {qux: 123, bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with an extra kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: "baz", qux: 123, extra: 0}) }
|
||||
context "with a missing kwarg" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different splat arguments" do
|
||||
let(other) { Spectator::Arguments.new(arguments.args, arguments.splat_name, {1, 2, 3}, arguments.kwargs) }
|
||||
context "with an extra kwarg" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {bar: "baz", qux: 123, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with mixed positional tuple types" do
|
||||
let(other) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
context "with different splat arguments" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {1, 2, 3}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with mixed positional tuple types (flipped)" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(other) { Spectator::Arguments.new({42, "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
context "with mixed positional tuple types" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42}, :splat, {"foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with args spilling over into splat and mixed positional tuple types" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(other) { Spectator::Arguments.new({42, "foo", :x, :y, :z}, nil, nil, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#===" do
|
||||
subject { pattern === arguments }
|
||||
|
||||
context "with equal arguments" do
|
||||
let(pattern) { arguments }
|
||||
context "with Arguments" do
|
||||
context "with equal arguments" do
|
||||
let(pattern) { arguments }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Int32, /foo/}, {bar: /baz/, qux: Int32}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with non-matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Float64, /bar/}, {bar: /foo/, qux: "123"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({123, :foo, "bar"}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, {qux: Int32, bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with an additional kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, {bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, {bar: /baz/, qux: Int32, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
|
||||
context "with matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Int32, /foo/}, :splat, {Symbol, Symbol, :z}, {bar: /baz/, qux: Int32}) }
|
||||
context "with FormalArguments" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with equal arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({42, "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
context "with non-matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Float64, /bar/}, :splat, {String, Int32, :x}, {bar: /foo/, qux: "123"}) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Int32, /foo/}, {bar: /baz/, qux: Int32}) }
|
||||
|
||||
context "with different arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({123, :foo, "bar"}, :splat, {1, 2, 3}, {opt: "foobar"}) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with non-matching arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({Float64, /bar/}, {bar: /foo/, qux: "123"}) }
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {qux: Int32, bar: /baz/}) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with different arguments" do
|
||||
let(pattern) { Spectator::Arguments.new({123, :foo, "bar"}, {opt: "foobar"}) }
|
||||
|
||||
context "with an additional kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: /baz/}) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with the same kwargs in a different order" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.positional, {qux: Int32, bar: /baz/}) }
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: /baz/, qux: Int32, extra: 0}) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with an additional kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.positional, {bar: /baz/}) }
|
||||
|
||||
context "with different splat arguments" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.args, arguments.splat_name, {1, 2, 3}, arguments.kwargs) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with a missing kwarg" do
|
||||
let(pattern) { Spectator::Arguments.new(arguments.positional, {bar: /baz/, qux: Int32, extra: 0}) }
|
||||
|
||||
context "with matching mixed positional tuple types" do
|
||||
let(pattern) { Spectator::Arguments.new({arg1: Int32, arg2: /foo/}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with different splat arguments" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {1, 2, 3}, super.kwargs) }
|
||||
let(pattern) { Spectator::Arguments.new({Int32, /foo/, 5}, arguments.kwargs) }
|
||||
|
||||
context "with non-matching mixed positional tuple types" do
|
||||
let(pattern) { Spectator::Arguments.new({arg1: Float64, arg2: /bar/}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with matching mixed positional tuple types" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {1, 2, 3}, super.kwargs) }
|
||||
let(pattern) { Spectator::Arguments.new({Int32, /foo/, 1, 2, 3}, arguments.kwargs) }
|
||||
|
||||
context "with matching args spilling over into splat and mixed positional tuple types" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(Int32, /foo/, Symbol, Symbol, :z, bar: /baz/, qux: Int32) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with non-matching mixed positional tuple types" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {1, 2, 3}, super.kwargs) }
|
||||
let(pattern) { Spectator::Arguments.new({Float64, /bar/, 3, 2, Symbol}, arguments.kwargs) }
|
||||
|
||||
context "with non-matching args spilling over into splat and mixed positional tuple types" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(Float64, /bar/, Symbol, String, :z, bar: /foo/, qux: Int32) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with matching args spilling over into splat and mixed positional tuple types" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(Int32, /foo/, Symbol, Symbol, :z, bar: /baz/, qux: Int32) }
|
||||
|
||||
context "with matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(/foo/, Symbol, :y, Symbol, arg1: Int32, bar: /baz/, qux: 123) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
context "with non-matching args spilling over into splat and mixed positional tuple types" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(Float64, /bar/, Symbol, String, :z, bar: /foo/, qux: Int32) }
|
||||
|
||||
context "with non-matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(5, Symbol, :z, Symbol, arg2: /foo/, bar: /baz/, qux: Int32) }
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
context "with matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(/foo/, Symbol, :y, Symbol, arg1: Int32, bar: /baz/, qux: 123) }
|
||||
|
||||
context "with non-matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::Arguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(/bar/, String, :y, Symbol, arg1: 0, bar: /foo/, qux: Float64) }
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
it { is_expected.to be_false }
|
||||
context "with non-matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(5, Symbol, :z, Symbol, arg2: /foo/, bar: /baz/, qux: Int32) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with non-matching mixed named positional and keyword arguments" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(pattern) { Spectator::Arguments.capture(/bar/, String, :y, Symbol, arg1: 0, bar: /foo/, qux: Float64) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
325
spec/spectator/mocks/formal_arguments_spec.cr
Normal file
325
spec/spectator/mocks/formal_arguments_spec.cr
Normal file
|
@ -0,0 +1,325 @@
|
|||
require "../../spec_helper"
|
||||
|
||||
Spectator.describe Spectator::FormalArguments do
|
||||
subject(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "stores the arguments" do
|
||||
expect(arguments).to have_attributes(
|
||||
args: {arg1: 42, arg2: "foo"},
|
||||
splat_name: :splat,
|
||||
splat: {:x, :y, :z},
|
||||
kwargs: {bar: "baz", qux: 123}
|
||||
)
|
||||
end
|
||||
|
||||
describe ".build" do
|
||||
subject { Spectator::FormalArguments.build({arg1: 42, arg2: "foo"}, :splat, {1, 2, 3}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "stores the arguments and keyword arguments" do
|
||||
is_expected.to have_attributes(
|
||||
args: {arg1: 42, arg2: "foo"},
|
||||
splat_name: :splat,
|
||||
splat: {1, 2, 3},
|
||||
kwargs: {bar: "baz", qux: 123}
|
||||
)
|
||||
end
|
||||
|
||||
context "without a splat" do
|
||||
subject { Spectator::FormalArguments.build({arg1: 42, arg2: "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "stores the arguments and keyword arguments" do
|
||||
is_expected.to have_attributes(
|
||||
args: {arg1: 42, arg2: "foo"},
|
||||
splat: nil,
|
||||
kwargs: {bar: "baz", qux: 123}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#[](index)" do
|
||||
it "returns a positional argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[0]).to eq(42)
|
||||
expect(arguments[1]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns splat arguments" do
|
||||
aggregate_failures do
|
||||
expect(arguments[2]).to eq(:x)
|
||||
expect(arguments[3]).to eq(:y)
|
||||
expect(arguments[4]).to eq(:z)
|
||||
end
|
||||
end
|
||||
|
||||
context "with named positional arguments" do
|
||||
subject(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "returns a positional argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[0]).to eq(42)
|
||||
expect(arguments[1]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns splat arguments" do
|
||||
aggregate_failures do
|
||||
expect(arguments[2]).to eq(:x)
|
||||
expect(arguments[3]).to eq(:y)
|
||||
expect(arguments[4]).to eq(:z)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#[](symbol)" do
|
||||
it "returns a keyword argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[:bar]).to eq("baz")
|
||||
expect(arguments[:qux]).to eq(123)
|
||||
end
|
||||
end
|
||||
|
||||
context "with named positional arguments" do
|
||||
subject(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "returns a positional argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[:arg1]).to eq(42)
|
||||
expect(arguments[:arg2]).to eq("foo")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns a keyword argument" do
|
||||
aggregate_failures do
|
||||
expect(arguments[:bar]).to eq("baz")
|
||||
expect(arguments[:qux]).to eq(123)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_s" do
|
||||
subject { arguments.to_s }
|
||||
|
||||
it "formats the arguments" do
|
||||
is_expected.to eq("(arg1: 42, arg2: \"foo\", *splat: {:x, :y, :z}, bar: \"baz\", qux: 123)")
|
||||
end
|
||||
|
||||
context "when empty" do
|
||||
let(arguments) { Spectator::FormalArguments.none }
|
||||
|
||||
it "returns (no args)" do
|
||||
is_expected.to eq("(no args)")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a splat and no arguments" do
|
||||
let(arguments) { Spectator::FormalArguments.build(NamedTuple.new, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it "omits the splat name" do
|
||||
is_expected.to eq("(:x, :y, :z, bar: \"baz\", qux: 123)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#==" do
|
||||
subject { arguments == other }
|
||||
|
||||
context "with Arguments" do
|
||||
context "with equal arguments" do
|
||||
let(other) { Spectator::Arguments.new(arguments.positional, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(other) { Spectator::Arguments.new({123, :foo, "bar"}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(other) { Spectator::Arguments.new(arguments.positional, {qux: 123, bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.positional, {bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with an extra kwarg" do
|
||||
let(other) { Spectator::Arguments.new(arguments.positional, {bar: "baz", qux: 123, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
|
||||
context "with FormalArguments" do
|
||||
context "with equal arguments" do
|
||||
let(other) { arguments }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 123, arg2: :foo, arg3: "bar"}, :splat, {1, 2, 3}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(other) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {qux: 123, bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(other) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: "baz"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with an extra kwarg" do
|
||||
let(other) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: "baz", qux: 123, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different splat arguments" do
|
||||
let(other) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, {1, 2, 3}, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with mixed positional tuple types" do
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with mixed positional tuple types (flipped)" do
|
||||
let(arguments) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
let(other) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, :splat, {:x, :y, :z}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#===" do
|
||||
subject { pattern === arguments }
|
||||
|
||||
context "with Arguments" do
|
||||
let(arguments) { Spectator::Arguments.new({42, "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
context "with equal arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: 42, arg2: "foo"}, {bar: "baz", qux: 123}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with matching arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, {bar: /baz/, qux: Int32}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with non-matching arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Float64, arg2: /bar/}, {bar: /foo/, qux: "123"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: 123, arg2: :foo, arg3: "bar"}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, {qux: Int32, bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with an additional kwarg" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, {bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, {bar: /baz/, qux: Int32, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
|
||||
context "with FormalArguments" do
|
||||
context "with equal arguments" do
|
||||
let(pattern) { arguments }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with matching arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, :splat, {Symbol, Symbol, :z}, {bar: /baz/, qux: Int32}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with non-matching arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Float64, arg2: /bar/}, :splat, {String, Int32, :x}, {bar: /foo/, qux: "123"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: 123, arg2: :foo, arg3: "bar"}, :splat, {1, 2, 3}, {opt: "foobar"}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with the same kwargs in a different order" do
|
||||
let(pattern) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {qux: Int32, bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with an additional kwarg" do
|
||||
let(pattern) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: /baz/}) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with a missing kwarg" do
|
||||
let(pattern) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, arguments.splat, {bar: /baz/, qux: Int32, extra: 0}) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with different splat arguments" do
|
||||
let(pattern) { Spectator::FormalArguments.new(arguments.args, arguments.splat_name, {1, 2, 3}, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
|
||||
context "with matching mixed positional tuple types" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Int32, arg2: /foo/}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_true }
|
||||
end
|
||||
|
||||
context "with non-matching mixed positional tuple types" do
|
||||
let(pattern) { Spectator::FormalArguments.new({arg1: Float64, arg2: /bar/}, arguments.splat_name, arguments.splat, arguments.kwargs) }
|
||||
|
||||
it { is_expected.to be_false }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,13 @@
|
|||
module Spectator
|
||||
# Untyped arguments to a method call (message).
|
||||
abstract class AbstractArguments
|
||||
# Utility method for comparing two named tuples ignoring order.
|
||||
private def compare_named_tuples(a : NamedTuple, b : NamedTuple)
|
||||
a.each do |k, v1|
|
||||
v2 = b.fetch(k) { return false }
|
||||
return false unless v1 === v2
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,123 +4,68 @@ module Spectator
|
|||
# Arguments used in a method call.
|
||||
#
|
||||
# Can also be used to match arguments.
|
||||
# *Args* must be a `Tuple` or `NamedTuple` type representing the standard arguments.
|
||||
# *Splat* must be a `Tuple` type representing the extra positional arguments.
|
||||
# *DoubleSplat* must be a `NamedTuple` type representing extra keyword arguments.
|
||||
class Arguments(Args, Splat, DoubleSplat) < AbstractArguments
|
||||
# *Args* must be a `Tuple` representing the standard arguments.
|
||||
# *KWArgs* must be a `NamedTuple` type representing extra keyword arguments.
|
||||
class Arguments(Args, KWArgs) < AbstractArguments
|
||||
# Positional arguments.
|
||||
getter args : Args
|
||||
|
||||
# Additional positional arguments.
|
||||
getter splat : Splat
|
||||
|
||||
# Keyword arguments.
|
||||
getter kwargs : DoubleSplat
|
||||
|
||||
# Name of the splat argument, if used.
|
||||
getter splat_name : Symbol?
|
||||
getter kwargs : KWArgs
|
||||
|
||||
# Creates arguments used in a method call.
|
||||
def initialize(@args : Args, @splat_name : Symbol?, @splat : Splat, @kwargs : DoubleSplat)
|
||||
end
|
||||
|
||||
# Creates arguments used in a method call.
|
||||
def self.new(args : Args, kwargs : DoubleSplat)
|
||||
new(args, nil, nil, kwargs)
|
||||
def initialize(@args : Args, @kwargs : KWArgs)
|
||||
{% raise "Positional arguments (generic type Args) must be a Tuple" unless Args <= Tuple %}
|
||||
{% raise "Keyword arguments (generic type KWArgs) must be a NamedTuple" unless KWArgs <= NamedTuple %}
|
||||
end
|
||||
|
||||
# Instance of empty arguments.
|
||||
class_getter none : AbstractArguments = build
|
||||
class_getter none : AbstractArguments = capture
|
||||
|
||||
# Returns unconstrained arguments.
|
||||
def self.any : AbstractArguments?
|
||||
nil.as(AbstractArguments?)
|
||||
end
|
||||
|
||||
# Captures arguments passed to a call.
|
||||
def self.build(args = Tuple.new, kwargs = NamedTuple.new)
|
||||
new(args, nil, nil, kwargs)
|
||||
end
|
||||
|
||||
# :ditto:
|
||||
def self.build(args : NamedTuple, splat_name : Symbol, splat : Tuple, kwargs = NamedTuple.new)
|
||||
new(args, splat_name, splat, kwargs)
|
||||
end
|
||||
|
||||
# Friendlier constructor for capturing arguments.
|
||||
def self.capture(*args, **kwargs)
|
||||
new(args, nil, nil, kwargs)
|
||||
new(args, kwargs)
|
||||
end
|
||||
|
||||
# Returns the positional argument at the specified index.
|
||||
def [](index : Int)
|
||||
positional[index]
|
||||
args[index]
|
||||
end
|
||||
|
||||
# Returns the specified named argument.
|
||||
def [](arg : Symbol)
|
||||
{% if Args < NamedTuple %}
|
||||
return @args[arg] if @args.has_key?(arg)
|
||||
{% end %}
|
||||
@kwargs[arg]
|
||||
end
|
||||
|
||||
# Returns all arguments and splatted arguments as a tuple.
|
||||
def positional : Tuple
|
||||
if (splat = @splat)
|
||||
{% if Args < NamedTuple %}args.values{% else %}args{% end %} + splat
|
||||
else
|
||||
{% if Args < NamedTuple %}args.values{% else %}args{% end %}
|
||||
end
|
||||
args
|
||||
end
|
||||
|
||||
# Returns all named positional and keyword arguments as a named tuple.
|
||||
def named : NamedTuple
|
||||
{% if Args < NamedTuple %}
|
||||
args.merge(kwargs)
|
||||
{% else %}
|
||||
kwargs
|
||||
{% end %}
|
||||
kwargs
|
||||
end
|
||||
|
||||
# Constructs a string representation of the arguments.
|
||||
def to_s(io : IO) : Nil
|
||||
return io << "(no args)" if args.empty? && ((splat = @splat).nil? || splat.empty?) && kwargs.empty?
|
||||
return io << "(no args)" if args.empty? && kwargs.empty?
|
||||
|
||||
io << '('
|
||||
|
||||
# Add the positional arguments.
|
||||
{% if Args < NamedTuple %}
|
||||
# Include argument names.
|
||||
args.each_with_index do |name, value, i|
|
||||
io << ", " if i > 0
|
||||
io << name << ": "
|
||||
value.inspect(io)
|
||||
end
|
||||
{% else %}
|
||||
args.each_with_index do |arg, i|
|
||||
io << ", " if i > 0
|
||||
arg.inspect(io)
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# Add the splat arguments.
|
||||
if (splat = @splat) && !splat.empty?
|
||||
io << ", " unless args.empty?
|
||||
if splat_name = !args.empty? && @splat_name
|
||||
io << '*' << splat_name << ": {"
|
||||
end
|
||||
splat.each_with_index do |arg, i|
|
||||
io << ", " if i > 0
|
||||
arg.inspect(io)
|
||||
end
|
||||
io << '}' if splat_name
|
||||
args.each_with_index do |arg, i|
|
||||
io << ", " if i > 0
|
||||
arg.inspect(io)
|
||||
end
|
||||
|
||||
# Add the keyword arguments.
|
||||
offset = args.size
|
||||
offset += splat.size if (splat = @splat)
|
||||
kwargs.each_with_index(offset) do |key, value, i|
|
||||
kwargs.each_with_index(args.size) do |key, value, i|
|
||||
io << ", " if i > 0
|
||||
io << key << ": "
|
||||
value.inspect(io)
|
||||
|
@ -130,55 +75,35 @@ module Spectator
|
|||
end
|
||||
|
||||
# Checks if this set of arguments and another are equal.
|
||||
def ==(other : Arguments)
|
||||
def ==(other : AbstractArguments)
|
||||
positional == other.positional && kwargs == other.kwargs
|
||||
end
|
||||
|
||||
# Checks if another set of arguments matches this set of arguments.
|
||||
def ===(other : Arguments)
|
||||
self_args = args
|
||||
other_args = other.args
|
||||
|
||||
case {self_args, other_args}
|
||||
when {Tuple, Tuple} then compare(positional, other.positional, kwargs, other.kwargs)
|
||||
when {Tuple, NamedTuple} then compare(kwargs, other.named, positional, other_args, other.splat)
|
||||
when {NamedTuple, Tuple} then compare(positional, other.positional, kwargs, other.kwargs)
|
||||
else
|
||||
self_args === other_args && (!splat || splat === other.splat) && compare_named_tuples(kwargs, other.kwargs)
|
||||
end
|
||||
positional === other.positional && compare_named_tuples(kwargs, other.kwargs)
|
||||
end
|
||||
|
||||
private def compare(self_positional : Tuple, other_positional : Tuple, self_kwargs : NamedTuple, other_kwargs : NamedTuple)
|
||||
self_positional === other_positional && compare_named_tuples(self_kwargs, other_kwargs)
|
||||
end
|
||||
|
||||
private def compare(self_kwargs : NamedTuple, other_named : NamedTuple, self_positional : Tuple, other_args : NamedTuple, other_splat : Tuple?)
|
||||
return false unless compare_named_tuples(self_kwargs, other_named)
|
||||
# :ditto:
|
||||
def ===(other : FormalArguments)
|
||||
return false unless compare_named_tuples(kwargs, other.named)
|
||||
|
||||
i = 0
|
||||
other_args.each do |k, v2|
|
||||
next if self_kwargs.has_key?(k) # Covered by named arguments.
|
||||
other.args.each do |k, v2|
|
||||
next if kwargs.has_key?(k) # Covered by named arguments.
|
||||
|
||||
v1 = self_positional.fetch(i) { return false }
|
||||
v1 = positional.fetch(i) { return false }
|
||||
i += 1
|
||||
return false unless v1 === v2
|
||||
end
|
||||
|
||||
other_splat.try &.each do |v2|
|
||||
v1 = self_positional.fetch(i) { return false }
|
||||
other.splat.try &.each do |v2|
|
||||
v1 = positional.fetch(i) { return false }
|
||||
i += 1
|
||||
return false unless v1 === v2
|
||||
end
|
||||
|
||||
i == self_positional.size
|
||||
end
|
||||
|
||||
private def compare_named_tuples(a : NamedTuple, b : NamedTuple)
|
||||
a.each do |k, v1|
|
||||
v2 = b.fetch(k) { return false }
|
||||
return false unless v1 === v2
|
||||
end
|
||||
true
|
||||
i == positional.size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
133
src/spectator/mocks/formal_arguments.cr
Normal file
133
src/spectator/mocks/formal_arguments.cr
Normal file
|
@ -0,0 +1,133 @@
|
|||
require "./abstract_arguments"
|
||||
|
||||
module Spectator
|
||||
# Arguments passed into a method.
|
||||
#
|
||||
# *Args* must be a `NamedTuple` type representing the standard arguments.
|
||||
# *Splat* must be a `Tuple` type representing the extra positional arguments.
|
||||
# *DoubleSplat* must be a `NamedTuple` type representing extra keyword arguments.
|
||||
class FormalArguments(Args, Splat, DoubleSplat) < AbstractArguments
|
||||
# Positional arguments.
|
||||
getter args : Args
|
||||
|
||||
# Additional positional arguments.
|
||||
getter splat : Splat
|
||||
|
||||
# Keyword arguments.
|
||||
getter kwargs : DoubleSplat
|
||||
|
||||
# Name of the splat argument, if used.
|
||||
getter splat_name : Symbol?
|
||||
|
||||
# Creates arguments used in a method call.
|
||||
def initialize(@args : Args, @splat_name : Symbol?, @splat : Splat, @kwargs : DoubleSplat)
|
||||
{% raise "Positional arguments (generic type Args) must be a NamedTuple" unless Args <= NamedTuple %}
|
||||
{% raise "Splat arguments (generic type Splat) must be a Tuple" unless Splat <= Tuple || Splat <= Nil %}
|
||||
{% raise "Keyword arguments (generic type DoubleSplat) must be a NamedTuple" unless DoubleSplat <= NamedTuple %}
|
||||
end
|
||||
|
||||
# Creates arguments used in a method call.
|
||||
def self.new(args : Args, kwargs : DoubleSplat)
|
||||
new(args, nil, nil, kwargs)
|
||||
end
|
||||
|
||||
# Captures arguments passed to a call.
|
||||
def self.build(args = NamedTuple.new, kwargs = NamedTuple.new)
|
||||
new(args, nil, nil, kwargs)
|
||||
end
|
||||
|
||||
# :ditto:
|
||||
def self.build(args : NamedTuple, splat_name : Symbol, splat : Tuple, kwargs = NamedTuple.new)
|
||||
new(args, splat_name, splat, kwargs)
|
||||
end
|
||||
|
||||
# Instance of empty arguments.
|
||||
class_getter none : AbstractArguments = build
|
||||
|
||||
# Returns the positional argument at the specified index.
|
||||
def [](index : Int)
|
||||
positional[index]
|
||||
end
|
||||
|
||||
# Returns the specified named argument.
|
||||
def [](arg : Symbol)
|
||||
return @args[arg] if @args.has_key?(arg)
|
||||
@kwargs[arg]
|
||||
end
|
||||
|
||||
# Returns all arguments and splatted arguments as a tuple.
|
||||
def positional : Tuple
|
||||
if (splat = @splat)
|
||||
args.values + splat
|
||||
else
|
||||
args.values
|
||||
end
|
||||
end
|
||||
|
||||
# Returns all named positional and keyword arguments as a named tuple.
|
||||
def named : NamedTuple
|
||||
args.merge(kwargs)
|
||||
end
|
||||
|
||||
# Constructs a string representation of the arguments.
|
||||
def to_s(io : IO) : Nil
|
||||
return io << "(no args)" if args.empty? && ((splat = @splat).nil? || splat.empty?) && kwargs.empty?
|
||||
|
||||
io << '('
|
||||
|
||||
# Add the positional arguments.
|
||||
{% if Args < NamedTuple %}
|
||||
# Include argument names.
|
||||
args.each_with_index do |name, value, i|
|
||||
io << ", " if i > 0
|
||||
io << name << ": "
|
||||
value.inspect(io)
|
||||
end
|
||||
{% else %}
|
||||
args.each_with_index do |arg, i|
|
||||
io << ", " if i > 0
|
||||
arg.inspect(io)
|
||||
end
|
||||
{% end %}
|
||||
|
||||
# Add the splat arguments.
|
||||
if (splat = @splat) && !splat.empty?
|
||||
io << ", " unless args.empty?
|
||||
if splat_name = !args.empty? && @splat_name
|
||||
io << '*' << splat_name << ": {"
|
||||
end
|
||||
splat.each_with_index do |arg, i|
|
||||
io << ", " if i > 0
|
||||
arg.inspect(io)
|
||||
end
|
||||
io << '}' if splat_name
|
||||
end
|
||||
|
||||
# Add the keyword arguments.
|
||||
offset = args.size
|
||||
offset += splat.size if (splat = @splat)
|
||||
kwargs.each_with_index(offset) do |key, value, i|
|
||||
io << ", " if i > 0
|
||||
io << key << ": "
|
||||
value.inspect(io)
|
||||
end
|
||||
|
||||
io << ')'
|
||||
end
|
||||
|
||||
# Checks if this set of arguments and another are equal.
|
||||
def ==(other : AbstractArguments)
|
||||
positional == other.positional && kwargs == other.kwargs
|
||||
end
|
||||
|
||||
# Checks if another set of arguments matches this set of arguments.
|
||||
def ===(other : Arguments)
|
||||
positional === other.positional && compare_named_tuples(kwargs, other.kwargs)
|
||||
end
|
||||
|
||||
# :ditto:
|
||||
def ===(other : FormalArguments)
|
||||
compare_named_tuples(args, other.args) && splat === other.splat && compare_named_tuples(kwargs, other.kwargs)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
require "./abstract_arguments"
|
||||
require "./arguments"
|
||||
require "./formal_arguments"
|
||||
|
||||
module Spectator
|
||||
# Stores information about a call to a method.
|
||||
|
@ -16,7 +17,14 @@ module Spectator
|
|||
|
||||
# Creates a method call by splatting its arguments.
|
||||
def self.capture(method : Symbol, *args, **kwargs)
|
||||
arguments = Arguments.new(args, kwargs).as(AbstractArguments)
|
||||
arguments = Arguments.capture(*args, **kwargs).as(AbstractArguments)
|
||||
new(method, arguments)
|
||||
end
|
||||
|
||||
# Creates a method call from within a method.
|
||||
# Takes the same arguments as `FormalArguments.build` but with the method name first.
|
||||
def self.build(method : Symbol, *args, **kwargs)
|
||||
arguments = FormalArguments.build(*args, **kwargs).as(AbstractArguments)
|
||||
new(method, arguments)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require "../dsl/reserved"
|
||||
require "./arguments"
|
||||
require "./formal_arguments"
|
||||
require "./method_call"
|
||||
require "./stub"
|
||||
require "./typed_stub"
|
||||
|
@ -140,7 +140,8 @@ module Spectator
|
|||
){% if method.return_type %} : {{method.return_type}}{% end %}{% if !method.free_vars.empty? %} forall {{method.free_vars.splat}}{% end %}
|
||||
|
||||
# Capture information about the call.
|
||||
%args = ::Spectator::Arguments.build(
|
||||
%call = ::Spectator::MethodCall.build(
|
||||
{{method.name.symbolize}},
|
||||
::NamedTuple.new(
|
||||
{% for arg, i in method.args %}{% if !method.splat_index || i < method.splat_index %}{{arg.internal_name.stringify}}: {{arg.internal_name}}, {% end %}{% end %}
|
||||
),
|
||||
|
@ -149,7 +150,6 @@ module Spectator
|
|||
{% for arg, i in method.args %}{% if method.splat_index && i > method.splat_index %}{{arg.internal_name.stringify}}: {{arg.internal_name}}, {% end %}{% end %}
|
||||
).merge({{method.double_splat}})
|
||||
)
|
||||
%call = ::Spectator::MethodCall.new({{method.name.symbolize}}, %args)
|
||||
_spectator_record_call(%call)
|
||||
|
||||
# Attempt to find a stub that satisfies the method call and arguments.
|
||||
|
@ -242,7 +242,8 @@ module Spectator
|
|||
){% if method.return_type %} : {{method.return_type}}{% end %}{% if !method.free_vars.empty? %} forall {{method.free_vars.splat}}{% end %}
|
||||
|
||||
# Capture information about the call.
|
||||
%args = ::Spectator::Arguments.build(
|
||||
%call = ::Spectator::MethodCall.build(
|
||||
{{method.name.symbolize}},
|
||||
::NamedTuple.new(
|
||||
{% for arg, i in method.args %}{% if !method.splat_index || i < method.splat_index %}{{arg.internal_name.stringify}}: {{arg.internal_name}}, {% end %}{% end %}
|
||||
),
|
||||
|
@ -251,7 +252,6 @@ module Spectator
|
|||
{% for arg, i in method.args %}{% if method.splat_index && i > method.splat_index %}{{arg.internal_name.stringify}}: {{arg.internal_name}}, {% end %}{% end %}
|
||||
).merge({{method.double_splat}})
|
||||
)
|
||||
%call = ::Spectator::MethodCall.new({{method.name.symbolize}}, %args)
|
||||
_spectator_record_call(%call)
|
||||
|
||||
# Attempt to find a stub that satisfies the method call and arguments.
|
||||
|
|
Loading…
Reference in a new issue