module RSpec::Matchers::Composable

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.

@api public

Constants

DescribableItem

Wraps an item in order to surface its ‘description` via `inspect`. @api private

Public Class Methods

@api private We should enumerate arrays as long as they are not recursive.

# File rspec-expectations/lib/rspec/matchers/composable.rb, line 142
def should_enumerate?(item)
  Array === item && item.none? { |subitem| subitem.equal?(item) }
end

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.

@!visibility public

# 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

@api private

# 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

Public Instance Methods

Alias for: and

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

# File rspec-expectations/lib/rspec/matchers/composable.rb, line 45
def ===(value)
  matches?(value)
end
Alias for: or

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.

@example

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

@note The negative form (‘expect(…).not_to matcher.and other`)

is not supported at this time.
# File rspec-expectations/lib/rspec/matchers/composable.rb, line 22
def and(matcher)
  BuiltIn::Compound::And.new self, matcher
end
Also aliased as: &

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.

@example

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

@note The negative form (‘expect(…).not_to matcher.or other`)

is not supported at this time.
# File rspec-expectations/lib/rspec/matchers/composable.rb, line 38
def or(matcher)
  BuiltIn::Compound::Or.new self, matcher
end
Also aliased as: |