Class: RSpec::Matchers::BuiltIn::ContainExactly::PairingsMaximizer

### Home Classes Methods

### In Files

- lib/rspec/matchers/built_in/contain_exactly.rb

### Parent

Object

### Methods

- ::new
- #find_best_solution

### Class/Module Index

Quicksearch

- RSpec
- RSpec::Expectations
- RSpec::Expectations::BlockExpectationTarget
- RSpec::Expectations::Configuration
- RSpec::Expectations::ExpectationHelper
- RSpec::Expectations::ExpectationNotMetError
- RSpec::Expectations::ExpectationTarget
- RSpec::Expectations::LegacyMacherAdapter
- RSpec::Expectations::LegacyMatcherAdapter
- RSpec::Expectations::LegacyMatcherAdapter::RSpec1
- RSpec::Expectations::LegacyMatcherAdapter::RSpec2
- RSpec::Expectations::NegativeExpectationHandler
- RSpec::Expectations::PositiveExpectationHandler
- RSpec::Expectations::Syntax
- RSpec::Expectations::Version
- RSpec::Matchers
- RSpec::Matchers::AliasedMatcher
- RSpec::Matchers::AliasedMatcherWithOperatorSupport
- RSpec::Matchers::AliasedNegatedMatcher
- RSpec::Matchers::BuiltIn
- RSpec::Matchers::BuiltIn::All
- RSpec::Matchers::BuiltIn::BaseMatcher
- RSpec::Matchers::BuiltIn::BaseMatcher::DefaultFailureMessages
- RSpec::Matchers::BuiltIn::Be
- RSpec::Matchers::BuiltIn::BeAKindOf
- RSpec::Matchers::BuiltIn::BeAnInstanceOf
- RSpec::Matchers::BuiltIn::BeBetween
- RSpec::Matchers::BuiltIn::BeComparedTo
- RSpec::Matchers::BuiltIn::BeFalsey
- RSpec::Matchers::BuiltIn::BeHelpers
- RSpec::Matchers::BuiltIn::BeNil
- RSpec::Matchers::BuiltIn::BePredicate
- RSpec::Matchers::BuiltIn::BeTruthy
- RSpec::Matchers::BuiltIn::BeWithin
- RSpec::Matchers::BuiltIn::CaptureStderr
- RSpec::Matchers::BuiltIn::CaptureStdout
- RSpec::Matchers::BuiltIn::CaptureStreamToTempfile
- RSpec::Matchers::BuiltIn::Change
- RSpec::Matchers::BuiltIn::ChangeDetails
- RSpec::Matchers::BuiltIn::ChangeFromValue
- RSpec::Matchers::BuiltIn::ChangeRelatively
- RSpec::Matchers::BuiltIn::ChangeToValue
- RSpec::Matchers::BuiltIn::Compound
- RSpec::Matchers::BuiltIn::Compound::And
- RSpec::Matchers::BuiltIn::Compound::NestedEvaluator
- RSpec::Matchers::BuiltIn::Compound::Or
- RSpec::Matchers::BuiltIn::Compound::SequentialEvaluator
- RSpec::Matchers::BuiltIn::ContainExactly
- RSpec::Matchers::BuiltIn::ContainExactly::PairingsMaximizer
- RSpec::Matchers::BuiltIn::Cover
- RSpec::Matchers::BuiltIn::EndWith
- RSpec::Matchers::BuiltIn::Eq
- RSpec::Matchers::BuiltIn::Eql
- RSpec::Matchers::BuiltIn::Equal
- RSpec::Matchers::BuiltIn::Exist
- RSpec::Matchers::BuiltIn::Exist::ExistenceTest
- RSpec::Matchers::BuiltIn::Has
- RSpec::Matchers::BuiltIn::HaveAttributes
- RSpec::Matchers::BuiltIn::Include
- RSpec::Matchers::BuiltIn::Match
- RSpec::Matchers::BuiltIn::NegativeOperatorMatcher
- RSpec::Matchers::BuiltIn::NullCapture
- RSpec::Matchers::BuiltIn::OperatorMatcher
- RSpec::Matchers::BuiltIn::Output
- RSpec::Matchers::BuiltIn::PositiveOperatorMatcher
- RSpec::Matchers::BuiltIn::RaiseError
- RSpec::Matchers::BuiltIn::RespondTo
- RSpec::Matchers::BuiltIn::Satisfy
- RSpec::Matchers::BuiltIn::SpecificValuesChange
- RSpec::Matchers::BuiltIn::StartAndEndWith
- RSpec::Matchers::BuiltIn::StartOrEndWith
- RSpec::Matchers::BuiltIn::StartWith
- RSpec::Matchers::BuiltIn::ThrowSymbol
- RSpec::Matchers::BuiltIn::YieldControl
- RSpec::Matchers::BuiltIn::YieldProbe
- RSpec::Matchers::BuiltIn::YieldSuccessiveArgs
- RSpec::Matchers::BuiltIn::YieldWithArgs
- RSpec::Matchers::BuiltIn::YieldWithNoArgs
- RSpec::Matchers::Composable
- RSpec::Matchers::DSL
- RSpec::Matchers::DSL::DefaultImplementations
- RSpec::Matchers::DSL::Macros
- RSpec::Matchers::DSL::Macros::Deprecated
- RSpec::Matchers::DSL::Matcher
- RSpec::Matchers::ExpectedsForMultipleDiffs
- RSpec::Matchers::MatcherDelegator
- RSpec::Matchers::MatcherProtocol
- RSpec::Matchers::Pretty
- BasicObject
- Object

No matching classes.

# RSpec::Matchers::BuiltIn::ContainExactly::PairingsMaximizer

Once we started supporting composing matchers, the algorithm for this matcher got much more complicated. Consider this expression:

    expect(["fool", "food"]).to contain_exactly(/foo/, /fool/)

This should pass (because we can pair /fool/ with "fool" and /foo/ with "food"), but the original algorithm used by this matcher would pair the first elements it could (/foo/ with "fool"), which would leave /fool/ and "food" unmatched. When we have an expected element which is a matcher that matches a superset of actual items compared to another expected element matcher, we need to consider every possible pairing.

This class is designed to maximize the number of actual/expected pairings -- or, conversely, to minimize the number of unpaired items. It's essentially a brute force solution, but with a few heuristics applied to reduce the size of the problem space:

    * Any items which match none of the items in the other list are immediately
      placed into the `unmatched_expected_indexes` or `unmatched_actual_indexes` array.
      The extra items and missing items in the matcher failure message are derived
      from these arrays.
    * Any items which reciprocally match only each other are paired up and not
      considered further.

What's left is only the items which match multiple items from the other list (or vice versa). From here, it performs a brute-force depth-first search, looking for a solution which pairs all elements in both lists, or, barring that, that produces the fewest unmatched items.

@private

### Constants

NullSolution

  @private Starting solution that is worse than any other real solution.

Solution

### Attributes

actual_to_expected_matched_indexes[R]
expected_to_actual_matched_indexes[R]
solution[R]

### Public Class Methods

new(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes) click to toggle source

    # File lib/rspec/matchers/built_in/contain_exactly.rb, line 163
    def initialize(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
      @expected_to_actual_matched_indexes = expected_to_actual_matched_indexes
      @actual_to_expected_matched_indexes = actual_to_expected_matched_indexes

      unmatched_expected_indexes, indeterminate_expected_indexes =
        categorize_indexes(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)

      unmatched_actual_indexes, indeterminate_actual_indexes =
        categorize_indexes(actual_to_expected_matched_indexes, expected_to_actual_matched_indexes)

      @solution = Solution.new(unmatched_expected_indexes,     unmatched_actual_indexes,
                               indeterminate_expected_indexes, indeterminate_actual_indexes)
    end

### Public Instance Methods

find_best_solution() click to toggle source

    # File lib/rspec/matchers/built_in/contain_exactly.rb, line 177
    def find_best_solution
      return solution if solution.candidate?
      best_solution_so_far = NullSolution

      expected_index = solution.indeterminate_expected_indexes.first
      actuals = expected_to_actual_matched_indexes[expected_index]

      actuals.each do |actual_index|
        solution = best_solution_for_pairing(expected_index, actual_index)
        return solution if solution.ideal?
        best_solution_so_far = solution if best_solution_so_far.worse_than?(solution)
      end

      best_solution_so_far
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.