123456789_123456789_123456789_123456789_123456789_

Module: RSpec::Matchers::Composable

Relationships & Source Files
Namespace Children
Classes:
Extension / Inclusion / Inheritance Descendants
Included In:
::RSpec::Expectations::LegacyMatcherAdapter, ::RSpec::Expectations::LegacyMatcherAdapter::RSpec1, ::RSpec::Expectations::LegacyMatcherAdapter::RSpec2, AliasedMatcher, AliasedMatcherWithOperatorSupport, AliasedNegatedMatcher, BuiltIn::All, BuiltIn::BaseMatcher, BuiltIn::Be, BuiltIn::BeAKindOf, BuiltIn::BeAnInstanceOf, BuiltIn::BeBetween, BuiltIn::BeComparedTo, BuiltIn::BeFalsey, BuiltIn::BeNil, BuiltIn::BePredicate, BuiltIn::BeTruthy, BuiltIn::BeWithin, BuiltIn::Change, BuiltIn::ChangeFromValue, BuiltIn::ChangeRelatively, BuiltIn::ChangeToValue, BuiltIn::Compound, BuiltIn::Compound::And, BuiltIn::Compound::Or, BuiltIn::ContainExactly, BuiltIn::Cover, BuiltIn::DynamicPredicate, BuiltIn::EndWith, BuiltIn::Eq, BuiltIn::Eql, BuiltIn::Equal, BuiltIn::Exist, BuiltIn::Has, BuiltIn::HaveAttributes, BuiltIn::Include, BuiltIn::Match, BuiltIn::Output, BuiltIn::RaiseError, BuiltIn::RespondTo, BuiltIn::Satisfy, BuiltIn::SpecificValuesChange, BuiltIn::StartOrEndWith, BuiltIn::StartWith, BuiltIn::ThrowSymbol, BuiltIn::YieldControl, BuiltIn::YieldSuccessiveArgs, BuiltIn::YieldWithArgs, BuiltIn::YieldWithNoArgs, DSL::Matcher, MatcherDelegator, ::RSpec::Rails::Matchers::ActionCable::HaveBroadcastedTo, ::RSpec::Rails::Matchers::ActionCable::HaveStream, ::RSpec::Rails::Matchers::ActionMailbox::Base, ::RSpec::Rails::Matchers::ActionMailbox::ReceiveInboundEmail, ::RSpec::Rails::Matchers::ActiveJob::Base, ::RSpec::Rails::Matchers::ActiveJob::HaveBeenEnqueued, ::RSpec::Rails::Matchers::ActiveJob::HaveBeenPerformed, ::RSpec::Rails::Matchers::ActiveJob::HaveEnqueuedJob, ::RSpec::Rails::Matchers::ActiveJob::HavePerformedJob, ::RSpec::Rails::Matchers::BaseMatcher, ::RSpec::Rails::Matchers::BeANew, ::RSpec::Rails::Matchers::BeANewRecord, ::RSpec::Rails::Matchers::BeValid, ::RSpec::Rails::Matchers::HaveEnqueuedMail, ::RSpec::Rails::Matchers::HaveHttpStatus::GenericStatus, ::RSpec::Rails::Matchers::HaveHttpStatus::NumericCode, ::RSpec::Rails::Matchers::HaveHttpStatus::SymbolicStatus, ::RSpec::Rails::Matchers::RedirectTo::RedirectTo, ::RSpec::Rails::Matchers::RenderTemplate::RenderTemplateMatcher, ::RSpec::Rails::Matchers::RoutingMatchers::BeRoutableMatcher, ::RSpec::Rails::Matchers::RoutingMatchers::RouteToMatcher, ::RSpec::Rails::Matchers::SendEmail
Defined in: rspec-expectations/lib/rspec/matchers/composable.rb

Overview

Mixin designed to support the composable matcher features of ::RSpec 3+. Mix it into your custom matcher classes to allow them to be used in a composable fashion.

Class Method Summary

Instance Method Summary

Class Method Details

.should_enumerate?(item) ⇒ Boolean (mod_func)

This method is for internal use only.

We should enumerate arrays as long as they are not recursive.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 142

def should_enumerate?(item)
  Array === item && item.none? { |subitem| subitem.equal?(item) }
end

.surface_descriptions_in(item) (mod_func)

Transforms the given data structure (typically a hash or array) into a new data structure that, when ‘#inspect` is called on it, will provide descriptions of any contained matchers rather than the normal #inspect output.

You are encouraged to use this in your custom matcher’s ‘description`, failure_message or failure_message_when_negated implementation if you are supporting any arguments which may be a data structure containing matchers.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 98

def surface_descriptions_in(item)
  if Matchers.is_a_describable_matcher?(item)
    DescribableItem.new(item)
  elsif Hash === item
    Hash[surface_descriptions_in(item.to_a)]
  elsif Struct === item || unreadable_io?(item)
    RSpec::Support::ObjectFormatter.format(item)
  elsif should_enumerate?(item)
    item.map { |subitem| surface_descriptions_in(subitem) }
  else
    item
  end
end

.unreadable_io?(object) ⇒ Boolean (mod_func)

This method is for internal use only.
[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 147

def unreadable_io?(object)
  return false unless IO === object
  object.each {} # STDOUT is enumerable but raises an error
  false
rescue IOError
  true
end

Instance Method Details

#&(matcher)

Alias for #and.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 25

alias & and

#===(value)

Delegates to ‘#matches?`. Allows matchers to be used in composable fashion and also supports using matchers in case statements.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 45

def ===(value)
  matches?(value)
end

#and(matcher) Also known as: #&

Note:

The negative form (‘expect(…).not_to matcher.and other`) is not supported at this time.

Creates a compound ‘and` expectation. The matcher will only pass if both sub-matchers pass. This can be chained together to form an arbitrarily long chain of matchers.

Examples:

expect(alphabet).to start_with("a").and end_with("z")
expect(alphabet).to start_with("a") & end_with("z")
[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 22

def and(matcher)
  BuiltIn::Compound::And.new self, matcher
end

#description_of(object)

Returns the description of the given object in a way that is aware of composed matchers. If the object is a matcher with a ‘description` method, returns the description; otherwise returns object.inspect.

You are encouraged to use this in your custom matcher’s ‘description`, failure_message or failure_message_when_negated implementation if you are supporting matcher arguments.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 82

def description_of(object)
  RSpec::Support::ObjectFormatter.format(object)
end

#or(matcher) Also known as: #|

Note:

The negative form (‘expect(…).not_to matcher.or other`) is not supported at this time.

Creates a compound ‘or` expectation. The matcher will pass if either sub-matcher passes. This can be chained together to form an arbitrarily long chain of matchers.

Examples:

expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")
[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 38

def or(matcher)
  BuiltIn::Compound::Or.new self, matcher
end

#values_match?(expected, actual) ⇒ Boolean

This provides a generic way to fuzzy-match an expected value against an actual value. It understands nested data structures (e.g. hashes and arrays) and is able to match against a matcher being used as the expected value or within the expected value at any level of nesting.

Within a custom matcher you are encouraged to use this whenever your matcher needs to match two values, unless it needs more precise semantics. For example, the ‘eq` matcher _does not_ use this as it is meant to use == (and only ==) for matching.

Parameters:

  • expected (Object)

    what is expected

  • actual (Object)

    the actual value

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 66

def values_match?(expected, actual)
  expected = with_matchers_cloned(expected)
  Support::FuzzyMatcher.values_match?(expected, actual)
end

#with_matchers_cloned(object) (private)

This method is for internal use only.

Historically, a single matcher instance was only checked against a single value. Given that the matcher was only used once, it’s been common to memoize some intermediate calculation that is derived from the ‘actual` value in order to reuse that intermediate result in the failure message.

This can cause a problem when using such a matcher as an argument to another matcher in a composed matcher expression, since the matcher instance may be checked against multiple values and produce invalid results due to the memoization.

To deal with this, we clone any matchers in ‘expected` via this method when using #values_match?, so that any memoization does not “leak” between checks.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 128

def with_matchers_cloned(object)
  if Matchers.is_a_matcher?(object)
    object.clone
  elsif Hash === object
    Hash[with_matchers_cloned(object.to_a)]
  elsif should_enumerate?(object)
    object.map { |subobject| with_matchers_cloned(subobject) }
  else
    object
  end
end

#|(matcher)

Alias for #or.

[ GitHub ]

  
# File 'rspec-expectations/lib/rspec/matchers/composable.rb', line 41

alias | or