vcr 2.0.0.beta1 → 2.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +37 -2
- data/Gemfile +2 -2
- data/README.md +10 -1
- data/Rakefile +43 -7
- data/Upgrade.md +45 -0
- data/features/.nav +1 -0
- data/features/cassettes/automatic_re_recording.feature +19 -17
- data/features/cassettes/dynamic_erb.feature +32 -28
- data/features/cassettes/exclusive.feature +28 -24
- data/features/cassettes/format.feature +213 -31
- data/features/cassettes/update_content_length_header.feature +20 -18
- data/features/configuration/filter_sensitive_data.feature +4 -4
- data/features/configuration/hooks.feature +27 -23
- data/features/http_libraries/em_http_request.feature +79 -75
- data/features/record_modes/all.feature +14 -14
- data/features/record_modes/new_episodes.feature +15 -15
- data/features/record_modes/none.feature +15 -15
- data/features/record_modes/once.feature +15 -15
- data/features/request_matching/body.feature +25 -23
- data/features/request_matching/custom_matcher.feature +25 -23
- data/features/request_matching/headers.feature +32 -36
- data/features/request_matching/host.feature +27 -25
- data/features/request_matching/identical_request_sequence.feature +27 -25
- data/features/request_matching/method.feature +27 -25
- data/features/request_matching/path.feature +27 -25
- data/features/request_matching/playback_repeats.feature +27 -25
- data/features/request_matching/uri.feature +27 -25
- data/features/request_matching/uri_without_param.feature +28 -26
- data/features/step_definitions/cli_steps.rb +71 -17
- data/features/support/env.rb +3 -1
- data/features/support/http_lib_filters.rb +6 -3
- data/features/support/vcr_cucumber_helpers.rb +4 -2
- data/lib/vcr.rb +6 -2
- data/lib/vcr/cassette.rb +75 -51
- data/lib/vcr/cassette/migrator.rb +111 -0
- data/lib/vcr/cassette/serializers.rb +35 -0
- data/lib/vcr/cassette/serializers/json.rb +23 -0
- data/lib/vcr/cassette/serializers/psych.rb +24 -0
- data/lib/vcr/cassette/serializers/syck.rb +35 -0
- data/lib/vcr/cassette/serializers/yaml.rb +24 -0
- data/lib/vcr/configuration.rb +6 -1
- data/lib/vcr/errors.rb +1 -1
- data/lib/vcr/library_hooks/excon.rb +1 -7
- data/lib/vcr/library_hooks/typhoeus.rb +6 -22
- data/lib/vcr/library_hooks/webmock.rb +1 -1
- data/lib/vcr/middleware/faraday.rb +1 -1
- data/lib/vcr/request_matcher_registry.rb +43 -30
- data/lib/vcr/structs.rb +209 -0
- data/lib/vcr/tasks/vcr.rake +9 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
- data/spec/fixtures/cassette_spec/example.yml +79 -78
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
- data/spec/fixtures/fake_example.com_responses.yml +78 -76
- data/spec/fixtures/match_requests_on.yml +147 -145
- data/spec/monkey_patches.rb +5 -5
- data/spec/support/http_library_adapters.rb +48 -0
- data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
- data/spec/support/sinatra_app.rb +12 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
- data/spec/vcr/cassette/migrator_spec.rb +183 -0
- data/spec/vcr/cassette/serializers_spec.rb +122 -0
- data/spec/vcr/cassette_spec.rb +147 -83
- data/spec/vcr/configuration_spec.rb +11 -1
- data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
- data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
- data/spec/vcr/request_ignorer_spec.rb +1 -1
- data/spec/vcr/request_matcher_registry_spec.rb +46 -4
- data/spec/vcr/structs_spec.rb +309 -0
- data/spec/vcr_spec.rb +7 -0
- data/vcr.gemspec +9 -12
- metadata +75 -61
- data/lib/vcr/structs/http_interaction.rb +0 -58
- data/lib/vcr/structs/normalizers/body.rb +0 -24
- data/lib/vcr/structs/normalizers/header.rb +0 -64
- data/lib/vcr/structs/normalizers/status_message.rb +0 -17
- data/lib/vcr/structs/normalizers/uri.rb +0 -34
- data/lib/vcr/structs/request.rb +0 -13
- data/lib/vcr/structs/response.rb +0 -13
- data/lib/vcr/structs/response_status.rb +0 -5
- data/lib/vcr/util/yaml.rb +0 -11
- data/spec/support/shared_example_groups/normalizers.rb +0 -94
- data/spec/vcr/structs/http_interaction_spec.rb +0 -89
- data/spec/vcr/structs/request_spec.rb +0 -39
- data/spec/vcr/structs/response_spec.rb +0 -44
- data/spec/vcr/structs/response_status_spec.rb +0 -9
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'vcr/structs'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module VCR
|
6
|
+
class Cassette
|
7
|
+
class Migrator
|
8
|
+
def initialize(dir, out = $stdout)
|
9
|
+
@dir, @out = dir, out
|
10
|
+
@yaml_load_errors = yaml_load_errors
|
11
|
+
end
|
12
|
+
|
13
|
+
def migrate!
|
14
|
+
@out.puts "Migrating VCR cassettes in #{@dir}..."
|
15
|
+
Dir["#{@dir}/**/*.yml"].each do |cassette|
|
16
|
+
migrate_cassette(cassette)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def migrate_cassette(cassette)
|
23
|
+
unless http_interactions = load_yaml(cassette)
|
24
|
+
@out.puts " - Ignored #{relative_casssette_name(cassette)} since it could not be parsed as YAML (does it have some ERB?)"
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
unless valid_vcr_1_cassette?(http_interactions)
|
29
|
+
@out.puts " - Ignored #{relative_casssette_name(cassette)} since it does not appear to be a valid VCR 1.x cassette"
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
http_interactions.map! do |interaction|
|
34
|
+
interaction.recorded_at = File.mtime(cassette)
|
35
|
+
remove_unnecessary_standard_port(interaction)
|
36
|
+
denormalize_http_header_keys(interaction.request)
|
37
|
+
denormalize_http_header_keys(interaction.response)
|
38
|
+
normalize_body(interaction.request)
|
39
|
+
normalize_body(interaction.response)
|
40
|
+
interaction.to_hash
|
41
|
+
end
|
42
|
+
|
43
|
+
hash = {
|
44
|
+
"http_interactions" => http_interactions,
|
45
|
+
"recorded_with" => "VCR 1.11.3" # assume the last 1.x release
|
46
|
+
}
|
47
|
+
|
48
|
+
def hash.each
|
49
|
+
yield 'http_interactions', self['http_interactions']
|
50
|
+
yield 'recorded_with', self['recorded_with']
|
51
|
+
end
|
52
|
+
|
53
|
+
File.open(cassette, 'w') { |f| f.write ::YAML.dump(hash) }
|
54
|
+
@out.puts " - Migrated #{relative_casssette_name(cassette)}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_yaml(cassette)
|
58
|
+
::YAML.load_file(cassette)
|
59
|
+
rescue *@yaml_load_errors
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def yaml_load_errors
|
64
|
+
[ArgumentError].tap do |errors|
|
65
|
+
errors << Psych::SyntaxError if defined?(Psych::SyntaxError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def relative_casssette_name(cassette)
|
70
|
+
cassette.gsub(%r|\A#{Regexp.escape(@dir)}/?|, '')
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_vcr_1_cassette?(content)
|
74
|
+
content.is_a?(Array) &&
|
75
|
+
content.map(&:class).uniq == [HTTPInteraction]
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_unnecessary_standard_port(interaction)
|
79
|
+
uri = URI(interaction.request.uri)
|
80
|
+
if uri.scheme == 'http' && uri.port == 80 ||
|
81
|
+
uri.scheme == 'https' && uri.port == 443
|
82
|
+
uri.port = nil
|
83
|
+
interaction.request.uri = uri.to_s
|
84
|
+
end
|
85
|
+
rescue URI::InvalidURIError
|
86
|
+
# ignore this URI.
|
87
|
+
# This can occur when the user uses the filter_sensitive_data option
|
88
|
+
# to put a substitution string in their URI
|
89
|
+
end
|
90
|
+
|
91
|
+
def denormalize_http_header_keys(object)
|
92
|
+
object.headers = {}.tap do |denormalized|
|
93
|
+
object.headers.each do |k, v|
|
94
|
+
denormalized[denormalize_header_key(k)] = v
|
95
|
+
end if object.headers
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def denormalize_header_key(key)
|
100
|
+
key.split('-'). # 'user-agent' => %w(user agent)
|
101
|
+
each { |w| w.capitalize! }. # => %w(User Agent)
|
102
|
+
join('-')
|
103
|
+
end
|
104
|
+
|
105
|
+
def normalize_body(object)
|
106
|
+
object.body = '' if object.body.nil?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module VCR
|
2
|
+
class Cassette
|
3
|
+
class Serializers
|
4
|
+
autoload :YAML, 'vcr/cassette/serializers/yaml'
|
5
|
+
autoload :Syck, 'vcr/cassette/serializers/syck'
|
6
|
+
autoload :Psych, 'vcr/cassette/serializers/psych'
|
7
|
+
autoload :JSON, 'vcr/cassette/serializers/json'
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@serializers = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](name)
|
14
|
+
@serializers.fetch(name) do |_|
|
15
|
+
@serializers[name] = case name
|
16
|
+
when :yaml then YAML
|
17
|
+
when :syck then Syck
|
18
|
+
when :psych then Psych
|
19
|
+
when :json then JSON
|
20
|
+
else raise ArgumentError.new("The requested VCR cassette serializer (#{name.inspect}) is not registered.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(name, value)
|
26
|
+
if @serializers.has_key?(name)
|
27
|
+
warn "WARNING: There is already a VCR cassette serializer registered for #{name.inspect}. Overriding it."
|
28
|
+
end
|
29
|
+
|
30
|
+
@serializers[name] = value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
class Cassette
|
5
|
+
class Serializers
|
6
|
+
module JSON
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def file_extension
|
10
|
+
"json"
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(hash)
|
14
|
+
MultiJson.encode(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(string)
|
18
|
+
MultiJson.decode(string)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'psych'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
class Cassette
|
5
|
+
class Serializers
|
6
|
+
module Psych
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def file_extension
|
10
|
+
"yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(hash)
|
14
|
+
::Psych.dump(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(string)
|
18
|
+
::Psych.load(string)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
class Cassette
|
5
|
+
class Serializers
|
6
|
+
module Syck
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def file_extension
|
10
|
+
"yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(hash)
|
14
|
+
using_syck { ::YAML.dump(hash) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(string)
|
18
|
+
using_syck { ::YAML.load(string) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def using_syck
|
22
|
+
return yield unless defined?(::YAML::ENGINE)
|
23
|
+
original_engine = ::YAML::ENGINE.yamler
|
24
|
+
::YAML::ENGINE.yamler = 'syck'
|
25
|
+
|
26
|
+
begin
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
::YAML::ENGINE.yamler = original_engine
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
class Cassette
|
5
|
+
class Serializers
|
6
|
+
module YAML
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def file_extension
|
10
|
+
"yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(hash)
|
14
|
+
::YAML.dump(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(string)
|
18
|
+
::YAML.load(string)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/vcr/configuration.rb
CHANGED
@@ -14,7 +14,8 @@ module VCR
|
|
14
14
|
@allow_http_connections_when_no_cassette = nil
|
15
15
|
@default_cassette_options = {
|
16
16
|
:record => :once,
|
17
|
-
:match_requests_on => RequestMatcherRegistry::DEFAULT_MATCHERS
|
17
|
+
:match_requests_on => RequestMatcherRegistry::DEFAULT_MATCHERS,
|
18
|
+
:serialize_with => :yaml
|
18
19
|
}
|
19
20
|
end
|
20
21
|
|
@@ -62,6 +63,10 @@ module VCR
|
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
66
|
+
def cassette_serializers
|
67
|
+
VCR.cassette_serializers
|
68
|
+
end
|
69
|
+
|
65
70
|
private
|
66
71
|
|
67
72
|
def load_library_hook(hook)
|
data/lib/vcr/errors.rb
CHANGED
@@ -6,7 +6,7 @@ module VCR
|
|
6
6
|
class MissingERBVariableError < Error; end
|
7
7
|
class LibraryVersionTooLowError < Error; end
|
8
8
|
class UnregisteredMatcherError < Error; end
|
9
|
-
|
9
|
+
class InvalidCassetteFormatError < Error; end
|
10
10
|
|
11
11
|
class HTTPConnectionNotAllowedError < Error
|
12
12
|
def initialize(request)
|
@@ -112,17 +112,11 @@ module VCR
|
|
112
112
|
normalized = {}
|
113
113
|
headers.each do |k, v|
|
114
114
|
v = v.join(', ') if v.respond_to?(:join)
|
115
|
-
normalized[
|
115
|
+
normalized[k] = v
|
116
116
|
end
|
117
117
|
normalized
|
118
118
|
end
|
119
119
|
|
120
|
-
def normalize_header_key(key)
|
121
|
-
key.split('-'). # 'user-agent' => %w(user agent)
|
122
|
-
each { |w| w.capitalize! }. # => %w(User Agent)
|
123
|
-
join('-')
|
124
|
-
end
|
125
|
-
|
126
120
|
::Excon.stub({}) do |params|
|
127
121
|
self.new(params).handle
|
128
122
|
end
|
@@ -2,7 +2,7 @@ require 'vcr/util/version_checker'
|
|
2
2
|
require 'vcr/request_handler'
|
3
3
|
require 'typhoeus'
|
4
4
|
|
5
|
-
VCR::VersionChecker.new('Typhoeus', Typhoeus::VERSION, '0.2
|
5
|
+
VCR::VersionChecker.new('Typhoeus', Typhoeus::VERSION, '0.3.2', '0.3').check_version!
|
6
6
|
|
7
7
|
module VCR
|
8
8
|
class LibraryHooks
|
@@ -35,16 +35,12 @@ module VCR
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
def on_stubbed_request
|
39
|
-
hydra_mock
|
40
|
-
end
|
41
|
-
|
42
38
|
def vcr_request
|
43
39
|
@vcr_request ||= vcr_request_from(request)
|
44
40
|
end
|
45
41
|
|
46
|
-
def
|
47
|
-
|
42
|
+
def on_stubbed_request
|
43
|
+
::Typhoeus::Response.new \
|
48
44
|
:http_version => stubbed_response.http_version,
|
49
45
|
:code => stubbed_response.status.code,
|
50
46
|
:status_message => stubbed_response.status.message,
|
@@ -52,12 +48,6 @@ module VCR
|
|
52
48
|
:body => stubbed_response.body
|
53
49
|
end
|
54
50
|
|
55
|
-
def hydra_mock
|
56
|
-
@hydra_mock ||= ::Typhoeus::HydraMock.new(/.*/, :any).tap do |m|
|
57
|
-
m.and_return(typhoeus_response)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
51
|
def stubbed_response_headers
|
62
52
|
@stubbed_response_headers ||= {}.tap do |hash|
|
63
53
|
stubbed_response.headers.each do |key, values|
|
@@ -75,19 +65,13 @@ module VCR
|
|
75
65
|
end
|
76
66
|
end
|
77
67
|
|
68
|
+
::Typhoeus::Hydra.register_stub_finder do |request|
|
69
|
+
VCR::LibraryHooks::Typhoeus::RequestHandler.new(request).handle
|
70
|
+
end
|
78
71
|
end
|
79
72
|
end
|
80
73
|
end
|
81
74
|
|
82
|
-
# TODO: add Typhoeus::Hydra.register_stub_finder API to Typhoeus
|
83
|
-
# so we can use that instead of monkey-patching it.
|
84
|
-
Typhoeus::Hydra::Stubbing::SharedMethods.class_eval do
|
85
|
-
undef find_stub_from_request
|
86
|
-
def find_stub_from_request(request)
|
87
|
-
VCR::LibraryHooks::Typhoeus::RequestHandler.new(request).handle
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
75
|
VCR.configuration.after_library_hooks_loaded do
|
92
76
|
# ensure WebMock's Typhoeus adapter does not conflict with us here
|
93
77
|
# (i.e. to double record requests or whatever).
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'vcr/errors'
|
2
|
+
|
1
3
|
module VCR
|
2
4
|
class RequestMatcherRegistry
|
3
5
|
DEFAULT_MATCHERS = [:method, :uri]
|
@@ -8,6 +10,33 @@ module VCR
|
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
13
|
+
class URIWithoutParamsMatcher < Struct.new(:params_to_ignore)
|
14
|
+
def partial_uri_from(request)
|
15
|
+
URI(request.uri).tap do |uri|
|
16
|
+
break unless uri.query # ignore uris without params, e.g. "http://example.com/"
|
17
|
+
|
18
|
+
uri.query = uri.query.split('&').tap { |params|
|
19
|
+
params.map! do |p|
|
20
|
+
key, value = p.split('=')
|
21
|
+
key.gsub!(/\[\]\z/, '') # handle params like tag[]=
|
22
|
+
[key, value]
|
23
|
+
end
|
24
|
+
|
25
|
+
params.reject! { |p| params_to_ignore.include?(p.first) }
|
26
|
+
params.map! { |p| p.join('=') }
|
27
|
+
}.join('&')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(request_1, request_2)
|
32
|
+
partial_uri_from(request_1) == partial_uri_from(request_2)
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_proc
|
36
|
+
lambda { |r1, r2| call(r1, r2) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
11
40
|
def initialize
|
12
41
|
@registry = {}
|
13
42
|
register_built_ins
|
@@ -30,31 +59,19 @@ module VCR
|
|
30
59
|
end
|
31
60
|
|
32
61
|
def uri_without_params(*ignores)
|
33
|
-
ignores
|
34
|
-
|
35
|
-
lambda do |request_1, request_2|
|
36
|
-
uri_1, uri_2 = [request_1, request_2].map do |r|
|
37
|
-
URI(r.uri).tap do |uri|
|
38
|
-
uri.query = uri.query.split('&').tap { |params|
|
39
|
-
params.map! do |p|
|
40
|
-
key, value = p.split('=')
|
41
|
-
key.gsub!(/\[\]\z/, '') # handle params like tag[]=
|
42
|
-
[key, value]
|
43
|
-
end
|
44
|
-
|
45
|
-
params.reject! { |p| ignores.include?(p.first) }
|
46
|
-
params.map! { |p| p.join('=') }
|
47
|
-
}.join('&')
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
uri_1 == uri_2
|
52
|
-
end
|
62
|
+
uri_without_param_matchers[ignores]
|
53
63
|
end
|
54
64
|
alias uri_without_param uri_without_params
|
55
65
|
|
56
66
|
private
|
57
67
|
|
68
|
+
def uri_without_param_matchers
|
69
|
+
@uri_without_param_matchers ||= Hash.new do |hash, params|
|
70
|
+
params = params.map(&:to_s)
|
71
|
+
hash[params] = URIWithoutParamsMatcher.new(params)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
58
75
|
def raise_unregistered_matcher_error(name)
|
59
76
|
raise Errors::UnregisteredMatcherError.new \
|
60
77
|
"There is no matcher registered for #{name.inspect}. " +
|
@@ -63,22 +80,18 @@ module VCR
|
|
63
80
|
|
64
81
|
def register_built_ins
|
65
82
|
register(:method) { |r1, r2| r1.method == r2.method }
|
66
|
-
register(:uri) { |r1, r2|
|
83
|
+
register(:uri) { |r1, r2| without_standard_port(r1.uri) == without_standard_port(r2.uri) }
|
67
84
|
register(:host) { |r1, r2| URI(r1.uri).host == URI(r2.uri).host }
|
68
85
|
register(:path) { |r1, r2| URI(r1.uri).path == URI(r2.uri).path }
|
69
86
|
register(:body) { |r1, r2| r1.body == r2.body }
|
70
87
|
register(:headers) { |r1, r2| r1.headers == r2.headers }
|
71
88
|
end
|
72
89
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# Faraday normalizes URIs by replacing '+' with '%20'
|
79
|
-
uri.gsub('+', '%20')
|
80
|
-
else
|
81
|
-
uri
|
90
|
+
def without_standard_port(uri)
|
91
|
+
URI(uri).tap do |u|
|
92
|
+
if [['http', 80], ['https', 443]].include?([u.scheme, u.port])
|
93
|
+
u.port = nil
|
94
|
+
end
|
82
95
|
end
|
83
96
|
end
|
84
97
|
end
|