forked from vcr/vcr
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathrequest_matcher_registry.rb
119 lines (101 loc) · 3.63 KB
/
request_matcher_registry.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
require 'vcr/errors'
module VCR
# Keeps track of the different request matchers.
class RequestMatcherRegistry
# The default request matchers used for any cassette that does not
# specify request matchers.
DEFAULT_MATCHERS = [:method, :uri]
# @private
class Matcher < Struct.new(:callable)
def matches?(request_1, request_2)
callable.call(request_1, request_2)
end
end
# @private
class URIWithoutParamsMatcher < Struct.new(:params_to_ignore)
def partial_uri_from(request)
URI(request.uri).tap do |uri|
break unless uri.query # ignore uris without params, e.g. "http://example.com/"
uri.query = uri.query.split('&').tap { |params|
params.map! do |p|
key, value = p.split('=')
key.gsub!(/\[\]\z/, '') # handle params like tag[]=
[key, value]
end
params.reject! { |p| params_to_ignore.include?(p.first) }
params.map! { |p| p.join('=') }
}.join('&')
end
end
def call(request_1, request_2)
partial_uri_from(request_1) == partial_uri_from(request_2)
end
def to_proc
lambda { |r1, r2| call(r1, r2) }
end
end
# @private
def initialize
@registry = {}
register_built_ins
end
# @private
def register(name, &block)
if @registry.has_key?(name)
warn "WARNING: There is already a VCR request matcher registered for #{name.inspect}. Overriding it."
end
@registry[name] = Matcher.new(block)
end
# @private
def [](matcher)
@registry.fetch(matcher) do
matcher.respond_to?(:call) ?
Matcher.new(matcher) :
raise_unregistered_matcher_error(matcher)
end
end
# Builds a dynamic request matcher that matches on a URI while ignoring the
# named query parameters. This is useful for dealing with non-deterministic
# URIs (i.e. that have a timestamp or request signature parameter).
#
# @example
# without_timestamp = VCR.request_matchers.uri_without_param(:timestamp)
#
# # use it directly...
# VCR.use_cassette('example', :match_requests_on => [:method, without_timestamp]) { }
#
# # ...or register it as a named matcher
# VCR.configure do |c|
# c.register_request_matcher(:uri_without_timestamp, &without_timestamp)
# end
#
# VCR.use_cassette('example', :match_requests_on => [:method, :uri_without_timestamp]) { }
#
# @param ignores [Array<#to_s>] The names of the query parameters to ignore
# @return [#call] the request matcher
def uri_without_params(*ignores)
uri_without_param_matchers[ignores]
end
alias uri_without_param uri_without_params
private
def uri_without_param_matchers
@uri_without_param_matchers ||= Hash.new do |hash, params|
params = params.map(&:to_s)
hash[params] = URIWithoutParamsMatcher.new(params)
end
end
def raise_unregistered_matcher_error(name)
raise Errors::UnregisteredMatcherError.new \
"There is no matcher registered for #{name.inspect}. " +
"Did you mean one of #{@registry.keys.map(&:inspect).join(', ')}?"
end
def register_built_ins
register(:method) { |r1, r2| r1.method == r2.method }
register(:uri) { |r1, r2| r1.uri == r2.uri }
register(:host) { |r1, r2| URI(r1.uri).host == URI(r2.uri).host }
register(:path) { |r1, r2| URI(r1.uri).path == URI(r2.uri).path }
register(:body) { |r1, r2| r1.body == r2.body }
register(:headers) { |r1, r2| r1.headers == r2.headers }
end
end
end