Refactor StartWithMatcher

This commit is contained in:
Michael Miller 2019-08-08 16:04:09 -06:00
parent a3fa522bd4
commit cbaa9a4c43

View file

@ -4,85 +4,81 @@ module Spectator::Matchers
# Matcher that tests whether a value, such as a `String` or `Array`, starts with a value. # Matcher that tests whether a value, such as a `String` or `Array`, starts with a value.
# The `starts_with?` method is used if it's defined on the actual type. # The `starts_with?` method is used if it's defined on the actual type.
# Otherwise, it is treated as an `Enumerable` and the `first` value is compared against. # Otherwise, it is treated as an `Enumerable` and the `first` value is compared against.
struct StartWithMatcher(ExpectedType) < ValueMatcher(ExpectedType) struct StartWithMatcher(ExpectedType) < Matcher
# Determines whether the matcher is satisfied with the value given to it. private getter expected
private def match_starts_with?(actual)
actual.starts_with?(expected) def initialize(@expected : TestValue(ExpectedType))
end end
# Determines whether the matcher is satisfied with the value given to it. def description
private def match_first?(actual) "starts with #{expected.label}"
expected === actual
end end
# Determines whether the matcher is satisfied with the partial given to it. def match(actual)
def match(partial, negated = false) if actual.value.responds_to?(:starts_with?)
values = ExpectedActual.new(partial, self) match_starts_with(actual)
actual = values.actual
if actual.responds_to?(:starts_with?)
StartsWithMatchData.new(match_starts_with?(actual), values)
else else
first = actual.first match_first(actual)
FirstMatchData.new(match_first?(first), values, first)
end end
end end
# Match data specific to this matcher. private def match_starts_with(actual)
# This type is used when the actual value responds to `starts_with?`. if actual.value.starts_with?(expected.value)
private struct StartsWithMatchData(ExpectedType, ActualType) < MatchData SuccessfulMatchData.new
# Creates the match data. else
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType)) FailedMatchData.new("#{actual.label} does not start with #{expected.label} (using #starts_with?)",
super(matched) expected: expected.value.inspect,
end actual: actual.value.inspect
)
# Information about the match.
def named_tuple
{
expected: NegatableMatchDataValue.new(@values.expected),
actual: @values.actual,
}
end
# Describes the condition that satisfies the matcher.
# This is informational and displayed to the end-user.
def message
"#{@values.actual_label} starts with #{@values.expected_label} (using #starts_with?)"
end
# Describes the condition that won't satsify the matcher.
# This is informational and displayed to the end-user.
def negated_message
"#{@values.actual_label} does not start with #{@values.expected_label} (using #starts_with?)"
end end
end end
# Match data specific to this matcher. private def match_last(actual)
# This type is used when the actual value does not respond to `ends_with?`. list = actual.value.to_a
private struct FirstMatchData(ExpectedType, ActualType, FirstType) < MatchData first = list.first
# Creates the match data.
def initialize(matched, @values : ExpectedActual(ExpectedType, ActualType), @first : FirstType)
super(matched)
end
# Information about the match. if expected.value === first
def named_tuple SuccessfulMatchData.new
{ else
expected: @values.expected, FailedMatchData.new("#{actual.label} does not start with #{expected.label} (using expected === first)",
actual: @first, expected: expected.value,
list: @values.actual, actual: first,
} list: list
)
end end
end
# Describes the condition that satisfies the matcher. def negated_match(actual)
# This is informational and displayed to the end-user. if actual.value.responds_to?(:starts_with?)
def message negated_match_starts_with(actual)
"#{@values.actual_label} starts with #{@values.expected_label} (using expected === actual.first)" else
negated_match_first(actual)
end end
end
# Describes the condition that won't satsify the matcher. private def negated_match_starts_with(actual)
# This is informational and displayed to the end-user. if actual.value.starts_with?(expected.value)
def negated_message FailedMatchData.new("#{actual.label} starts with #{expected.label} (using #starts_with?)",
"#{@values.actual_label} does not start with #{@values.expected_label} (using expected === actual.first)" expected: expected.value.inspect,
actual: actual.value.inspect
)
else
SuccessfulMatchData.new
end
end
private def negated_match_first(actual)
list = actual.value.to_a
first = list.first
if expected.value === first
FailedMatchData.new("#{actual.label} starts with #{expected.label} (using expected === first)",
expected: expected.value,
actual: first,
list: list
)
else
SuccessfulMatchData.new
end end
end end
end end