lib/typhoeus/expectation.rb



module Typhoeus

  # This class represents an expectation. It is part
  # of the stubbing mechanism. An expectation contains
  # an url and options, like a request. They were compared
  # to the request url and options in order to evaluate
  # wether they match. If thats the case, the attached
  # responses were returned one by one.
  #
  # @example Stub a request and get specified response.
  #   expected = Typhoeus::Response.new
  #   Typhoeus.stub("www.example.com").and_return(expected)
  #
  #   actual = Typhoeus.get("www.example.com")
  #   expected == actual
  #   #=> true
  class Expectation

    # @api private
    attr_reader :url

    # @api private
    attr_reader :options

    # @api private
    attr_reader :from

    class << self

      # Returns all expectations.
      #
      # @example Return expectations.
      #   Typhoeus::Expectation.all
      #
      # @return [ Array<Typhoeus::Expectation> ] The expectations.
      def all
        @expectations ||= []
      end

      # Clears expectations. This is handy while
      # testing and you want to make sure, that
      # you don't get canned responses.
      #
      # @example Clear expectations.
      #   Typhoeus::Expectation.clear
      def clear
        all.clear
      end

      # Returns expecation matching the provided
      # request.
      #
      # @example Find expectation.
      #   Typhoeus::Expectation.find_by(request)
      #
      # @return [ Expectation ] The matching expectation.
      #
      # @api private
      def find_by(request)
        all.find do |expectation|
          expectation.matches?(request)
        end
      end
    end

    # Creates an expactation.
    #
    # @example Create expactation.
    #   Typhoeus::Expectation.new(url)
    #
    # @return [ Expectation ] The created expactation.
    #
    # @api private
    def initialize(url, options = {})
      @url = url
      @options = options
      @response_counter = 0
      @from = nil
    end

    # Set from value to mark an expecation. Useful for
    # other libraries, eg. webmock.
    #
    # @example Mark expecation.
    #   expecation.from(:webmock)
    #
    # @param [ String ] value Value to set.
    #
    # @return [ Expectation ] Returns self.
    #
    # @api private
    def stubbed_from(value)
      @from = value
      self
    end

    # Specify what should be returned,
    # when this expactation is hit.
    #
    # @example Add response.
    #   expectation.and_return(response)
    #
    # @return [ void ]
    def and_return(response)
      responses << response
    end

    # Checks wether this expectation matches
    # the provided request.
    #
    # @example Check if request matches.
    #   expectation.matches? request
    #
    # @param [ Request ] request The request to check.
    #
    # @return [ Boolean ] True when matches, else false.
    #
    # @api private
    def matches?(request)
      url_match?(request.url) &&
        (options ? options.all?{ |k,v| request.original_options[k] == v } : true)
    end

    # Return canned responses.
    #
    # @example Return responses.
    #   expectation.responses
    #
    # @return [ Array<Typhoeus::Response> ] The responses.
    #
    # @api private
    def responses
      @responses ||= []
    end

    # Return the response. When there are
    # multiple responses, they were returned one
    # by one.
    #
    # @example Return response.
    #   expectation.response
    #
    # @return [ Response ] The response.
    #
    # @api private
    def response
      response = responses.fetch(@response_counter, responses.last)
      @response_counter += 1
      response.mock = @from || true
      response
    end

    private

    # Check wether the url matches the request url.
    # The url can be a string, regex or nil. String and
    # regexp were checked, nil is always true. Else false.
    #
    # Nil serves as a placeholder in case you want to match
    # all urls.
    def url_match?(request_url)
      case url
      when String
        url == request_url
      when Regexp
        !!request_url.match(url)
      when nil
        true
      else
        false
      end
    end
  end
end