fedux-org/proxy_pac_rb

View on GitHub
lib/proxy_pac_rb/rspec/matchers/base_matcher.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
module ProxyPacRb
  module RSpecMatchers
    # @api private
    #
    # Used _internally_ as a base class for matchers that ship with
    # rspec-expectations and rspec-rails.
    #
    # ### Warning:
    #
    # This class is for internal use, and subject to change without notice.  We
    # strongly recommend that you do not base your custom matchers on this
    # class. If/when this changes, we will announce it and remove this warning.
    class BaseMatcher
      include RSpec::Matchers::Composable

      # @api private
      # Used to detect when no arg is passed to `initialize`.
      # `nil` cannot be used because it's a valid value to pass.
      UNDEFINED = Object.new.freeze

      # @private
      attr_reader :actual, :expected, :rescued_exception

      def initialize(expected = UNDEFINED)
        @expected = expected unless UNDEFINED.equal?(expected)
      end

      # @api private
      # Indicates if the match is successful. Delegates to `match`, which
      # should be defined on a subclass. Takes care of consistently
      # initializing the `actual` attribute.
      def matches?(actual)
        @actual = actual
        match(expected, actual)
      end

      # @api private
      # Used to wrap a block of code that will indicate failure by
      # raising one of the named exceptions.
      #
      # This is used by rspec-rails for some of its matchers that
      # wrap rails' assertions.
      def match_unless_raises(*exceptions)
        exceptions.unshift Exception if exceptions.empty?
        begin
          yield
          true
        rescue *exceptions => @rescued_exception
          false
        end
      end

      # @api private
      # Generates a description using {EnglishPhrasing}.
      # @return [String]
      def description
        desc = self.class.matcher_name.to_s.tr('_', ' ')
        desc << Array(@expected).join(', ') if defined?(@expected) && @expected.is_a?(Array)
        desc
      end

      # @api private
      # Matchers are not diffable by default. Override this to make your
      # subclass diffable.
      def diffable?
        false
      end

      # @api private
      # Most matchers are value matchers (i.e. meant to work with `expect(value)`)
      # rather than block matchers (i.e. meant to work with `expect { }`), so
      # this defaults to false. Block matchers must override this to return true.
      def supports_block_expectations?
        false
      end

      # @api private
      def expects_call_stack_jump?
        false
      end

      # @private
      def expected_formatted
        RSpec::Support::ObjectInspector.inspect(@expected)
      end

      # @private
      def actual_formatted
        RSpec::Support::ObjectInspector.inspect(@actual)
      end

      # @private
      def self.matcher_name
        @matcher_name ||= underscore(name.split('::').last)
      end

      # @private
      # Borrowed from ActiveSupport.
      def self.underscore(camel_cased_word)
        word = camel_cased_word.to_s.dup
        word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
        word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
        word.tr!('-', '_')
        word.downcase!
        word
      end
      private_class_method :underscore

      private

      def assert_ivars(*expected_ivars)
        return unless (expected_ivars - present_ivars).any?
        ivar_list = EnglishPhrasing.list(expected_ivars)
        raise "#{self.class.name} needs to supply#{ivar_list}"
      end

      if RUBY_VERSION.to_f < 1.9
        # :nocov:
        def present_ivars
          instance_variables.map(&:to_sym)
        end
        # :nocov:
      else
        alias present_ivars instance_variables
      end

      # @private
      module HashFormatting
        # `{ :a => 5, :b => 2 }.inspect` produces:
        #
        #     {:a=>5, :b=>2}
        #
        # ...but it looks much better as:
        #
        #     {:a => 5, :b => 2}
        #
        # This is idempotent and safe to run on a string multiple times.
        def improve_hash_formatting(inspect_string)
          inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
        end
        module_function :improve_hash_formatting
      end

      include HashFormatting

      # @api private
      # Provides default implementations of failure messages, based on the `description`.
      module DefaultFailureMessages
        # @api private
        # Provides a good generic failure message. Based on `description`.
        # When subclassing, if you are not satisfied with this failure message
        # you often only need to override `description`.
        # @return [String]
        def failure_message
          "expected #{description_of @actual} to #{description}"
        end

        # @api private
        # Provides a good generic negative failure message. Based on `description`.
        # When subclassing, if you are not satisfied with this failure message
        # you often only need to override `description`.
        # @return [String]
        def failure_message_when_negated
          "expected #{description_of @actual} not to #{description}"
        end

        # @private
        # rubocop:disable Style/PredicateName
        def self.has_default_failure_messages?(matcher)
          matcher.method(:failure_message).owner == self &&
            matcher.method(:failure_message_when_negated).owner == self
        rescue NameError
          false
        end
        # rubocop:enable Style/PredicateName
      end

      include DefaultFailureMessages
    end
  end
end