vcr 2.0.0.beta2 → 2.0.0.rc1
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 +0 -2
- data/CHANGELOG.md +22 -1
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +1 -9
- data/README.md +8 -2
- data/Rakefile +12 -1
- data/cucumber.yml +8 -9
- data/features/.nav +10 -4
- data/features/cassettes/format.feature +1 -1
- data/features/cassettes/no_cassette.feature +8 -11
- data/features/configuration/allow_http_connections_when_no_cassette.feature +4 -4
- data/features/configuration/filter_sensitive_data.feature +3 -0
- data/features/configuration/hook_into.feature +4 -8
- data/features/configuration/ignore_request.feature +191 -0
- data/features/getting_started.md +38 -21
- data/features/hooks/after_http_request.feature +44 -0
- data/features/hooks/around_http_request.feature +56 -0
- data/features/hooks/before_http_request.feature +44 -0
- data/features/hooks/before_playback.feature +181 -0
- data/features/hooks/before_record.feature +172 -0
- data/features/middleware/faraday.feature +7 -3
- data/features/record_modes/none.feature +2 -1
- data/features/record_modes/once.feature +2 -1
- data/features/request_matching/body.feature +2 -2
- data/features/request_matching/custom_matcher.feature +2 -2
- data/features/request_matching/headers.feature +2 -2
- data/features/request_matching/host.feature +2 -2
- data/features/request_matching/identical_request_sequence.feature +2 -2
- data/features/request_matching/method.feature +2 -2
- data/features/request_matching/path.feature +2 -2
- data/features/request_matching/playback_repeats.feature +2 -1
- data/features/request_matching/uri.feature +2 -2
- data/features/support/env.rb +21 -12
- data/features/test_frameworks/cucumber.feature +9 -4
- data/features/test_frameworks/{rspec.feature → rspec_macro.feature} +7 -7
- data/features/test_frameworks/rspec_metadata.feature +90 -0
- data/lib/vcr.rb +1 -1
- data/lib/vcr/cassette.rb +3 -3
- data/lib/vcr/cassette/http_interaction_list.rb +13 -9
- data/lib/vcr/cassette/migrator.rb +1 -1
- data/lib/vcr/configuration.rb +37 -0
- data/lib/vcr/errors.rb +172 -6
- data/lib/vcr/library_hooks.rb +4 -6
- data/lib/vcr/library_hooks/excon.rb +23 -11
- data/lib/vcr/library_hooks/fakeweb.rb +85 -24
- data/lib/vcr/library_hooks/faraday.rb +30 -2
- data/lib/vcr/library_hooks/typhoeus.rb +25 -3
- data/lib/vcr/library_hooks/webmock.rb +25 -36
- data/lib/vcr/middleware/faraday.rb +23 -5
- data/lib/vcr/request_handler.rb +12 -1
- data/lib/vcr/request_ignorer.rb +12 -1
- data/lib/vcr/request_matcher_registry.rb +1 -9
- data/lib/vcr/structs.rb +32 -2
- data/lib/vcr/test_frameworks/rspec.rb +28 -0
- data/lib/vcr/util/hooks.rb +12 -4
- data/lib/vcr/util/version_checker.rb +2 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/fixtures/cassette_spec/example.yml +1 -1
- data/spec/fixtures/{fake_example.com_responses.yml → fake_example_responses.yml} +0 -0
- data/spec/monkey_patches.rb +1 -1
- data/spec/spec_helper.rb +3 -1
- data/spec/support/http_library_adapters.rb +4 -3
- data/spec/support/shared_example_groups/hook_into_http_library.rb +194 -12
- data/spec/support/shared_example_groups/request_hooks.rb +58 -0
- data/spec/support/shared_example_groups/version_checking.rb +5 -0
- data/spec/support/sinatra_app.rb +17 -9
- data/spec/support/vcr_stub_helpers.rb +17 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +28 -29
- data/spec/vcr/cassette/migrator_spec.rb +6 -7
- data/spec/vcr/cassette_spec.rb +5 -5
- data/spec/vcr/configuration_spec.rb +51 -32
- data/spec/vcr/deprecations_spec.rb +0 -8
- data/spec/vcr/errors_spec.rb +129 -0
- data/spec/vcr/library_hooks/excon_spec.rb +21 -4
- data/spec/vcr/library_hooks/fakeweb_spec.rb +71 -3
- data/spec/vcr/library_hooks/faraday_spec.rb +45 -0
- data/spec/vcr/library_hooks/typhoeus_spec.rb +31 -1
- data/spec/vcr/library_hooks/webmock_spec.rb +26 -3
- data/spec/vcr/middleware/faraday_spec.rb +84 -1
- data/spec/vcr/request_ignorer_spec.rb +16 -0
- data/spec/vcr/request_matcher_registry_spec.rb +0 -26
- data/spec/vcr/structs_spec.rb +61 -1
- data/spec/vcr/test_frameworks/rspec_spec.rb +32 -0
- data/spec/vcr/util/hooks_spec.rb +73 -63
- data/spec/vcr_spec.rb +2 -2
- data/vcr.gemspec +5 -5
- metadata +51 -31
- data/features/configuration/hooks.feature +0 -270
- data/features/configuration/ignore_hosts.feature +0 -61
- data/features/configuration/ignore_localhost.feature +0 -97
data/lib/vcr/configuration.rb
CHANGED
@@ -9,6 +9,8 @@ module VCR
|
|
9
9
|
define_hook :before_record
|
10
10
|
define_hook :before_playback
|
11
11
|
define_hook :after_library_hooks_loaded
|
12
|
+
define_hook :before_http_request
|
13
|
+
define_hook :after_http_request, :prepend
|
12
14
|
|
13
15
|
def initialize
|
14
16
|
@allow_http_connections_when_no_cassette = nil
|
@@ -48,6 +50,10 @@ module VCR
|
|
48
50
|
VCR.request_ignorer.ignore_localhost = value
|
49
51
|
end
|
50
52
|
|
53
|
+
def ignore_request(&block)
|
54
|
+
VCR.request_ignorer.ignore_request(&block)
|
55
|
+
end
|
56
|
+
|
51
57
|
attr_writer :allow_http_connections_when_no_cassette
|
52
58
|
def allow_http_connections_when_no_cassette?
|
53
59
|
!!@allow_http_connections_when_no_cassette
|
@@ -62,11 +68,28 @@ module VCR
|
|
62
68
|
interaction.filter!(placeholder, call_block(block, interaction))
|
63
69
|
end
|
64
70
|
end
|
71
|
+
alias define_cassette_placeholder filter_sensitive_data
|
65
72
|
|
66
73
|
def cassette_serializers
|
67
74
|
VCR.cassette_serializers
|
68
75
|
end
|
69
76
|
|
77
|
+
def around_http_request(&block)
|
78
|
+
require 'fiber'
|
79
|
+
rescue LoadError
|
80
|
+
raise Errors::NotSupportedError.new \
|
81
|
+
"VCR::Configuration#around_http_request requires fibers, " +
|
82
|
+
"which are not available on your ruby intepreter."
|
83
|
+
else
|
84
|
+
fiber, hook_decaration = nil, caller.first
|
85
|
+
before_http_request { |request| fiber = start_new_fiber_for(request, block) }
|
86
|
+
after_http_request { |request| resume_fiber(fiber, hook_decaration) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def configure_rspec_metadata!
|
90
|
+
VCR::RSpec::Metadata.configure!
|
91
|
+
end
|
92
|
+
|
70
93
|
private
|
71
94
|
|
72
95
|
def load_library_hook(hook)
|
@@ -76,6 +99,20 @@ module VCR
|
|
76
99
|
raise e unless e.message.include?(file) # in case FakeWeb/WebMock/etc itself is not available
|
77
100
|
raise ArgumentError.new("#{hook.inspect} is not a supported VCR HTTP library hook.")
|
78
101
|
end
|
102
|
+
|
103
|
+
def resume_fiber(fiber, hook_declaration)
|
104
|
+
fiber.resume
|
105
|
+
rescue FiberError
|
106
|
+
raise Errors::AroundHTTPRequestHookError.new \
|
107
|
+
"Your around_http_request hook declared at #{hook_declaration}" +
|
108
|
+
" must call #proceed on the yielded request but did not."
|
109
|
+
end
|
110
|
+
|
111
|
+
def start_new_fiber_for(request, block)
|
112
|
+
Fiber.new(&block).tap do |fiber|
|
113
|
+
fiber.resume(request.fiber_aware)
|
114
|
+
end
|
115
|
+
end
|
79
116
|
end
|
80
117
|
end
|
81
118
|
|
data/lib/vcr/errors.rb
CHANGED
@@ -7,15 +7,181 @@ module VCR
|
|
7
7
|
class LibraryVersionTooLowError < Error; end
|
8
8
|
class UnregisteredMatcherError < Error; end
|
9
9
|
class InvalidCassetteFormatError < Error; end
|
10
|
+
class AroundHTTPRequestHookError < Error; end
|
11
|
+
class NotSupportedError < Error; end
|
12
|
+
|
13
|
+
class UnhandledHTTPRequestError < Error
|
14
|
+
attr_reader :request
|
10
15
|
|
11
|
-
class HTTPConnectionNotAllowedError < Error
|
12
16
|
def initialize(request)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
@request = request
|
18
|
+
super construct_message
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def relish_version_slug
|
24
|
+
@relish_version_slug ||= VCR.version.gsub(/\W/, '-')
|
25
|
+
end
|
26
|
+
|
27
|
+
def construct_message
|
28
|
+
["", "", "=" * 80,
|
29
|
+
"An HTTP request has been made that VCR does not know how to handle:",
|
30
|
+
" #{request_description}\n",
|
31
|
+
cassette_description,
|
32
|
+
formatted_suggestions,
|
33
|
+
"=" * 80, "", ""].join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def request_description
|
37
|
+
"#{request.method.to_s.upcase} #{request.uri}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def cassette_description
|
41
|
+
if cassette = VCR.current_cassette
|
42
|
+
["VCR is currently using the following cassette:",
|
43
|
+
" - #{cassette.file}",
|
44
|
+
" - :record => #{cassette.record_mode.inspect}",
|
45
|
+
" - :match_requests_on => #{cassette.match_requests_on.inspect}\n",
|
46
|
+
"Under the current configuration VCR can not find a suitable HTTP interaction",
|
47
|
+
"to replay and is prevented from recording new requests. There are a few ways",
|
48
|
+
"you can deal with this:\n"].join("\n")
|
49
|
+
else
|
50
|
+
["There is currently no cassette in use. There are a few ways",
|
51
|
+
"you can configure VCR to handle this request:\n"].join("\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def formatted_suggestions
|
56
|
+
formatted_points, formatted_foot_notes = [], []
|
57
|
+
|
58
|
+
suggestions.each_with_index do |suggestion, index|
|
59
|
+
bullet_point, foot_note = suggestion.first, suggestion.last
|
60
|
+
formatted_points << format_bullet_point(bullet_point, index)
|
61
|
+
formatted_foot_notes << format_foot_note(foot_note, index)
|
62
|
+
end
|
63
|
+
|
64
|
+
[
|
65
|
+
formatted_points.join("\n"),
|
66
|
+
formatted_foot_notes.join("\n")
|
67
|
+
].join("\n\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_bullet_point(lines, index)
|
71
|
+
lines.first.insert(0, " * ")
|
72
|
+
lines.last << " [#{index + 1}]."
|
73
|
+
lines.join("\n ")
|
74
|
+
end
|
75
|
+
|
76
|
+
def format_foot_note(url, index)
|
77
|
+
"[#{index + 1}] #{url % relish_version_slug}"
|
78
|
+
end
|
79
|
+
|
80
|
+
ALL_SUGGESTIONS = {
|
81
|
+
:use_new_episodes => [
|
82
|
+
["You can use the :new_episodes record mode to allow VCR to",
|
83
|
+
"record this new request to the existing cassette"],
|
84
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/record-modes/new-episodes"
|
85
|
+
],
|
86
|
+
|
87
|
+
:delete_cassette_for_once => [
|
88
|
+
["The current record mode (:once) does not allow new requests to be recorded",
|
89
|
+
"to a previously recorded cassette. You can delete the cassette file and re-run",
|
90
|
+
"your tests to allow the cassette to be recorded with this request"],
|
91
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/record-modes/once"
|
92
|
+
],
|
93
|
+
|
94
|
+
:deal_with_none => [
|
95
|
+
["The current record mode (:none) does not allow requests to be recorded. You",
|
96
|
+
"can temporarily change the record mode to :once, delete the cassette file ",
|
97
|
+
"and re-run your tests to allow the cassette to be recorded with this request"],
|
98
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/record-modes/none"
|
99
|
+
],
|
100
|
+
|
101
|
+
:use_a_cassette => [
|
102
|
+
["If you want VCR to record this request and play it back during future test",
|
103
|
+
"runs, you should wrap your test (or this portion of your test) in a",
|
104
|
+
"`VCR.use_cassette` block"],
|
105
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/getting-started"
|
106
|
+
],
|
107
|
+
|
108
|
+
:allow_http_connections_when_no_cassette => [
|
109
|
+
["If you only want VCR to handle requests made while a cassette is in use,",
|
110
|
+
"configure `allow_http_connections_when_no_cassette = true`. VCR will",
|
111
|
+
"ignore this request since it is made when there is no cassette"],
|
112
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/configuration/allow-http-connections-when-no-cassette"
|
113
|
+
],
|
114
|
+
|
115
|
+
:ignore_request => [
|
116
|
+
["If you want VCR to ignore this request (and others like it), you can",
|
117
|
+
"set an `ignore_request` callback"],
|
118
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/configuration/ignore-request"
|
119
|
+
],
|
120
|
+
|
121
|
+
:allow_playback_repeats => [
|
122
|
+
["The cassette contains an HTTP interaction that matches this request,",
|
123
|
+
"but it has already been played back. If you wish to allow a single HTTP",
|
124
|
+
"interaction to be played back multiple times, set the `:allow_playback_repeats`",
|
125
|
+
"cassette option"],
|
126
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/request-matching/playback-repeats"
|
127
|
+
],
|
128
|
+
|
129
|
+
:match_requests_on => [
|
130
|
+
["The cassette contains %s not been",
|
131
|
+
"played back. If your request is non-deterministic, you may need to",
|
132
|
+
"change your :match_requests_on cassette option to be more lenient",
|
133
|
+
"or use a custom request matcher to allow it to match"],
|
134
|
+
"https://www.relishapp.com/myronmarston/vcr/v/%s/docs/request-matching"
|
135
|
+
]
|
136
|
+
}
|
137
|
+
|
138
|
+
def suggestion_for(key)
|
139
|
+
bullet_point_lines, url = ALL_SUGGESTIONS[key]
|
140
|
+
bullet_point_lines = bullet_point_lines.map(&:dup)
|
141
|
+
url = url.dup
|
142
|
+
[bullet_point_lines, url]
|
143
|
+
end
|
144
|
+
|
145
|
+
def suggestions
|
146
|
+
return no_cassette_suggestions unless cassette = VCR.current_cassette
|
147
|
+
|
148
|
+
[:use_new_episodes, :ignore_request].tap do |suggestions|
|
149
|
+
suggestions.push(*record_mode_suggestion)
|
150
|
+
suggestions << :allow_playback_repeats if cassette.http_interactions.has_used_interaction_matching?(request)
|
151
|
+
suggestions.map! { |k| suggestion_for(k) }
|
152
|
+
suggestions.push(*match_requests_on_suggestion)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def no_cassette_suggestions
|
157
|
+
[:use_a_cassette, :allow_http_connections_when_no_cassette, :ignore_request].map do |key|
|
158
|
+
suggestion_for(key)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def record_mode_suggestion
|
163
|
+
case VCR.current_cassette.record_mode
|
164
|
+
when :none then [:deal_with_none]
|
165
|
+
when :once then [:delete_cassette_for_once]
|
166
|
+
else []
|
167
|
+
end
|
18
168
|
end
|
169
|
+
|
170
|
+
def match_requests_on_suggestion
|
171
|
+
num_remaining_interactions = VCR.current_cassette.http_interactions.remaining_unused_interaction_count
|
172
|
+
return [] if num_remaining_interactions.zero?
|
173
|
+
|
174
|
+
interaction_description = if num_remaining_interactions == 1
|
175
|
+
"1 HTTP interaction that has"
|
176
|
+
else
|
177
|
+
"#{num_remaining_interactions} HTTP interactions that have"
|
178
|
+
end
|
179
|
+
|
180
|
+
description_lines, link = suggestion_for(:match_requests_on)
|
181
|
+
description_lines[0] = description_lines[0] % interaction_description
|
182
|
+
[[description_lines, link]]
|
183
|
+
end
|
184
|
+
|
19
185
|
end
|
20
186
|
end
|
21
187
|
end
|
data/lib/vcr/library_hooks.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
module VCR
|
2
2
|
class LibraryHooks
|
3
|
-
|
4
|
-
@exclusive_hook = nil
|
5
|
-
end
|
3
|
+
attr_accessor :exclusive_hook
|
6
4
|
|
7
5
|
def disabled?(hook)
|
8
|
-
![nil, hook].include?(
|
6
|
+
![nil, hook].include?(exclusive_hook)
|
9
7
|
end
|
10
8
|
|
11
9
|
def exclusively_enabled(hook)
|
12
|
-
|
10
|
+
self.exclusive_hook = hook
|
13
11
|
yield
|
14
12
|
ensure
|
15
|
-
|
13
|
+
self.exclusive_hook = nil
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|
@@ -2,7 +2,7 @@ require 'vcr/util/version_checker'
|
|
2
2
|
require 'vcr/request_handler'
|
3
3
|
require 'excon'
|
4
4
|
|
5
|
-
VCR::VersionChecker.new('Excon', Excon::VERSION, '0.6.5', '0.
|
5
|
+
VCR::VersionChecker.new('Excon', Excon::VERSION, '0.6.5', '0.7').check_version!
|
6
6
|
|
7
7
|
module VCR
|
8
8
|
class LibraryHooks
|
@@ -10,12 +10,20 @@ module VCR
|
|
10
10
|
class RequestHandler < ::VCR::RequestHandler
|
11
11
|
attr_reader :params
|
12
12
|
def initialize(params)
|
13
|
+
@vcr_response = nil
|
13
14
|
@params = params
|
14
15
|
end
|
15
16
|
|
17
|
+
def handle
|
18
|
+
super
|
19
|
+
ensure
|
20
|
+
invoke_after_request_hook(@vcr_response)
|
21
|
+
end
|
22
|
+
|
16
23
|
private
|
17
24
|
|
18
25
|
def on_stubbed_request
|
26
|
+
@vcr_response = stubbed_response
|
19
27
|
{
|
20
28
|
:body => stubbed_response.body,
|
21
29
|
:headers => normalized_headers(stubbed_response.headers || {}),
|
@@ -23,6 +31,10 @@ module VCR
|
|
23
31
|
}
|
24
32
|
end
|
25
33
|
|
34
|
+
def on_ignored_request
|
35
|
+
perform_real_request
|
36
|
+
end
|
37
|
+
|
26
38
|
def response_from_excon_error(error)
|
27
39
|
if error.respond_to?(:response)
|
28
40
|
error.response
|
@@ -34,20 +46,18 @@ module VCR
|
|
34
46
|
end
|
35
47
|
|
36
48
|
def perform_real_request
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
rescue ::Excon::Errors::Error => e
|
42
|
-
yield response_from_excon_error(e) if block_given?
|
43
|
-
raise e
|
49
|
+
begin
|
50
|
+
response = ::Excon.new(uri).request(params.merge(:mock => false))
|
51
|
+
rescue ::Excon::Errors::Error => excon_error
|
52
|
+
response = response_from_excon_error(excon_error)
|
44
53
|
end
|
45
54
|
|
55
|
+
@vcr_response = vcr_response_from(response)
|
46
56
|
yield response if block_given?
|
57
|
+
raise excon_error if excon_error
|
47
58
|
|
48
59
|
response.attributes
|
49
60
|
end
|
50
|
-
alias on_ignored_request perform_real_request
|
51
61
|
|
52
62
|
def on_recordable_request
|
53
63
|
perform_real_request do |response|
|
@@ -60,6 +70,8 @@ module VCR
|
|
60
70
|
@uri ||= "#{params[:scheme]}://#{params[:host]}:#{params[:port]}#{params[:path]}#{query}"
|
61
71
|
end
|
62
72
|
|
73
|
+
# based on:
|
74
|
+
# https://github.com/geemus/excon/blob/v0.7.8/lib/excon/connection.rb#L117-132
|
63
75
|
def query
|
64
76
|
@query ||= case params[:query]
|
65
77
|
when String
|
@@ -84,7 +96,7 @@ module VCR
|
|
84
96
|
def http_interaction_for(response)
|
85
97
|
VCR::HTTPInteraction.new \
|
86
98
|
vcr_request,
|
87
|
-
|
99
|
+
vcr_response_from(response)
|
88
100
|
end
|
89
101
|
|
90
102
|
def vcr_request
|
@@ -100,7 +112,7 @@ module VCR
|
|
100
112
|
end
|
101
113
|
end
|
102
114
|
|
103
|
-
def
|
115
|
+
def vcr_response_from(response)
|
104
116
|
VCR::Response.new \
|
105
117
|
VCR::ResponseStatus.new(response.status, nil),
|
106
118
|
response.headers,
|
@@ -3,6 +3,7 @@ require 'fakeweb'
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'vcr/extensions/net_http_response'
|
5
5
|
require 'vcr/request_handler'
|
6
|
+
require 'set'
|
6
7
|
|
7
8
|
VCR::VersionChecker.new('FakeWeb', FakeWeb::VERSION, '1.3.0', '1.3').check_version!
|
8
9
|
|
@@ -10,39 +11,84 @@ module VCR
|
|
10
11
|
class LibraryHooks
|
11
12
|
module FakeWeb
|
12
13
|
class RequestHandler < ::VCR::RequestHandler
|
13
|
-
attr_reader :net_http, :request, :request_body, :
|
14
|
-
def initialize(net_http, request, request_body = nil, &
|
15
|
-
@net_http, @request, @request_body, @
|
16
|
-
net_http, request, request_body,
|
14
|
+
attr_reader :net_http, :request, :request_body, :response_block
|
15
|
+
def initialize(net_http, request, request_body = nil, &response_block)
|
16
|
+
@net_http, @request, @request_body, @response_block =
|
17
|
+
net_http, request, request_body, response_block
|
18
|
+
@vcr_response, @recursing = nil, false
|
17
19
|
end
|
18
20
|
|
19
|
-
|
21
|
+
def handle
|
22
|
+
super
|
23
|
+
ensure
|
24
|
+
invoke_after_request_hook(@vcr_response) unless @recursing
|
25
|
+
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
class << self
|
28
|
+
def already_seen_requests
|
29
|
+
@already_seen_requests ||= Set.new
|
30
|
+
end
|
31
|
+
end
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
33
|
+
private
|
34
|
+
|
35
|
+
def invoke_before_request_hook
|
36
|
+
unless self.class.already_seen_requests.include?(request.object_id)
|
37
|
+
super
|
38
|
+
# we use the object_id so that if there is bug that causes
|
39
|
+
# us not to fully cleanup, we'll only be leaking the memory
|
40
|
+
# of one integer, not the whole request object.
|
41
|
+
self.class.already_seen_requests << request.object_id
|
31
42
|
end
|
32
43
|
end
|
33
|
-
alias on_recordable_request perform_and_record_request
|
34
44
|
|
35
|
-
def
|
45
|
+
def invoke_after_request_hook(vcr_response)
|
46
|
+
self.class.already_seen_requests.delete(request.object_id)
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def on_recordable_request
|
51
|
+
perform_request(net_http.started?, :record_interaction)
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_stubbed_request
|
36
55
|
with_exclusive_fakeweb_stub(stubbed_response) do
|
37
|
-
|
56
|
+
# force it to be considered started since it doesn't
|
57
|
+
# recurse in this case like the others.
|
58
|
+
perform_request(:started)
|
38
59
|
end
|
39
60
|
end
|
40
|
-
alias on_stubbed_request perform_stubbed_request
|
41
61
|
|
42
|
-
def
|
43
|
-
net_http.
|
62
|
+
def on_ignored_request
|
63
|
+
perform_request(net_http.started?)
|
64
|
+
end
|
65
|
+
|
66
|
+
# overriden to prevent it from invoking the after_http_request hook,
|
67
|
+
# since we invoke the hook in an ensure block above.
|
68
|
+
def on_connection_not_allowed
|
69
|
+
raise VCR::Errors::UnhandledHTTPRequestError.new(vcr_request)
|
70
|
+
end
|
71
|
+
|
72
|
+
def perform_request(started, record_interaction = false)
|
73
|
+
# Net::HTTP calls #request recursively in certain circumstances.
|
74
|
+
# We only want to record the request when the request is started, as
|
75
|
+
# that is the final time through #request.
|
76
|
+
unless started
|
77
|
+
@recursing = true
|
78
|
+
return net_http.request_without_vcr(request, request_body, &response_block)
|
79
|
+
end
|
80
|
+
|
81
|
+
net_http.request_without_vcr(request, request_body) do |response|
|
82
|
+
@vcr_response = vcr_response_from(response)
|
83
|
+
|
84
|
+
if record_interaction
|
85
|
+
VCR.record_http_interaction VCR::HTTPInteraction.new(vcr_request, @vcr_response)
|
86
|
+
end
|
87
|
+
|
88
|
+
response.extend VCR::Net::HTTPResponse # "unwind" the response
|
89
|
+
response_block.call(response) if response_block
|
90
|
+
end
|
44
91
|
end
|
45
|
-
alias on_ignored_request perform_request
|
46
92
|
|
47
93
|
def uri
|
48
94
|
@uri ||= ::FakeWeb::Utility.request_uri_as_string(net_http, request)
|
@@ -91,9 +137,13 @@ module Net
|
|
91
137
|
class HTTP
|
92
138
|
unless method_defined?(:request_with_vcr)
|
93
139
|
def request_with_vcr(*args, &block)
|
94
|
-
VCR
|
95
|
-
|
96
|
-
|
140
|
+
if VCR.turned_on?
|
141
|
+
VCR::LibraryHooks::FakeWeb::RequestHandler.new(
|
142
|
+
self, *args, &block
|
143
|
+
).handle
|
144
|
+
else
|
145
|
+
request_without_vcr(*args, &block)
|
146
|
+
end
|
97
147
|
end
|
98
148
|
|
99
149
|
alias request_without_vcr request
|
@@ -102,6 +152,17 @@ module Net
|
|
102
152
|
end
|
103
153
|
end
|
104
154
|
|
155
|
+
class << FakeWeb
|
156
|
+
# ensure HTTP requests are always allowed; VCR takes care of disallowing
|
157
|
+
# them at the appropriate times in its hook
|
158
|
+
def allow_net_connect_with_vcr?(*args)
|
159
|
+
VCR.turned_on? ? true : allow_net_connect_without_vcr?
|
160
|
+
end
|
161
|
+
|
162
|
+
alias allow_net_connect_without_vcr? allow_net_connect?
|
163
|
+
alias allow_net_connect? allow_net_connect_with_vcr?
|
164
|
+
end unless FakeWeb.respond_to?(:allow_net_connect_with_vcr?)
|
165
|
+
|
105
166
|
VCR.configuration.after_library_hooks_loaded do
|
106
167
|
if defined?(WebMock)
|
107
168
|
raise ArgumentError.new("You have configured VCR to hook into both :fakeweb and :webmock. You cannot use both.")
|