diff --git a/lib/rspec/matchers/built_in/start_or_end_with.rb b/lib/rspec/matchers/built_in/start_or_end_with.rb index 81f06c288..53a22ebc1 100644 --- a/lib/rspec/matchers/built_in/start_or_end_with.rb +++ b/lib/rspec/matchers/built_in/start_or_end_with.rb @@ -13,11 +13,12 @@ def initialize(*expected) # @api private # @return [String] def failure_message + response_msg = ", but it does not respond to #{method} and cannot be indexed using #[]" super.tap do |msg| if @actual_does_not_have_ordered_elements msg << ", but it does not have ordered elements" elsif !actual.respond_to?(:[]) - msg << ", but it cannot be indexed using #[]" + msg << response_msg end end end @@ -33,7 +34,9 @@ def description private - def match(_expected, actual) + def match(expected, actual) + # use an object's start_with? or end_with? as appropriate + return actual.send(method, expected) if actual.respond_to?(method) return false unless actual.respond_to?(:[]) begin @@ -73,6 +76,10 @@ def subset_matches? def element_matches? values_match?(expected, actual[0]) end + + def method + :start_with? + end end # @api private @@ -88,6 +95,10 @@ def subset_matches? def element_matches? values_match?(expected, actual[-1]) end + + def method + :end_with? + end end end end diff --git a/spec/rspec/matchers/built_in/start_and_end_with_spec.rb b/spec/rspec/matchers/built_in/start_and_end_with_spec.rb index b063f199d..02bed68c9 100644 --- a/spec/rspec/matchers/built_in/start_and_end_with_spec.rb +++ b/spec/rspec/matchers/built_in/start_and_end_with_spec.rb @@ -102,11 +102,25 @@ def ==(other) end context "with an object that does not respond to :[]" do - it "fails with a useful message" do - actual = Object.new - expect { - expect(actual).to start_with 0 - }.to fail_with("expected #{actual.inspect} to start with 0, but it cannot be indexed using #[]") + context "with an object that responds to start_with?" do + it "relies on start_with?" do + my_struct = Struct.new(:foo) do + def start_with?(elem) + true + end + end + + expect(my_struct.new("foo")).to start_with(0) + end + end + + context "with an object that does not respond to start_with?" do + it "fails with a useful message" do + actual = Object.new + expect { + expect(actual).to start_with 0 + }.to fail_with("expected #{actual.inspect} to start with 0, but it does not respond to start_with? and cannot be indexed using #[]") + end end end @@ -310,11 +324,24 @@ def ==(other) end context "with an object that does not respond to :[]" do - it "fails with a useful message" do - actual = Object.new - expect { - expect(actual).to end_with 0 - }.to fail_with("expected #{actual.inspect} to end with 0, but it cannot be indexed using #[]") + context "with an object that responds to end_with?" do + it "relies on end_with?" do + my_struct = Struct.new(:foo) do + def end_with?(elem) + true + end + end + expect(my_struct.new("foo")).to end_with(0) + end + end + + context "with an object that does not respond to end_with?" do + it "fails with a useful message" do + actual = Object.new + expect { + expect(actual).to end_with 0 + }.to fail_with("expected #{actual.inspect} to end with 0, but it does not respond to end_with? and cannot be indexed using #[]") + end end end