lib/rspec/matchers/built_in/start_or_end_with.rb
module RSpec
module Matchers
module BuiltIn
# @api private
# Base class for the `end_with` and `start_with` matchers.
# Not intended to be instantiated directly.
class StartOrEndWith < BaseMatcher
def initialize(*expected)
@actual_does_not_have_ordered_elements = false
@expected = expected.length == 1 ? expected.first : expected
end
# @api private
# @return [String]
def failure_message
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 #[]"
end
end
end
# @api private
# @return [String]
def description
return super unless Hash === expected
english_name = EnglishPhrasing.split_words(self.class.matcher_name)
description_of_expected = surface_descriptions_in(expected).inspect
"#{english_name} #{description_of_expected}"
end
private
def match(_expected, actual)
return false unless actual.respond_to?(:[])
begin
return true if subsets_comparable? && subset_matches?
element_matches?
rescue ArgumentError
@actual_does_not_have_ordered_elements = true
return false
end
end
def subsets_comparable?
# Structs support the Enumerable interface but don't really have
# the semantics of a subset of a larger set...
return false if Struct === expected
expected.respond_to?(:length)
end
end
# For RSpec 3.1, the base class was named `StartAndEndWith`. For SemVer reasons,
# we still provide this constant until 4.0.
# @deprecated Use StartOrEndWith instead.
# @private
StartAndEndWith = StartOrEndWith
# @api private
# Provides the implementation for `start_with`.
# Not intended to be instantiated directly.
class StartWith < StartOrEndWith
private
def subset_matches?
values_match?(expected, actual[0, expected.length])
end
def element_matches?
values_match?(expected, actual[0])
end
end
# @api private
# Provides the implementation for `end_with`.
# Not intended to be instantiated directly.
class EndWith < StartOrEndWith
private
def subset_matches?
values_match?(expected, actual[-expected.length, expected.length])
end
def element_matches?
values_match?(expected, actual[-1])
end
end
end
end
end