module RSpec module Matchers module BuiltIn # @api private # Provides the implementation for dynamic predicate matchers. # Not intended to be inherited directly. class DynamicPredicate < BaseMatcher include BeHelpers def initialize(method_name, *args, &block) @method_name, @args, @block = method_name, args, block end ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true) # @private def matches?(actual, &block) @actual = actual @block ||= block predicate_accessible? && predicate_matches? end # @private def does_not_match?(actual, &block) @actual = actual @block ||= block predicate_accessible? && predicate_matches?(false) end # @api private # @return [String] def failure_message failure_message_expecting(true) end # @api private # @return [String] def failure_message_when_negated failure_message_expecting(false) end # @api private # @return [String] def description "#{method_description}#{args_to_sentence}" end private def predicate_accessible? @actual.respond_to? predicate end # support 1.8.7, evaluate once at load time for performance if String === methods.first # :nocov: def private_predicate? @actual.private_methods.include? predicate.to_s end # :nocov: else def private_predicate? @actual.private_methods.include? predicate end end def predicate_result @predicate_result = actual.__send__(predicate_method_name, *@args, &@block) end def predicate_method_name predicate end def predicate_matches?(value=true) if RSpec::Expectations.configuration.strict_predicate_matchers? value == predicate_result else value == !!predicate_result end end def root # On 1.9, there appears to be a bug where String#match can return `false` # rather than the match data object. Changing to Regex#match appears to # work around this bug. For an example of this bug, see: # https://travis-ci.org/rspec/rspec-expectations/jobs/27549635 self.class::REGEX.match(@method_name.to_s).captures.first end def method_description EnglishPhrasing.split_words(@method_name) end def failure_message_expecting(value) validity_message || "expected `#{actual_formatted}.#{predicate}#{args_to_s}` to #{expectation_of value}, got #{description_of @predicate_result}" end def expectation_of(value) if RSpec::Expectations.configuration.strict_predicate_matchers? "return #{value}" elsif value "be truthy" else "be falsey" end end def validity_message return nil if predicate_accessible? "expected #{actual_formatted} to respond to `#{predicate}`#{failure_to_respond_explanation}" end def failure_to_respond_explanation if private_predicate? " but `#{predicate}` is a private method" end end end # @api private # Provides the implementation for `has_`. # Not intended to be instantiated directly. class Has < DynamicPredicate # :nodoc: REGEX = Matchers::HAS_REGEX private def predicate @predicate ||= :"has_#{root}?" end end # @api private # Provides the implementation of `be_`. # Not intended to be instantiated directly. class BePredicate < DynamicPredicate # :nodoc: REGEX = Matchers::BE_PREDICATE_REGEX private def predicate @predicate ||= :"#{root}?" end def predicate_method_name actual.respond_to?(predicate) ? predicate : present_tense_predicate end def failure_to_respond_explanation super || if predicate == :true? " or perhaps you meant `be true` or `be_truthy`" elsif predicate == :false? " or perhaps you meant `be false` or `be_falsey`" end end def predicate_accessible? super || actual.respond_to?(present_tense_predicate) end def present_tense_predicate :"#{root}s?" end end end end end