module FakeWebMatcher # Matcher class, following RSpec's expectations. Used to confirm whether a # request has been made on a given method and URI. # # Monkey patch to add a normalize! method to the URI::HTTP class. # It doesn't currently sort the query params which means two URLs # that are equivalent except for a different order of query params # will be !=. This isn't how FakeWeb works, so we patch it here. class ::URI::HTTP # Normalize an HTTP URI so that query param order differences don't # affect equality. def normalize! if query query_array = self.query.split('&') set_query(query_array.sort.join('&')) end super end end class RequestMatcher attr_reader :url, :method # Create a new matcher. # # @param [Symbol] method The HTTP method. Defaults to :any if not supplied. # @param [String, Regexp] uri The URI to check for # def initialize(*args) @method, @url = args_split(*args) end # Indication of whether there's a match on the URI from given requests. # # @param [Module] FakeWeb Module, necessary for RSpec, although not # required internally. # @return [Boolean] true if the URI was requested, otherwise false. # def matches?(fakeweb) !FakeWeb::Registry.instance.requests.detect { |req| method, url = args_split(*req) match_method(method) && match_url(url) }.nil? end # Failure message if the URI should have been requested. # # @return [String] failure message # def failure_message regex?(@url) ? regex_failure_message : url_failure_message end # Failure message if the URI should not have been requested. # # @return [String] failure message # def negative_failure_message regex?(@url) ? regex_negative_failure_message : url_negative_failure_message end private def regex_negative_failure_message "A URL that matches #{@url.inspect} was requested#{failure_message_method} and should not have been." end def url_negative_failure_message "The URL #{@url} was requested#{failure_message_method} and should not have been." end def regex_failure_message "A URL that matches #{@url.inspect} was not requested#{failure_message_method}." end def url_failure_message "The URL #{@url} was not requested#{failure_message_method}." end def failure_message_method " using #{formatted_method}" unless @method == :any end # Compares methods, or ignores if either side of the comparison is :any. # # @param [Symbol] method HTTP method # @return [Boolean] true if methods match or either is :any. # def match_method(method) @method == :any || method == :any || method == @method end # Compares the url either by match it agains a regular expression or # by simple comparison # # @param [String] url the called URI # @return [Boolean] true if exprexted URI and called URI match. # def match_url(url) return @url.match(url.to_s) if regex?(@url) @url == url end # Expected method formatted to be an uppercase string. Example: :get becomes # "GET". # # @return [String] uppercase method # def formatted_method @method.to_s.upcase end # Interprets given arguments to a method and URI instance. The URI, as a # string, is required, but the method is not (will default to :any). # # @param [Array] args # @return [Array] Two items: method and URI instance or regular expression # def args_split(*args) method = :any uri = nil case args.length when 1 then uri = args[0] when 2 then method, uri = args[0], args[1] else raise ArgumentError.new("wrong number of arguments") end uri = URI.parse(uri) unless regex?(uri) return method, uri end def regex?(object) object.is_a?(Regexp) end end end