vcr 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 806e6d86ccbe7132de58cc1313e0aef6ab0c2d8d
4
- data.tar.gz: e86dd3056697f943fe96721fd2e58163fa900c1e
2
+ SHA256:
3
+ metadata.gz: fb0121a881f4d4b1ac0f68b5078f5674f137e07a4a0580b70a438f920c5953db
4
+ data.tar.gz: 551623fd6fd0317c2510e1a02c36f6a8c3cac4bba72752ffd31907de0e1bfef9
5
5
  SHA512:
6
- metadata.gz: 7549fff407299870a1d989c3627a371b1a77d5054ba42b9a3ee52abd1e579acccbe282a1e7077f3f7bf14eceef2ed6a04d8a7a7578895061d6f858436c07be01
7
- data.tar.gz: 427619bde2eb934e06da74ba0888544efc574f437387cd6e00b7e8e3736f760bb5461626e697e4d9725c3d53103d22d9c589f1d809c3aa062550ab9c6fe3d746
6
+ metadata.gz: d6ad287e1f993836c3adac326228312596935313d65313fb60b07b16a962b0b5b9cff3f74db5200ce21e2c5ba5b6e32da0fd445d73fe7024c35308fecfcdc688
7
+ data.tar.gz: bcf77106091efc49ae8ee789f076b162eb28491349a2e04ef0a1220ae5b1cfb3501de2e3106d8aa14459c7577207775650232ebf11235fe110c28dfa16614149
data/lib/vcr.rb CHANGED
@@ -202,10 +202,13 @@ module VCR
202
202
  # # make multiple HTTP requests
203
203
  # end
204
204
  def use_cassettes(cassettes, &block)
205
- return use_cassette(cassettes.last[:name]) { block.call } if cassettes.length == 1
206
205
  cassette = cassettes.pop
207
- use_cassette(cassette[:name], cassette[:options]) do
208
- use_cassettes(cassettes, &block)
206
+ use_cassette(cassette[:name], cassette[:options] || {}) do
207
+ if cassettes.empty?
208
+ block.call
209
+ else
210
+ use_cassettes(cassettes, &block)
211
+ end
209
212
  end
210
213
  end
211
214
 
@@ -48,6 +48,7 @@ module VCR
48
48
  def initialize(name, options = {})
49
49
  @name = name
50
50
  @options = VCR.configuration.default_cassette_options.merge(options)
51
+ @mutex = Mutex.new
51
52
 
52
53
  assert_valid_options!
53
54
  extract_options
@@ -74,12 +75,16 @@ module VCR
74
75
 
75
76
  # @private
76
77
  def http_interactions
77
- @http_interactions ||= HTTPInteractionList.new \
78
- should_stub_requests? ? previously_recorded_interactions : [],
79
- match_requests_on,
80
- @allow_playback_repeats,
81
- @parent_list,
82
- log_prefix
78
+ # Without this mutex, under threaded access, an HTTPInteractionList will overwrite
79
+ # the first.
80
+ @mutex.synchronize do
81
+ @http_interactions ||= HTTPInteractionList.new \
82
+ should_stub_requests? ? previously_recorded_interactions : [],
83
+ match_requests_on,
84
+ @allow_playback_repeats,
85
+ @parent_list,
86
+ log_prefix
87
+ end
83
88
  end
84
89
 
85
90
  # @private
@@ -149,7 +154,7 @@ module VCR
149
154
  :record, :erb, :match_requests_on, :re_record_interval, :tag, :tags,
150
155
  :update_content_length_header, :allow_playback_repeats, :allow_unused_http_interactions,
151
156
  :exclusive, :serialize_with, :preserve_exact_body_bytes, :decode_compressed_response,
152
- :persist_with, :clean_outdated_http_interactions
157
+ :recompress_response, :persist_with, :clean_outdated_http_interactions
153
158
  ]
154
159
 
155
160
  if invalid_options.size > 0
@@ -175,7 +180,7 @@ module VCR
175
180
  def assign_tags
176
181
  @tags = Array(@options.fetch(:tags) { @options[:tag] })
177
182
 
178
- [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response].each do |tag|
183
+ [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response, :recompress_response].each do |tag|
179
184
  @tags << tag if @options[tag]
180
185
  end
181
186
  end
@@ -22,21 +22,26 @@ module VCR
22
22
  @parent_list = parent_list
23
23
  @used_interactions = []
24
24
  @log_prefix = log_prefix
25
+ @mutex = Mutex.new
25
26
 
26
27
  interaction_summaries = interactions.map { |i| "#{request_summary(i.request)} => #{response_summary(i.response)}" }
27
28
  log "Initialized HTTPInteractionList with request matchers #{request_matchers.inspect} and #{interactions.size} interaction(s): { #{interaction_summaries.join(', ')} }", 1
28
29
  end
29
30
 
30
31
  def response_for(request)
31
- if index = matching_interaction_index_for(request)
32
- interaction = @interactions.delete_at(index)
33
- @used_interactions.unshift interaction
34
- log "Found matching interaction for #{request_summary(request)} at index #{index}: #{response_summary(interaction.response)}", 1
35
- interaction.response
36
- elsif interaction = matching_used_interaction_for(request)
37
- interaction.response
38
- else
39
- @parent_list.response_for(request)
32
+ # Without this mutex, under threaded access, the wrong response may be removed
33
+ # out of the (remaining) interactions list (and other problems).
34
+ @mutex.synchronize do
35
+ if index = matching_interaction_index_for(request)
36
+ interaction = @interactions.delete_at(index)
37
+ @used_interactions.unshift interaction
38
+ log "Found matching interaction for #{request_summary(request)} at index #{index}: #{response_summary(interaction.response)}", 1
39
+ interaction.response
40
+ elsif interaction = matching_used_interaction_for(request)
41
+ interaction.response
42
+ else
43
+ @parent_list.response_for(request)
44
+ end
40
45
  end
41
46
  end
42
47
 
@@ -45,11 +45,6 @@ module VCR
45
45
  "recorded_with" => "VCR #{VCR.version}"
46
46
  }
47
47
 
48
- def hash.each
49
- yield 'http_interactions', self['http_interactions']
50
- yield 'recorded_with', self['recorded_with']
51
- end
52
-
53
48
  File.open(cassette, 'w') { |f| f.write ::YAML.dump(hash) }
54
49
  @out.puts " - Migrated #{relative_casssette_name(cassette)}"
55
50
  end
@@ -17,9 +17,9 @@ module VCR
17
17
 
18
18
  # The file extension to use for this serializer.
19
19
  #
20
- # @return [String] "gz"
20
+ # @return [String] "zz"
21
21
  def file_extension
22
- 'gz'
22
+ 'zz'
23
23
  end
24
24
 
25
25
  # Serializes the given hash using YAML and Zlib.
@@ -77,6 +77,15 @@ module VCR
77
77
  end
78
78
  alias ignore_host ignore_hosts
79
79
 
80
+ # Specifies host(s) that VCR should stop ignoring.
81
+ #
82
+ # @param hosts [Array<String>] List of hosts to unignore
83
+ # @see #ignore_hosts
84
+ def unignore_hosts(*hosts)
85
+ VCR.request_ignorer.unignore_hosts(*hosts)
86
+ end
87
+ alias unignore_host unignore_hosts
88
+
80
89
  # Sets whether or not VCR should ignore localhost requests.
81
90
  #
82
91
  # @param value [Boolean] the value to set
@@ -222,7 +231,7 @@ module VCR
222
231
 
223
232
  before_playback(tag) do |interaction|
224
233
  orig_text = call_block(block, interaction)
225
- log "before_playback: replacing #{placeholder.inspect} with #{orig_text.inspect}"
234
+ log "before_playback: replacing #{orig_text.inspect} with #{placeholder.inspect}"
226
235
  interaction.filter!(placeholder, orig_text)
227
236
  end
228
237
  end
@@ -550,6 +559,10 @@ module VCR
550
559
  end
551
560
 
552
561
  def register_built_in_hooks
562
+ before_playback(:recompress_response) do |interaction|
563
+ interaction.response.recompress if interaction.response.vcr_decompressed?
564
+ end
565
+
553
566
  before_playback(:update_content_length_header) do |interaction|
554
567
  interaction.response.update_content_length_header
555
568
  end
@@ -571,4 +584,3 @@ module VCR
571
584
  define_hook :after_library_hooks_loaded
572
585
  end
573
586
  end
574
-
@@ -88,18 +88,7 @@ module VCR
88
88
  end
89
89
 
90
90
  def current_cassettes
91
- @cassettes ||= begin
92
- cassettes = VCR.cassettes.to_a.reverse
93
-
94
- begin
95
- loop do
96
- break unless VCR.eject_cassette
97
- end
98
- rescue EjectLinkedCassetteError
99
- end
100
-
101
- cassettes
102
- end
91
+ @cassettes ||= VCR.cassettes.to_a.reverse
103
92
  end
104
93
 
105
94
  def request_description
@@ -22,6 +22,14 @@ VCR.configuration.after_library_hooks_loaded do
22
22
  # (i.e. to double record requests or whatever).
23
23
  if defined?(WebMock::HttpLibAdapters::ExconAdapter)
24
24
  WebMock::HttpLibAdapters::ExconAdapter.disable!
25
+
26
+ if defined?(::RSpec)
27
+ ::RSpec.configure do |config|
28
+ config.before(:suite) do
29
+ WebMock::HttpLibAdapters::ExconAdapter.disable!
30
+ end
31
+ end
32
+ end
25
33
  end
26
34
  end
27
35
 
@@ -29,6 +29,10 @@ module VCR
29
29
  ignored_hosts.merge(hosts)
30
30
  end
31
31
 
32
+ def unignore_hosts(*hosts)
33
+ ignored_hosts.subtract(hosts)
34
+ end
35
+
32
36
  def ignore?(request)
33
37
  invoke_hook(:ignore_request, request).any?
34
38
  end
@@ -40,4 +44,3 @@ module VCR
40
44
  end
41
45
  end
42
46
  end
43
-
@@ -138,7 +138,7 @@ module VCR
138
138
 
139
139
  register(:body_as_json) do |r1, r2|
140
140
  begin
141
- JSON.parse(r1.body) == JSON.parse(r2.body)
141
+ r1.body == r2.body || JSON.parse(r1.body) == JSON.parse(r2.body)
142
142
  rescue JSON::ParserError
143
143
  false
144
144
  end
@@ -167,25 +167,6 @@ module VCR
167
167
  end
168
168
  end
169
169
 
170
- # @private
171
- module OrderedHashSerializer
172
- def each
173
- @ordered_keys.each do |key|
174
- yield key, self[key] if has_key?(key)
175
- end
176
- end
177
-
178
- if RUBY_VERSION.to_f > 1.8
179
- # 1.9+ hashes are already ordered.
180
- def self.apply_to(*args); end
181
- else
182
- def self.apply_to(hash, keys)
183
- hash.instance_variable_set(:@ordered_keys, keys)
184
- hash.extend self
185
- end
186
- end
187
- end
188
-
189
170
  # The request of an {HTTPInteraction}.
190
171
  #
191
172
  # @attr [Symbol] method the HTTP method (i.e. :head, :options, :get, :post, :put, :patch or :delete)
@@ -219,7 +200,7 @@ module VCR
219
200
  'uri' => uri,
220
201
  'body' => serializable_body,
221
202
  'headers' => headers
222
- }.tap { |h| OrderedHashSerializer.apply_to(h, members) }
203
+ }
223
204
  end
224
205
 
225
206
  # Constructs a new instance from a hash.
@@ -369,7 +350,6 @@ module VCR
369
350
  'http_version' => http_version
370
351
  }.tap do |hash|
371
352
  hash['adapter_metadata'] = adapter_metadata unless adapter_metadata.empty?
372
- OrderedHashSerializer.apply_to(hash, members)
373
353
  end
374
354
  end
375
355
 
@@ -403,6 +383,11 @@ module VCR
403
383
  %w[ gzip deflate ].include? content_encoding
404
384
  end
405
385
 
386
+ # Checks if VCR decompressed the response body
387
+ def vcr_decompressed?
388
+ adapter_metadata['vcr_decompressed']
389
+ end
390
+
406
391
  # Decodes the compressed body and deletes evidence that it was ever compressed.
407
392
  #
408
393
  # @return self
@@ -412,11 +397,43 @@ module VCR
412
397
  self.class.decompress(body, content_encoding) { |new_body|
413
398
  self.body = new_body
414
399
  update_content_length_header
400
+ adapter_metadata['vcr_decompressed'] = content_encoding
415
401
  delete_header('Content-Encoding')
416
402
  }
417
403
  return self
418
404
  end
419
405
 
406
+ # Recompresses the decompressed body according to adapter metadata.
407
+ #
408
+ # @raise [VCR::Errors::UnknownContentEncodingError] if the content encoding
409
+ # stored in the adapter metadata is unknown
410
+ def recompress
411
+ type = adapter_metadata['vcr_decompressed']
412
+ new_body = begin
413
+ case type
414
+ when 'gzip'
415
+ body_str = ''
416
+ args = [StringIO.new(body_str)]
417
+ args << { :encoding => 'ASCII-8BIT' } if ''.respond_to?(:encoding)
418
+ writer = Zlib::GzipWriter.new(*args)
419
+ writer.write(body)
420
+ writer.close
421
+ body_str
422
+ when 'deflate'
423
+ Zlib::Deflate.inflate(body)
424
+ when 'identity', NilClass
425
+ nil
426
+ else
427
+ raise Errors::UnknownContentEncodingError, "unknown content encoding: #{type}"
428
+ end
429
+ end
430
+ if new_body
431
+ self.body = new_body
432
+ update_content_length_header
433
+ headers['Content-Encoding'] = type
434
+ end
435
+ end
436
+
420
437
  begin
421
438
  require 'zlib'
422
439
  require 'stringio'
@@ -463,7 +480,7 @@ module VCR
463
480
  def to_hash
464
481
  {
465
482
  'code' => code, 'message' => message
466
- }.tap { |h| OrderedHashSerializer.apply_to(h, members) }
483
+ }
467
484
  end
468
485
 
469
486
  # Constructs a new instance from a hash.
@@ -496,9 +513,7 @@ module VCR
496
513
  'request' => request.to_hash,
497
514
  'response' => response.to_hash,
498
515
  'recorded_at' => recorded_at.httpdate
499
- }.tap do |hash|
500
- OrderedHashSerializer.apply_to(hash, members)
501
- end
516
+ }
502
517
  end
503
518
 
504
519
  # Constructs a new instance from a hash.
@@ -1,31 +1,25 @@
1
1
  module VCR
2
2
  # Ruby 1.8 provides Ping.pingecho, but it was removed in 1.9.
3
- # So we try requiring it, and if that fails, define it ourselves.
4
- begin
5
- require 'ping'
6
- Ping = ::Ping
7
- rescue LoadError
8
- # This is copied, verbatim, from Ruby 1.8.7's ping.rb.
9
- require 'timeout'
10
- require "socket"
3
+ # This is copied, verbatim, from Ruby 1.8.7's ping.rb.
4
+ require 'timeout'
5
+ require "socket"
11
6
 
12
- # @private
13
- module Ping
14
- def pingecho(host, timeout=5, service="echo")
15
- begin
16
- Timeout.timeout(timeout) do
17
- s = TCPSocket.new(host, service)
18
- s.close
19
- end
20
- rescue Errno::ECONNREFUSED
21
- return true
22
- rescue Timeout::Error, StandardError
23
- return false
7
+ # @private
8
+ module Ping
9
+ def pingecho(host, timeout=5, service="echo")
10
+ begin
11
+ Timeout.timeout(timeout) do
12
+ s = TCPSocket.new(host, service)
13
+ s.close
24
14
  end
15
+ rescue Errno::ECONNREFUSED
25
16
  return true
17
+ rescue Timeout::Error, StandardError
18
+ return false
26
19
  end
27
- module_function :pingecho
20
+ return true
28
21
  end
22
+ module_function :pingecho
29
23
  end
30
24
 
31
25
  # @private
@@ -10,7 +10,7 @@ module VCR
10
10
  # * `parts` [Array<Integer>] List of the version parts.
11
11
  def version
12
12
  @version ||= begin
13
- string = '4.0.0'
13
+ string = '5.0.0'
14
14
 
15
15
  def string.parts
16
16
  split('.').map { |p| p.to_i }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vcr
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Myron Marston
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-04 00:00:00.000000000 Z
11
+ date: 2019-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -210,16 +210,16 @@ dependencies:
210
210
  name: excon
211
211
  requirement: !ruby/object:Gem::Requirement
212
212
  requirements:
213
- - - ">="
213
+ - - '='
214
214
  - !ruby/object:Gem::Version
215
- version: '0'
215
+ version: 0.62.0
216
216
  type: :development
217
217
  prerelease: false
218
218
  version_requirements: !ruby/object:Gem::Requirement
219
219
  requirements:
220
- - - ">="
220
+ - - '='
221
221
  - !ruby/object:Gem::Version
222
- version: '0'
222
+ version: 0.62.0
223
223
  - !ruby/object:Gem::Dependency
224
224
  name: timecop
225
225
  requirement: !ruby/object:Gem::Requirement
@@ -371,8 +371,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
371
371
  - !ruby/object:Gem::Version
372
372
  version: '0'
373
373
  requirements: []
374
- rubyforge_project:
375
- rubygems_version: 2.6.13
374
+ rubygems_version: 3.0.3
376
375
  signing_key:
377
376
  specification_version: 4
378
377
  summary: Record your test suite's HTTP interactions and replay them during future