mustwin-vcr 2.9.3

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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/features/about_these_examples.md +18 -0
  3. data/features/cassettes/allow_unused_http_interactions.feature +100 -0
  4. data/features/cassettes/automatic_re_recording.feature +72 -0
  5. data/features/cassettes/decompress.feature +74 -0
  6. data/features/cassettes/dynamic_erb.feature +100 -0
  7. data/features/cassettes/exclusive.feature +126 -0
  8. data/features/cassettes/format.feature +323 -0
  9. data/features/cassettes/freezing_time.feature +68 -0
  10. data/features/cassettes/naming.feature +28 -0
  11. data/features/cassettes/no_cassette.feature +152 -0
  12. data/features/cassettes/update_content_length_header.feature +112 -0
  13. data/features/configuration/allow_http_connections_when_no_cassette.feature +55 -0
  14. data/features/configuration/cassette_library_dir.feature +31 -0
  15. data/features/configuration/debug_logging.feature +59 -0
  16. data/features/configuration/default_cassette_options.feature +100 -0
  17. data/features/configuration/filter_sensitive_data.feature +153 -0
  18. data/features/configuration/hook_into.feature +172 -0
  19. data/features/configuration/ignore_request.feature +192 -0
  20. data/features/configuration/preserve_exact_body_bytes.feature +108 -0
  21. data/features/configuration/query_parser.feature +84 -0
  22. data/features/configuration/uri_parser.feature +89 -0
  23. data/features/getting_started.md +82 -0
  24. data/features/hooks/after_http_request.feature +58 -0
  25. data/features/hooks/around_http_request.feature +57 -0
  26. data/features/hooks/before_http_request.feature +63 -0
  27. data/features/hooks/before_playback.feature +184 -0
  28. data/features/hooks/before_record.feature +172 -0
  29. data/features/http_libraries/em_http_request.feature +250 -0
  30. data/features/http_libraries/net_http.feature +179 -0
  31. data/features/middleware/faraday.feature +56 -0
  32. data/features/middleware/rack.feature +92 -0
  33. data/features/record_modes/all.feature +82 -0
  34. data/features/record_modes/new_episodes.feature +79 -0
  35. data/features/record_modes/none.feature +72 -0
  36. data/features/record_modes/once.feature +95 -0
  37. data/features/request_matching/README.md +30 -0
  38. data/features/request_matching/body.feature +91 -0
  39. data/features/request_matching/body_as_json.feature +90 -0
  40. data/features/request_matching/custom_matcher.feature +135 -0
  41. data/features/request_matching/headers.feature +85 -0
  42. data/features/request_matching/host.feature +95 -0
  43. data/features/request_matching/identical_request_sequence.feature +89 -0
  44. data/features/request_matching/method.feature +96 -0
  45. data/features/request_matching/path.feature +96 -0
  46. data/features/request_matching/playback_repeats.feature +98 -0
  47. data/features/request_matching/query.feature +97 -0
  48. data/features/request_matching/uri.feature +94 -0
  49. data/features/request_matching/uri_without_param.feature +101 -0
  50. data/features/step_definitions/cli_steps.rb +193 -0
  51. data/features/support/env.rb +44 -0
  52. data/features/support/http_lib_filters.rb +53 -0
  53. data/features/test_frameworks/cucumber.feature +211 -0
  54. data/features/test_frameworks/rspec_macro.feature +81 -0
  55. data/features/test_frameworks/rspec_metadata.feature +150 -0
  56. data/features/test_frameworks/test_unit.feature +49 -0
  57. data/lib/vcr.rb +347 -0
  58. data/lib/vcr/cassette.rb +291 -0
  59. data/lib/vcr/cassette/erb_renderer.rb +55 -0
  60. data/lib/vcr/cassette/http_interaction_list.rb +108 -0
  61. data/lib/vcr/cassette/migrator.rb +118 -0
  62. data/lib/vcr/cassette/persisters.rb +42 -0
  63. data/lib/vcr/cassette/persisters/file_system.rb +64 -0
  64. data/lib/vcr/cassette/serializers.rb +57 -0
  65. data/lib/vcr/cassette/serializers/json.rb +48 -0
  66. data/lib/vcr/cassette/serializers/psych.rb +48 -0
  67. data/lib/vcr/cassette/serializers/syck.rb +61 -0
  68. data/lib/vcr/cassette/serializers/yaml.rb +50 -0
  69. data/lib/vcr/configuration.rb +555 -0
  70. data/lib/vcr/deprecations.rb +109 -0
  71. data/lib/vcr/errors.rb +266 -0
  72. data/lib/vcr/extensions/net_http_response.rb +36 -0
  73. data/lib/vcr/library_hooks.rb +18 -0
  74. data/lib/vcr/library_hooks/excon.rb +27 -0
  75. data/lib/vcr/library_hooks/fakeweb.rb +196 -0
  76. data/lib/vcr/library_hooks/faraday.rb +51 -0
  77. data/lib/vcr/library_hooks/typhoeus.rb +120 -0
  78. data/lib/vcr/library_hooks/typhoeus_0.4.rb +103 -0
  79. data/lib/vcr/library_hooks/webmock.rb +164 -0
  80. data/lib/vcr/middleware/excon.rb +221 -0
  81. data/lib/vcr/middleware/excon/legacy_methods.rb +33 -0
  82. data/lib/vcr/middleware/faraday.rb +118 -0
  83. data/lib/vcr/middleware/rack.rb +79 -0
  84. data/lib/vcr/request_handler.rb +114 -0
  85. data/lib/vcr/request_ignorer.rb +43 -0
  86. data/lib/vcr/request_matcher_registry.rb +149 -0
  87. data/lib/vcr/structs.rb +578 -0
  88. data/lib/vcr/tasks/vcr.rake +9 -0
  89. data/lib/vcr/test_frameworks/cucumber.rb +64 -0
  90. data/lib/vcr/test_frameworks/rspec.rb +47 -0
  91. data/lib/vcr/util/hooks.rb +61 -0
  92. data/lib/vcr/util/internet_connection.rb +43 -0
  93. data/lib/vcr/util/logger.rb +59 -0
  94. data/lib/vcr/util/variable_args_block_caller.rb +13 -0
  95. data/lib/vcr/util/version_checker.rb +48 -0
  96. data/lib/vcr/version.rb +34 -0
  97. data/spec/acceptance/threading_spec.rb +34 -0
  98. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  99. data/spec/fixtures/cassette_spec/empty.yml +0 -0
  100. data/spec/fixtures/cassette_spec/example.yml +111 -0
  101. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +111 -0
  102. data/spec/fixtures/fake_example_responses.yml +110 -0
  103. data/spec/fixtures/match_requests_on.yml +187 -0
  104. data/spec/lib/vcr/cassette/erb_renderer_spec.rb +53 -0
  105. data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +295 -0
  106. data/spec/lib/vcr/cassette/migrator_spec.rb +195 -0
  107. data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +69 -0
  108. data/spec/lib/vcr/cassette/persisters_spec.rb +39 -0
  109. data/spec/lib/vcr/cassette/serializers_spec.rb +176 -0
  110. data/spec/lib/vcr/cassette_spec.rb +618 -0
  111. data/spec/lib/vcr/configuration_spec.rb +326 -0
  112. data/spec/lib/vcr/deprecations_spec.rb +85 -0
  113. data/spec/lib/vcr/errors_spec.rb +162 -0
  114. data/spec/lib/vcr/extensions/net_http_response_spec.rb +86 -0
  115. data/spec/lib/vcr/library_hooks/excon_spec.rb +104 -0
  116. data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +169 -0
  117. data/spec/lib/vcr/library_hooks/faraday_spec.rb +68 -0
  118. data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +36 -0
  119. data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +162 -0
  120. data/spec/lib/vcr/library_hooks/webmock_spec.rb +118 -0
  121. data/spec/lib/vcr/library_hooks_spec.rb +51 -0
  122. data/spec/lib/vcr/middleware/faraday_spec.rb +182 -0
  123. data/spec/lib/vcr/middleware/rack_spec.rb +115 -0
  124. data/spec/lib/vcr/request_ignorer_spec.rb +70 -0
  125. data/spec/lib/vcr/request_matcher_registry_spec.rb +345 -0
  126. data/spec/lib/vcr/structs_spec.rb +732 -0
  127. data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +107 -0
  128. data/spec/lib/vcr/test_frameworks/rspec_spec.rb +83 -0
  129. data/spec/lib/vcr/util/hooks_spec.rb +158 -0
  130. data/spec/lib/vcr/util/internet_connection_spec.rb +37 -0
  131. data/spec/lib/vcr/util/version_checker_spec.rb +31 -0
  132. data/spec/lib/vcr/version_spec.rb +27 -0
  133. data/spec/lib/vcr_spec.rb +349 -0
  134. data/spec/monkey_patches.rb +182 -0
  135. data/spec/spec_helper.rb +62 -0
  136. data/spec/support/configuration_stubbing.rb +8 -0
  137. data/spec/support/cucumber_helpers.rb +35 -0
  138. data/spec/support/fixnum_extension.rb +10 -0
  139. data/spec/support/http_library_adapters.rb +289 -0
  140. data/spec/support/limited_uri.rb +21 -0
  141. data/spec/support/ruby_interpreter.rb +7 -0
  142. data/spec/support/shared_example_groups/excon.rb +63 -0
  143. data/spec/support/shared_example_groups/hook_into_http_library.rb +594 -0
  144. data/spec/support/shared_example_groups/request_hooks.rb +59 -0
  145. data/spec/support/sinatra_app.rb +86 -0
  146. data/spec/support/vcr_localhost_server.rb +76 -0
  147. data/spec/support/vcr_stub_helpers.rb +17 -0
  148. metadata +677 -0
@@ -0,0 +1,164 @@
1
+ require 'vcr/util/version_checker'
2
+ require 'vcr/request_handler'
3
+ require 'webmock'
4
+
5
+ VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0').check_version!
6
+
7
+ module VCR
8
+ class LibraryHooks
9
+ # @private
10
+ module WebMock
11
+ extend self
12
+
13
+ attr_accessor :global_hook_disabled
14
+ alias global_hook_disabled? global_hook_disabled
15
+
16
+ def with_global_hook_disabled
17
+ self.global_hook_disabled = true
18
+
19
+ begin
20
+ yield
21
+ ensure
22
+ self.global_hook_disabled = false
23
+ end
24
+ end
25
+
26
+ # @private
27
+ module Helpers
28
+ def vcr_request_for(webmock_request)
29
+ VCR::Request.new \
30
+ webmock_request.method,
31
+ webmock_request.uri.to_s,
32
+ webmock_request.body,
33
+ request_headers_for(webmock_request)
34
+ end
35
+
36
+ # @private
37
+ def vcr_response_for(webmock_response)
38
+ VCR::Response.new \
39
+ VCR::ResponseStatus.new(*webmock_response.status),
40
+ webmock_response.headers,
41
+ webmock_response.body,
42
+ nil
43
+ end
44
+
45
+ if defined?(::Excon)
46
+ # @private
47
+ def request_headers_for(webmock_request)
48
+ return nil unless webmock_request.headers
49
+
50
+ # WebMock hooks deeply into a Excon at a place where it manually adds a "Host"
51
+ # header, but this isn't a header we actually care to store...
52
+ webmock_request.headers.dup.tap do |headers|
53
+ headers.delete("Host")
54
+ end
55
+ end
56
+ else
57
+ # @private
58
+ def request_headers_for(webmock_request)
59
+ webmock_request.headers
60
+ end
61
+ end
62
+
63
+ def typed_request_for(webmock_request, remove = false)
64
+ if webmock_request.instance_variables.find { |v| v.to_sym == :@__typed_vcr_request }
65
+ meth = remove ? :remove_instance_variable : :instance_variable_get
66
+ return webmock_request.send(meth, :@__typed_vcr_request)
67
+ end
68
+
69
+ warn <<-EOS.gsub(/^\s+\|/, '')
70
+ |WARNING: There appears to be a bug in WebMock's after_request hook
71
+ | and VCR is attempting to work around it. Some VCR features
72
+ | may not work properly.
73
+ EOS
74
+
75
+ Request::Typed.new(vcr_request_for(webmock_request), :unknown)
76
+ end
77
+ end
78
+
79
+ class RequestHandler < ::VCR::RequestHandler
80
+ include Helpers
81
+
82
+ attr_reader :request
83
+ def initialize(request)
84
+ @request = request
85
+ end
86
+
87
+ private
88
+
89
+ def externally_stubbed?
90
+ # prevent infinite recursion...
91
+ VCR::LibraryHooks::WebMock.with_global_hook_disabled do
92
+ ::WebMock.registered_request?(request)
93
+ end
94
+ end
95
+
96
+ def set_typed_request_for_after_hook(*args)
97
+ super
98
+ request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
99
+ end
100
+
101
+ def vcr_request
102
+ @vcr_request ||= vcr_request_for(request)
103
+ end
104
+
105
+ def on_externally_stubbed_request
106
+ # nil allows WebMock to handle the request
107
+ nil
108
+ end
109
+
110
+ def on_unhandled_request
111
+ invoke_after_request_hook(nil)
112
+ super
113
+ end
114
+
115
+ def on_stubbed_by_vcr_request
116
+ {
117
+ :body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
118
+ :status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
119
+ :headers => stubbed_response.headers
120
+ }
121
+ end
122
+ end
123
+
124
+ extend Helpers
125
+
126
+ ::WebMock.globally_stub_request do |req|
127
+ global_hook_disabled? ? nil : RequestHandler.new(req).handle
128
+ end
129
+
130
+ ::WebMock.after_request(:real_requests_only => true) do |request, response|
131
+ unless VCR.library_hooks.disabled?(:webmock)
132
+ http_interaction = VCR::HTTPInteraction.new \
133
+ typed_request_for(request), vcr_response_for(response)
134
+
135
+ VCR.record_http_interaction(http_interaction)
136
+ end
137
+ end
138
+
139
+ ::WebMock.after_request do |request, response|
140
+ unless VCR.library_hooks.disabled?(:webmock)
141
+ VCR.configuration.invoke_hook \
142
+ :after_http_request,
143
+ typed_request_for(request, :remove),
144
+ vcr_response_for(response)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ # @private
152
+ module WebMock
153
+ class << self
154
+ # ensure HTTP requests are always allowed; VCR takes care of disallowing
155
+ # them at the appropriate times in its hook
156
+ def net_connect_allowed_with_vcr?(*args)
157
+ VCR.turned_on? ? true : net_connect_allowed_without_vcr?(*args)
158
+ end
159
+
160
+ alias net_connect_allowed_without_vcr? net_connect_allowed?
161
+ alias net_connect_allowed? net_connect_allowed_with_vcr?
162
+ end unless respond_to?(:net_connect_allowed_with_vcr?)
163
+ end
164
+
@@ -0,0 +1,221 @@
1
+ require 'excon'
2
+ require 'vcr/request_handler'
3
+ require 'vcr/util/version_checker'
4
+
5
+ VCR::VersionChecker.new('Excon', Excon::VERSION, '0.25.2').check_version!
6
+
7
+ module VCR
8
+ # Contains middlewares for use with different libraries.
9
+ module Middleware
10
+ # Contains Excon middlewares.
11
+ module Excon
12
+ # One part of the Excon middleware that uses VCR to record
13
+ # and replay HTTP requests made through Excon.
14
+ #
15
+ # @private
16
+ class Request < ::Excon::Middleware::Base
17
+ # @private
18
+ def request_call(params)
19
+ params[:vcr_request_handler] = request_handler = RequestHandler.new
20
+ request_handler.before_request(params)
21
+
22
+ super
23
+ end
24
+ end
25
+
26
+ # One part of the Excon middleware that uses VCR to record
27
+ # and replay HTTP requests made through Excon.
28
+ #
29
+ # @private
30
+ class Response < ::Excon::Middleware::Base
31
+ # @private
32
+ def response_call(params)
33
+ complete_request(params)
34
+ super
35
+ end
36
+
37
+ def error_call(params)
38
+ complete_request(params)
39
+ super
40
+ end
41
+
42
+ private
43
+
44
+ def complete_request(params)
45
+ if handler = params.delete(:vcr_request_handler)
46
+ handler.after_request(params[:response])
47
+ end
48
+ end
49
+ end
50
+
51
+ # Handles a single Excon request.
52
+ #
53
+ # @private
54
+ class RequestHandler < ::VCR::RequestHandler
55
+ def initialize
56
+ @request_params = nil
57
+ @response_body_reader = nil
58
+ @should_record = false
59
+ end
60
+
61
+ # Performs before_request processing based on the provided
62
+ # request_params.
63
+ #
64
+ # @private
65
+ def before_request(request_params)
66
+ @request_params = request_params
67
+ @response_body_reader = create_response_body_reader
68
+ handle
69
+ end
70
+
71
+ # Performs after_request processing based on the provided response.
72
+ #
73
+ # @private
74
+ def after_request(response)
75
+ vcr_response = vcr_response_for(response)
76
+
77
+ if should_record?
78
+ VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, vcr_response))
79
+ end
80
+
81
+ invoke_after_request_hook(vcr_response)
82
+ end
83
+
84
+ def ensure_response_body_can_be_read_for_error_case
85
+ # Excon does not invoke the `:response_block` when an error
86
+ # has occurred, so we need to be sure to use the non-streaming
87
+ # body reader.
88
+ @response_body_reader = NonStreamingResponseBodyReader
89
+ end
90
+
91
+ attr_reader :request_params, :response_body_reader
92
+
93
+ private
94
+
95
+ def externally_stubbed?
96
+ !!::Excon.stub_for(request_params)
97
+ end
98
+
99
+ def should_record?
100
+ @should_record
101
+ end
102
+
103
+ def on_stubbed_by_vcr_request
104
+ request_params[:response] = {
105
+ :body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
106
+ :headers => normalized_headers(stubbed_response.headers || {}),
107
+ :status => stubbed_response.status.code
108
+ }
109
+ end
110
+
111
+ def on_recordable_request
112
+ @should_record = true
113
+ end
114
+
115
+ def create_response_body_reader
116
+ block = request_params[:response_block]
117
+ return NonStreamingResponseBodyReader unless block
118
+
119
+ StreamingResponseBodyReader.new(block).tap do |response_block_wrapper|
120
+ request_params[:response_block] = response_block_wrapper
121
+ end
122
+ end
123
+
124
+ def vcr_request
125
+ @vcr_request ||= begin
126
+ headers = request_params[:headers].dup
127
+ headers.delete("Host")
128
+
129
+ VCR::Request.new \
130
+ request_params[:method],
131
+ uri,
132
+ request_params[:body],
133
+ headers
134
+ end
135
+ end
136
+
137
+ def vcr_response_for(response)
138
+ return nil if response.nil?
139
+
140
+ VCR::Response.new(
141
+ VCR::ResponseStatus.new(response.fetch(:status), nil),
142
+ response.fetch(:headers),
143
+ response_body_reader.read_body_from(response),
144
+ nil
145
+ )
146
+ end
147
+
148
+ def normalized_headers(headers)
149
+ normalized = {}
150
+ headers.each do |k, v|
151
+ v = v.join(', ') if v.respond_to?(:join)
152
+ normalized[k] = v
153
+ end
154
+ normalized
155
+ end
156
+
157
+ if defined?(::Excon::Utils) && ::Excon::Utils.respond_to?(:request_uri)
158
+ def uri
159
+ @uri ||= "#{::Excon::Utils.request_uri(request_params)}"
160
+ end
161
+ else
162
+ require 'vcr/middleware/excon/legacy_methods'
163
+ include LegacyMethods
164
+
165
+ def uri
166
+ @uri ||= "#{request_params[:scheme]}://#{request_params[:host]}:#{request_params[:port]}#{request_params[:path]}#{query}"
167
+ end
168
+ end
169
+ end
170
+
171
+ # Wraps an Excon streaming `:response_block`, so that we can
172
+ # accumulate the response as it streams back from the real HTTP
173
+ # server in order to record it.
174
+ #
175
+ # @private
176
+ class StreamingResponseBodyReader
177
+ def initialize(response_block)
178
+ @response_block = response_block
179
+ @chunks = []
180
+ end
181
+
182
+ # @private
183
+ def call(chunk, remaining_bytes, total_bytes)
184
+ @chunks << chunk
185
+ @response_block.call(chunk, remaining_bytes, total_bytes)
186
+ end
187
+
188
+ # Provides a duck-typed interface that matches that of
189
+ # `NonStreamingResponseBodyReader`. The request handler
190
+ # will use this to get the response body.
191
+ #
192
+ # @private
193
+ def read_body_from(response_params)
194
+ if @chunks.none?
195
+ # Not sure why, but sometimes the body comes through the params
196
+ # instead of via the streaming block even when the block was
197
+ # configured.
198
+ response_params[:body]
199
+ else
200
+ @chunks.join('')
201
+ end
202
+ end
203
+ end
204
+
205
+ # Reads the body when no streaming is done.
206
+ #
207
+ # @private
208
+ class NonStreamingResponseBodyReader
209
+ # Provides a duck-typed interface that matches that of
210
+ # `StreamingResponseBodyReader`. The request handler
211
+ # will use this to get the response body.
212
+ #
213
+ # @private
214
+ def self.read_body_from(response_params)
215
+ response_params.fetch(:body)
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+
@@ -0,0 +1,33 @@
1
+ module VCR
2
+ module Middleware
3
+ module Excon
4
+ # Contains legacy methods only needed when integrating with older versions
5
+ # of Excon.
6
+ # @api private
7
+ module LegacyMethods
8
+ # based on:
9
+ # https://github.com/geemus/excon/blob/v0.7.8/lib/excon/connection.rb#L117-132
10
+ def query
11
+ @query ||= case request_params[:query]
12
+ when String
13
+ "?#{request_params[:query]}"
14
+ when Hash
15
+ qry = '?'
16
+ for key, values in request_params[:query]
17
+ if values.nil?
18
+ qry << key.to_s << '&'
19
+ else
20
+ for value in [*values]
21
+ qry << key.to_s << '=' << CGI.escape(value.to_s) << '&'
22
+ end
23
+ end
24
+ end
25
+ qry.chop! # remove trailing '&'
26
+ else
27
+ ''
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,118 @@
1
+ require 'faraday'
2
+ require 'vcr/util/version_checker'
3
+ require 'vcr/request_handler'
4
+
5
+ VCR::VersionChecker.new('Faraday', Faraday::VERSION, '0.7.0').check_version!
6
+
7
+ module VCR
8
+ # Contains middlewares for use with different libraries.
9
+ module Middleware
10
+ # Faraday middleware that VCR uses to record and replay HTTP requests made through
11
+ # Faraday.
12
+ #
13
+ # @note You can either insert this middleware into the Faraday middleware stack
14
+ # yourself or configure {VCR::Configuration#hook_into} to hook into `:faraday`.
15
+ class Faraday
16
+ include VCR::Deprecations::Middleware::Faraday
17
+
18
+ # Constructs a new instance of the Faraday middleware.
19
+ #
20
+ # @param [#call] app the faraday app
21
+ def initialize(app)
22
+ super
23
+ @app = app
24
+ end
25
+
26
+ # Handles the HTTP request being made through Faraday
27
+ #
28
+ # @param [Hash] env the Faraday request env hash
29
+ def call(env)
30
+ return @app.call(env) if VCR.library_hooks.disabled?(:faraday)
31
+ RequestHandler.new(@app, env).handle
32
+ end
33
+
34
+ # @private
35
+ class RequestHandler < ::VCR::RequestHandler
36
+ attr_reader :app, :env
37
+ def initialize(app, env)
38
+ @app, @env = app, env
39
+ @has_on_complete_hook = false
40
+ end
41
+
42
+ def handle
43
+ # Faraday must be exlusive here in case another library hook is being used.
44
+ # We don't want double recording/double playback.
45
+ VCR.library_hooks.exclusive_hook = :faraday
46
+ super
47
+ ensure
48
+ response = defined?(@vcr_response) ? @vcr_response : nil
49
+ invoke_after_request_hook(response) unless delay_finishing?
50
+ end
51
+
52
+ private
53
+
54
+ def delay_finishing?
55
+ !!env[:parallel_manager] && @has_on_complete_hook
56
+ end
57
+
58
+ def vcr_request
59
+ @vcr_request ||= VCR::Request.new \
60
+ env[:method],
61
+ env[:url].to_s,
62
+ raw_body_from(env[:body]),
63
+ env[:request_headers]
64
+ end
65
+
66
+ def raw_body_from(body)
67
+ return body unless body.respond_to?(:read)
68
+
69
+ body.read.tap do |b|
70
+ body.rewind if body.respond_to?(:rewind)
71
+ end
72
+ end
73
+
74
+ def response_for(response)
75
+ VCR::Response.new(
76
+ VCR::ResponseStatus.new(response.status, nil),
77
+ response.headers,
78
+ raw_body_from(response.body),
79
+ nil
80
+ )
81
+ end
82
+
83
+ def on_ignored_request
84
+ response = app.call(env)
85
+ @vcr_response = response_for(response)
86
+ response
87
+ end
88
+
89
+ def on_stubbed_by_vcr_request
90
+ headers = env[:response_headers] ||= ::Faraday::Utils::Headers.new
91
+ headers.update stubbed_response.headers if stubbed_response.headers
92
+ env.update :status => stubbed_response.status.code, :body => stubbed_response.body
93
+
94
+ @vcr_response = stubbed_response
95
+
96
+ faraday_response = ::Faraday::Response.new
97
+ faraday_response.finish(env)
98
+ env[:response] = faraday_response
99
+ end
100
+
101
+ def on_recordable_request
102
+ @has_on_complete_hook = true
103
+ response = app.call(env)
104
+ response.on_complete do
105
+ @vcr_response = response_for(response)
106
+ VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, @vcr_response))
107
+ invoke_after_request_hook(@vcr_response) if delay_finishing?
108
+ end
109
+ end
110
+
111
+ def invoke_after_request_hook(response)
112
+ super
113
+ VCR.library_hooks.exclusive_hook = nil
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end