From 81e20b13eca11b3c968a5532f5bc2946dfac6bd7 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 6 Mar 2019 11:05:13 -0700 Subject: [PATCH] Update PredicateMatcher to use MatchData --- spec/matchers/predicate_matcher_spec.cr | 120 +++++++++++++------- src/spectator/matchers/predicate_matcher.cr | 50 +++++--- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/spec/matchers/predicate_matcher_spec.cr b/spec/matchers/predicate_matcher_spec.cr index eb64f65..e71babc 100644 --- a/spec/matchers/predicate_matcher_spec.cr +++ b/spec/matchers/predicate_matcher_spec.cr @@ -1,57 +1,89 @@ require "../spec_helper" describe Spectator::Matchers::PredicateMatcher do - describe "#match?" do - context "with a true predicate" do - it "is true" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new - matcher.match?(partial).should be_true + describe "#match" do + context "returned MatchData" do + describe "#match?" do + context "with a true predicate" do + it "is true" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new + match_data = matcher.match(partial) + match_data.matched?.should be_true + end + end + + context "with a false predicate" do + it "is false" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(empty: Nil)).new + match_data = matcher.match(partial) + match_data.matched?.should be_false + end + end end - end - context "with a false predicate" do - it "is false" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(empty: Nil)).new - matcher.match?(partial).should be_false + describe "#values" do + it "contains a key for each expected attribute" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(empty: Nil, ascii_only: Nil)).new + match_data = matcher.match(partial) + values = match_data.values + values.has_key?(:empty).should be_true + values.has_key?(:ascii_only).should be_true + end + + it "has the actual values" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(empty: Nil, ascii_only: Nil)).new + match_data = matcher.match(partial) + values = match_data.values + values.[:empty].should eq(value.empty?) + values.[:ascii_only].should eq(value.ascii_only?) + end end - end - end - describe "#message" do - it "contains the actual label" do - value = "foobar" - label = "blah" - partial = new_partial(value, label) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new - matcher.message(partial).should contain(label) - end + describe "#message" do + it "contains the actual label" do + value = "foobar" + label = "blah" + partial = new_partial(value, label) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new + match_data = matcher.match(partial) + match_data.message.should contain(label) + end - it "contains stringified form of predicate" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new - matcher.message(partial).should contain("ascii_only") - end - end + it "contains stringified form of predicate" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new + match_data = matcher.match(partial) + match_data.message.should contain("ascii_only") + end + end - describe "#negated_message" do - it "contains the actual label" do - value = "foobar" - label = "blah" - partial = new_partial(value, label) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new - matcher.negated_message(partial).should contain(label) - end + describe "#negated_message" do + it "contains the actual label" do + value = "foobar" + label = "blah" + partial = new_partial(value, label) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new + match_data = matcher.match(partial) + match_data.negated_message.should contain(label) + end - it "contains stringified form of predicate" do - value = "foobar" - partial = new_partial(value) - matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new - matcher.negated_message(partial).should contain("ascii_only") + it "contains stringified form of predicate" do + value = "foobar" + partial = new_partial(value) + matcher = Spectator::Matchers::PredicateMatcher(NamedTuple(ascii_only: Nil)).new + match_data = matcher.match(partial) + match_data.negated_message.should contain("ascii_only") + end + end end end end diff --git a/src/spectator/matchers/predicate_matcher.cr b/src/spectator/matchers/predicate_matcher.cr index d5a7f2c..4208ee1 100644 --- a/src/spectator/matchers/predicate_matcher.cr +++ b/src/spectator/matchers/predicate_matcher.cr @@ -12,13 +12,10 @@ module Spectator::Matchers end # Determines whether the matcher is satisfied with the value given to it. - # True is returned if the match was successful, false otherwise. - def match?(partial) - actual = partial.actual - + private def match?(values) # Test each predicate and immediately return false if one is false. {% for attribute in ExpectedType.keys %} - return false unless actual.{{attribute}}? + return false unless values[{{attribute.symbolize}}] {% end %} # All checks passed if this point is reached. @@ -28,19 +25,44 @@ module Spectator::Matchers # Determines whether the matcher is satisfied with the partial given to it. # `MatchData` is returned that contains information about the match. def match(partial) : MatchData - raise NotImplementedError.new("#match") + values = snapshot_values(partial.actual) + MatchData.new(match?(values), values, partial.label) end - # Describes the condition that satisfies the matcher. - # This is informational and displayed to the end-user. - def message(partial) - "Expected #{partial.label} to be #{label}" + # Captures all of the actual values. + # A `NamedTuple` is returned, + # with each key being the attribute. + private def snapshot_values(actual) + {% begin %} + { + {% for attribute in ExpectedType.keys %} + {{attribute}}: actual.{{attribute}}?, + {% end %} + } + {% end %} end - # Describes the condition that won't satsify the matcher. - # This is informational and displayed to the end-user. - def negated_message(partial) - "Expected #{partial.label} to not be #{label}" + # Match data specific to this matcher. + private struct MatchData(ActualType) < MatchData + # Creates the match data. + def initialize(matched, @values : ActualType, @actual_label : String) + super(matched) + end + + # Information about the match. + getter values + + # Describes the condition that satisfies the matcher. + # This is informational and displayed to the end-user. + def message + "#{@actual_label} is " + {{ActualType.keys.splat.stringify}} + end + + # Describes the condition that won't satsify the matcher. + # This is informational and displayed to the end-user. + def negated_message + "#{@actual_label} is not " + {{ActualType.keys.splat.stringify}} + end end end end