class RSpec::Matchers::BuiltIn::ContainExactly

rubocop:disable Metrics/ClassLength @api private Provides the implementation for ‘contain_exactly` and `match_array`. Not intended to be instantiated directly.

Public Instance Methods

description() click to toggle source

@api private @return [String]

# File lib/rspec/matchers/built_in/contain_exactly.rb, line 29
def description
  list = EnglishPhrasing.list(surface_descriptions_in(expected))
  "contain exactly#{list}"
end
failure_message() click to toggle source

@api private @return [String]

# File lib/rspec/matchers/built_in/contain_exactly.rb, line 11
def failure_message
  if Array === actual
    generate_failure_message
  else
    "expected a collection that can be converted to an array with " \
    "`#to_ary` or `#to_a`, but got #{actual_formatted}"
  end
end
failure_message_when_negated() click to toggle source

@api private @return [String]

# File lib/rspec/matchers/built_in/contain_exactly.rb, line 22
def failure_message_when_negated
  list = EnglishPhrasing.list(surface_descriptions_in(expected))
  "expected #{actual_formatted} not to contain exactly#{list}"
end
matches?(actual) click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 34
def matches?(actual)
  @pairings_maximizer = nil
  @best_solution = nil
  @extra_items = nil
  @missing_items = nil
  super(actual)
end

Private Instance Methods

actual_collection_line() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 56
def actual_collection_line
  message_line('actual collection contained', actual)
end
best_solution() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 137
def best_solution
  @best_solution ||= pairings_maximizer.find_best_solution
end
convert_actual_to_an_array() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 94
def convert_actual_to_an_array
  if actual.respond_to?(:to_ary)
    @actual = actual.to_ary
  elsif actual.respond_to?(:to_a) && !to_a_disallowed?(actual)
    @actual = actual.to_a
  else
    false
  end
end
describe_collection(collection, surface_descriptions=false) click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 68
def describe_collection(collection, surface_descriptions=false)
  if surface_descriptions
    "#{description_of(safe_sort(surface_descriptions_in collection))}\n"
  else
    "#{description_of(safe_sort(collection))}\n"
  end
end
expected_collection_line() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 52
def expected_collection_line
  message_line('expected collection contained', expected, true)
end
extra_elements_line() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 64
def extra_elements_line
  message_line('the extra elements were', extra_items)
end
extra_items() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 131
def extra_items
  @extra_items ||= best_solution.unmatched_actual_indexes.map do |index|
    actual[index]
  end
end
generate_failure_message() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 44
def generate_failure_message
  message = expected_collection_line
  message += actual_collection_line
  message += missing_elements_line unless missing_items.empty?
  message += extra_elements_line unless extra_items.empty?
  message
end
match(_expected, _actual) click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 81
def match(_expected, _actual)
  return false unless convert_actual_to_an_array
  match_when_sorted? || (extra_items.empty? && missing_items.empty?)
end
match_when_sorted?() click to toggle source

This cannot always work (e.g. when dealing with unsortable items, or matchers as expected items), but it’s practically free compared to the slowness of the full matching algorithm, and in common cases this works, so it’s worth a try.

# File lib/rspec/matchers/built_in/contain_exactly.rb, line 90
def match_when_sorted?
  values_match?(safe_sort(expected), safe_sort(actual))
end
message_line(prefix, collection, surface_descriptions=false) click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 76
def message_line(prefix, collection, surface_descriptions=false)
  "%-32s%s" % [prefix + ':',
               describe_collection(collection, surface_descriptions)]
end
missing_elements_line() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 60
def missing_elements_line
  message_line('the missing elements were', missing_items, true)
end
missing_items() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 125
def missing_items
  @missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
    expected[index]
  end
end
pairings_maximizer() click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 141
def pairings_maximizer
  @pairings_maximizer ||= begin
    expected_matches = Hash[Array.new(expected.size) { |i| [i, []] }]
    actual_matches   = Hash[Array.new(actual.size)   { |i| [i, []] }]

    expected.each_with_index do |e, ei|
      actual.each_with_index do |a, ai|
        next unless values_match?(e, a)

        expected_matches[ei] << ai
        actual_matches[ai] << ei
      end
    end

    PairingsMaximizer.new(expected_matches, actual_matches)
  end
end
safe_sort(array) click to toggle source
# File lib/rspec/matchers/built_in/contain_exactly.rb, line 104
def safe_sort(array)
  array.sort
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
  array
end
to_a_disallowed?(object) click to toggle source

:nocov:

# File lib/rspec/matchers/built_in/contain_exactly.rb, line 112
def to_a_disallowed?(object)
  case object
  when NilClass, String then true
  else Kernel == RSpec::Support.method_handle_for(object, :to_a).owner
  end
end