vcr 3.0.3 → 6.2.0

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 (146) hide show
  1. checksums.yaml +5 -5
  2. data/lib/vcr/cassette/erb_renderer.rb +4 -2
  3. data/lib/vcr/cassette/http_interaction_list.rb +14 -9
  4. data/lib/vcr/cassette/migrator.rb +5 -6
  5. data/lib/vcr/cassette/persisters/file_system.rb +9 -1
  6. data/lib/vcr/cassette/serializers/compressed.rb +2 -2
  7. data/lib/vcr/cassette/serializers/json.rb +14 -8
  8. data/lib/vcr/cassette/serializers/psych.rb +10 -2
  9. data/lib/vcr/cassette/serializers/syck.rb +7 -1
  10. data/lib/vcr/cassette/serializers/yaml.rb +14 -2
  11. data/lib/vcr/cassette/serializers.rb +10 -0
  12. data/lib/vcr/cassette.rb +63 -16
  13. data/lib/vcr/configuration.rb +21 -8
  14. data/lib/vcr/deprecations.rb +0 -62
  15. data/lib/vcr/errors.rb +17 -12
  16. data/lib/vcr/library_hooks/excon.rb +8 -0
  17. data/lib/vcr/library_hooks/typhoeus.rb +91 -79
  18. data/lib/vcr/library_hooks/webmock.rb +2 -11
  19. data/lib/vcr/linked_cassette.rb +4 -4
  20. data/lib/vcr/middleware/excon.rb +1 -1
  21. data/lib/vcr/middleware/faraday.rb +29 -2
  22. data/lib/vcr/request_ignorer.rb +8 -1
  23. data/lib/vcr/request_matcher_registry.rb +3 -3
  24. data/lib/vcr/structs.rb +48 -32
  25. data/lib/vcr/test_frameworks/cucumber.rb +16 -5
  26. data/lib/vcr/test_frameworks/rspec.rb +34 -22
  27. data/lib/vcr/util/hooks.rb +1 -0
  28. data/lib/vcr/util/internet_connection.rb +15 -21
  29. data/lib/vcr/version.rb +2 -2
  30. data/lib/vcr.rb +52 -2
  31. metadata +45 -272
  32. data/features/CHANGELOG.md +0 -710
  33. data/features/CONTRIBUTING.md +0 -26
  34. data/features/LICENSE.md +0 -20
  35. data/features/README.md +0 -339
  36. data/features/Upgrade.md +0 -289
  37. data/features/about_these_examples.md +0 -18
  38. data/features/cassettes/allow_unused_http_interactions.feature +0 -100
  39. data/features/cassettes/automatic_re_recording.feature +0 -72
  40. data/features/cassettes/decompress.feature +0 -74
  41. data/features/cassettes/dynamic_erb.feature +0 -100
  42. data/features/cassettes/exclusive.feature +0 -126
  43. data/features/cassettes/format.feature +0 -411
  44. data/features/cassettes/freezing_time.feature +0 -68
  45. data/features/cassettes/naming.feature +0 -28
  46. data/features/cassettes/no_cassette.feature +0 -152
  47. data/features/cassettes/update_content_length_header.feature +0 -112
  48. data/features/configuration/allow_http_connections_when_no_cassette.feature +0 -55
  49. data/features/configuration/cassette_library_dir.feature +0 -31
  50. data/features/configuration/debug_logging.feature +0 -58
  51. data/features/configuration/default_cassette_options.feature +0 -100
  52. data/features/configuration/filter_sensitive_data.feature +0 -153
  53. data/features/configuration/hook_into.feature +0 -172
  54. data/features/configuration/ignore_request.feature +0 -192
  55. data/features/configuration/preserve_exact_body_bytes.feature +0 -108
  56. data/features/configuration/query_parser.feature +0 -84
  57. data/features/configuration/uri_parser.feature +0 -93
  58. data/features/getting_started.md +0 -82
  59. data/features/hooks/after_http_request.feature +0 -58
  60. data/features/hooks/around_http_request.feature +0 -57
  61. data/features/hooks/before_http_request.feature +0 -63
  62. data/features/hooks/before_playback.feature +0 -184
  63. data/features/hooks/before_record.feature +0 -172
  64. data/features/http_libraries/em_http_request.feature +0 -250
  65. data/features/http_libraries/net_http.feature +0 -179
  66. data/features/middleware/faraday.feature +0 -56
  67. data/features/middleware/rack.feature +0 -92
  68. data/features/record_modes/all.feature +0 -82
  69. data/features/record_modes/new_episodes.feature +0 -79
  70. data/features/record_modes/none.feature +0 -72
  71. data/features/record_modes/once.feature +0 -95
  72. data/features/request_matching/README.md +0 -30
  73. data/features/request_matching/body.feature +0 -91
  74. data/features/request_matching/body_as_json.feature +0 -90
  75. data/features/request_matching/custom_matcher.feature +0 -135
  76. data/features/request_matching/headers.feature +0 -85
  77. data/features/request_matching/host.feature +0 -95
  78. data/features/request_matching/identical_request_sequence.feature +0 -89
  79. data/features/request_matching/method.feature +0 -96
  80. data/features/request_matching/path.feature +0 -96
  81. data/features/request_matching/playback_repeats.feature +0 -98
  82. data/features/request_matching/query.feature +0 -97
  83. data/features/request_matching/uri.feature +0 -94
  84. data/features/request_matching/uri_without_param.feature +0 -101
  85. data/features/step_definitions/cli_steps.rb +0 -199
  86. data/features/support/env.rb +0 -46
  87. data/features/support/http_lib_filters.rb +0 -46
  88. data/features/test_frameworks/cucumber.feature +0 -211
  89. data/features/test_frameworks/rspec_macro.feature +0 -81
  90. data/features/test_frameworks/rspec_metadata.feature +0 -150
  91. data/features/test_frameworks/test_unit.feature +0 -49
  92. data/lib/vcr/extensions/net_http_response.rb +0 -36
  93. data/lib/vcr/library_hooks/fakeweb.rb +0 -197
  94. data/lib/vcr/library_hooks/typhoeus_0.4.rb +0 -103
  95. data/spec/acceptance/concurrency_spec.rb +0 -51
  96. data/spec/acceptance/threading_spec.rb +0 -34
  97. data/spec/fixtures/cassette_spec/1_x_cassette.yml +0 -110
  98. data/spec/fixtures/cassette_spec/empty.yml +0 -0
  99. data/spec/fixtures/cassette_spec/example.yml +0 -111
  100. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +0 -111
  101. data/spec/fixtures/fake_example_responses.yml +0 -110
  102. data/spec/fixtures/match_requests_on.yml +0 -187
  103. data/spec/lib/vcr/cassette/erb_renderer_spec.rb +0 -53
  104. data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +0 -295
  105. data/spec/lib/vcr/cassette/migrator_spec.rb +0 -196
  106. data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +0 -75
  107. data/spec/lib/vcr/cassette/persisters_spec.rb +0 -39
  108. data/spec/lib/vcr/cassette/serializers_spec.rb +0 -182
  109. data/spec/lib/vcr/cassette_spec.rb +0 -618
  110. data/spec/lib/vcr/configuration_spec.rb +0 -326
  111. data/spec/lib/vcr/deprecations_spec.rb +0 -85
  112. data/spec/lib/vcr/errors_spec.rb +0 -178
  113. data/spec/lib/vcr/extensions/net_http_response_spec.rb +0 -86
  114. data/spec/lib/vcr/library_hooks/excon_spec.rb +0 -104
  115. data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +0 -169
  116. data/spec/lib/vcr/library_hooks/faraday_spec.rb +0 -68
  117. data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +0 -36
  118. data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +0 -162
  119. data/spec/lib/vcr/library_hooks/webmock_spec.rb +0 -117
  120. data/spec/lib/vcr/library_hooks_spec.rb +0 -51
  121. data/spec/lib/vcr/middleware/faraday_spec.rb +0 -181
  122. data/spec/lib/vcr/middleware/rack_spec.rb +0 -115
  123. data/spec/lib/vcr/request_ignorer_spec.rb +0 -70
  124. data/spec/lib/vcr/request_matcher_registry_spec.rb +0 -345
  125. data/spec/lib/vcr/structs_spec.rb +0 -732
  126. data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +0 -107
  127. data/spec/lib/vcr/test_frameworks/rspec_spec.rb +0 -94
  128. data/spec/lib/vcr/util/hooks_spec.rb +0 -158
  129. data/spec/lib/vcr/util/internet_connection_spec.rb +0 -37
  130. data/spec/lib/vcr/util/version_checker_spec.rb +0 -31
  131. data/spec/lib/vcr/version_spec.rb +0 -27
  132. data/spec/lib/vcr_spec.rb +0 -354
  133. data/spec/monkey_patches.rb +0 -186
  134. data/spec/spec_helper.rb +0 -63
  135. data/spec/support/configuration_stubbing.rb +0 -8
  136. data/spec/support/cucumber_helpers.rb +0 -39
  137. data/spec/support/fixnum_extension.rb +0 -10
  138. data/spec/support/http_library_adapters.rb +0 -289
  139. data/spec/support/limited_uri.rb +0 -21
  140. data/spec/support/ruby_interpreter.rb +0 -7
  141. data/spec/support/shared_example_groups/excon.rb +0 -63
  142. data/spec/support/shared_example_groups/hook_into_http_library.rb +0 -594
  143. data/spec/support/shared_example_groups/request_hooks.rb +0 -59
  144. data/spec/support/sinatra_app.rb +0 -86
  145. data/spec/support/vcr_localhost_server.rb +0 -76
  146. data/spec/support/vcr_stub_helpers.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1b4d30cbef974c49ed29dee25cb2d8024049148a
4
- data.tar.gz: 32d2c73128d06279593abf6ba8c5168028eeec2a
2
+ SHA256:
3
+ metadata.gz: 902881c25780fa44253365c8f5fd0bb260510d7f754287d74a70275abc2dabce
4
+ data.tar.gz: fe52ddaa831a711b7415c350205a2662e833de2c4a6d58b8b6539170645bd218
5
5
  SHA512:
6
- metadata.gz: 07428a8f9ad5ffa5a0fa63617c9ca8f285fbe5ddf8a6d6bdee9eeebf972cce481b17c1036abee539dd7d255ee4e877d2dc4152f7f6aae6d9849281c16d3e13bc
7
- data.tar.gz: 95438bbea501e8ee6003f65a8bc1cbf9d3b991d44dbac7db038b7a4317f05956450ff0b438e19fc194c53609807dcc6a88cb176d4fb5cdb7a1a20a59a98b189b
6
+ metadata.gz: f79ffb02f15e691a1266c3421043a5ee95f3bd35715687137f139b291c490ee87a93351e0526faa0f29c73deba80ba41740f87d0fc9d50793a6b6d4101c7b116
7
+ data.tar.gz: 3e9276ffc9e2a2ae07f4a11054dcb484a324dc18e28a4badaa06df7454db36cfdbfbf38b95aec40c01a15a40cde56fb6cd9868780e50994fc2f554caf86e209a
@@ -32,7 +32,7 @@ module VCR
32
32
  end
33
33
 
34
34
  def erb_variables
35
- @erb if @erb.is_a?(Hash)
35
+ @erb if @erb.is_a?(Hash) && !@erb.empty?
36
36
  end
37
37
 
38
38
  def template
@@ -40,7 +40,9 @@ module VCR
40
40
  end
41
41
 
42
42
  @@struct_cache = Hash.new do |hash, attributes|
43
- hash[attributes] = Struct.new(*attributes)
43
+ attributes = attributes.map(&:to_sym)
44
+ hash[attributes] = Struct.new(*attributes) unless hash.key?(attributes)
45
+ hash[attributes]
44
46
  end
45
47
 
46
48
  def variables_object
@@ -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,17 +45,16 @@ 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
56
51
 
57
52
  def load_yaml(cassette)
58
- ::YAML.load_file(cassette)
53
+ if ::YAML.respond_to?(:unsafe_load_file)
54
+ ::YAML.unsafe_load_file(cassette)
55
+ else
56
+ ::YAML.load_file(cassette)
57
+ end
59
58
  rescue *@yaml_load_errors
60
59
  return nil
61
60
  end
@@ -55,7 +55,15 @@ module VCR
55
55
  file_extension = '.' + parts.pop
56
56
  end
57
57
 
58
- parts.join('.').gsub(/[^\w\-\/]+/, '_') + file_extension.to_s
58
+ file_name = parts.join('.').gsub(/[^[:word:]\-\/]+/, '_') + file_extension.to_s
59
+ file_name.downcase! if downcase_cassette_names?
60
+ file_name
61
+ end
62
+
63
+ def downcase_cassette_names?
64
+ !!VCR.configuration
65
+ .default_cassette_options
66
+ .dig(:persister_options, :downcase_cassette_names)
59
67
  end
60
68
  end
61
69
  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.
@@ -1,9 +1,9 @@
1
- require 'multi_json'
1
+ require 'json'
2
2
 
3
3
  module VCR
4
4
  class Cassette
5
5
  class Serializers
6
- # The JSON serializer. Uses `MultiJson` under the covers.
6
+ # The JSON serializer.
7
7
  #
8
8
  # @see Psych
9
9
  # @see Syck
@@ -11,10 +11,14 @@ module VCR
11
11
  module JSON
12
12
  extend self
13
13
  extend EncodingErrorHandling
14
+ extend SyntaxErrorHandling
14
15
 
15
16
  # @private
16
- ENCODING_ERRORS = [MultiJson::DecodeError, ArgumentError]
17
- ENCODING_ERRORS << EncodingError if defined?(EncodingError)
17
+ ENCODING_ERRORS = [ArgumentError]
18
+ ENCODING_ERRORS << ::JSON::GeneratorError
19
+
20
+ # @private
21
+ SYNTAX_ERRORS = [::JSON::ParserError]
18
22
 
19
23
  # The file extension to use for this serializer.
20
24
  #
@@ -23,23 +27,25 @@ module VCR
23
27
  "json"
24
28
  end
25
29
 
26
- # Serializes the given hash using `MultiJson`.
30
+ # Serializes the given hash using `JSON`.
27
31
  #
28
32
  # @param [Hash] hash the object to serialize
29
33
  # @return [String] the JSON string
30
34
  def serialize(hash)
31
35
  handle_encoding_errors do
32
- MultiJson.encode(hash)
36
+ ::JSON.pretty_generate(hash)
33
37
  end
34
38
  end
35
39
 
36
- # Deserializes the given string using `MultiJson`.
40
+ # Deserializes the given string using `JSON`.
37
41
  #
38
42
  # @param [String] string the JSON string
39
43
  # @return [Hash] the deserialized object
40
44
  def deserialize(string)
41
45
  handle_encoding_errors do
42
- MultiJson.decode(string)
46
+ handle_syntax_errors do
47
+ ::JSON.parse(string)
48
+ end
43
49
  end
44
50
  end
45
51
  end
@@ -11,10 +11,14 @@ module VCR
11
11
  module Psych
12
12
  extend self
13
13
  extend EncodingErrorHandling
14
+ extend SyntaxErrorHandling
14
15
 
15
16
  # @private
16
17
  ENCODING_ERRORS = [ArgumentError]
17
18
 
19
+ # @private
20
+ SYNTAX_ERRORS = [::Psych::SyntaxError]
21
+
18
22
  # The file extension to use for this serializer.
19
23
  #
20
24
  # @return [String] "yml"
@@ -28,7 +32,9 @@ module VCR
28
32
  # @return [String] the YAML string
29
33
  def serialize(hash)
30
34
  handle_encoding_errors do
31
- ::Psych.dump(hash)
35
+ result = ::Psych.dump(hash)
36
+ result.gsub!(": \n", ": null\n") # set canonical null value in order to avoid trailing whitespaces
37
+ result
32
38
  end
33
39
  end
34
40
 
@@ -38,7 +44,9 @@ module VCR
38
44
  # @return [Hash] the deserialized object
39
45
  def deserialize(string)
40
46
  handle_encoding_errors do
41
- ::Psych.load(string)
47
+ handle_syntax_errors do
48
+ ::Psych.load(string)
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -11,10 +11,14 @@ module VCR
11
11
  module Syck
12
12
  extend self
13
13
  extend EncodingErrorHandling
14
+ extend SyntaxErrorHandling
14
15
 
15
16
  # @private
16
17
  ENCODING_ERRORS = [ArgumentError]
17
18
 
19
+ # @private
20
+ SYNTAX_ERRORS = [::Psych::SyntaxError]
21
+
18
22
  # The file extension to use for this serializer.
19
23
  #
20
24
  # @return [String] "yml"
@@ -38,7 +42,9 @@ module VCR
38
42
  # @return [Hash] the deserialized object
39
43
  def deserialize(string)
40
44
  handle_encoding_errors do
41
- using_syck { ::YAML.load(string) }
45
+ handle_syntax_errors do
46
+ using_syck { ::YAML.load(string) }
47
+ end
42
48
  end
43
49
  end
44
50
 
@@ -13,10 +13,14 @@ module VCR
13
13
  module YAML
14
14
  extend self
15
15
  extend EncodingErrorHandling
16
+ extend SyntaxErrorHandling
16
17
 
17
18
  # @private
18
19
  ENCODING_ERRORS = [ArgumentError]
19
20
 
21
+ # @private
22
+ SYNTAX_ERRORS = [::Psych::SyntaxError]
23
+
20
24
  # The file extension to use for this serializer.
21
25
  #
22
26
  # @return [String] "yml"
@@ -30,7 +34,9 @@ module VCR
30
34
  # @return [String] the YAML string
31
35
  def serialize(hash)
32
36
  handle_encoding_errors do
33
- ::YAML.dump(hash)
37
+ result = ::YAML.dump(hash)
38
+ result.gsub!(": \n", ": null\n") # set canonical null value in order to avoid trailing whitespaces
39
+ result
34
40
  end
35
41
  end
36
42
 
@@ -40,7 +46,13 @@ module VCR
40
46
  # @return [Hash] the deserialized object
41
47
  def deserialize(string)
42
48
  handle_encoding_errors do
43
- ::YAML.load(string)
49
+ handle_syntax_errors do
50
+ if ::YAML.respond_to?(:unsafe_load)
51
+ ::YAML.unsafe_load(string)
52
+ else
53
+ ::YAML.load(string)
54
+ end
55
+ end
44
56
  end
45
57
  end
46
58
  end
@@ -54,6 +54,16 @@ module VCR
54
54
  raise
55
55
  end
56
56
  end
57
+
58
+ # @private
59
+ module SyntaxErrorHandling
60
+ def handle_syntax_errors
61
+ yield
62
+ rescue *self::SYNTAX_ERRORS => e
63
+ e.message << "\nNote: This is a VCR cassette. If it is using ERB, you may have forgotten to pass the `:erb` option to `use_cassette`."
64
+ raise
65
+ end
66
+ end
57
67
  end
58
68
  end
59
69
 
data/lib/vcr/cassette.rb CHANGED
@@ -24,6 +24,13 @@ module VCR
24
24
  # plays them back, or does both.
25
25
  attr_reader :record_mode
26
26
 
27
+ # @return [Boolean] The cassette's record_on_error mode. When the code that uses the cassette
28
+ # raises an error (for example a test failure) and record_on_error is set to false, no
29
+ # cassette will be recorded. This is useful when you are TDD'ing an API integration: when
30
+ # an error is raised that often means your request is invalid, so you don't want the cassette
31
+ # to be recorded.
32
+ attr_reader :record_on_error
33
+
27
34
  # @return [Array<Symbol, #call>] List of request matchers. Used to find a response from an
28
35
  # existing HTTP interaction to play back.
29
36
  attr_reader :match_requests_on
@@ -36,6 +43,12 @@ module VCR
36
43
  # @return [Integer, nil] How frequently (in seconds) the cassette should be re-recorded.
37
44
  attr_reader :re_record_interval
38
45
 
46
+ # @return [Boolean, nil] Should outdated interactions be recorded back to file
47
+ attr_reader :clean_outdated_http_interactions
48
+
49
+ # @return [Boolean] Should unused requests be dropped from the cassette?
50
+ attr_reader :drop_unused_requests
51
+
39
52
  # @return [Array<Symbol>] If set, {VCR::Configuration#before_record} and
40
53
  # {VCR::Configuration#before_playback} hooks with a corresponding tag will apply.
41
54
  attr_reader :tags
@@ -45,6 +58,7 @@ module VCR
45
58
  def initialize(name, options = {})
46
59
  @name = name
47
60
  @options = VCR.configuration.default_cassette_options.merge(options)
61
+ @mutex = Mutex.new
48
62
 
49
63
  assert_valid_options!
50
64
  extract_options
@@ -62,21 +76,40 @@ module VCR
62
76
  # @param (see VCR#eject_casssette)
63
77
  # @see VCR#eject_cassette
64
78
  def eject(options = {})
65
- write_recorded_interactions_to_disk
79
+ write_recorded_interactions_to_disk if should_write_recorded_interactions_to_disk?
66
80
 
67
81
  if should_assert_no_unused_interactions? && !options[:skip_no_unused_interactions_assertion]
68
82
  http_interactions.assert_no_unused_interactions!
69
83
  end
70
84
  end
71
85
 
86
+ # @private
87
+ def run_failed!
88
+ @run_failed = true
89
+ end
90
+
91
+ # @private
92
+ def run_failed?
93
+ @run_failed = false unless defined?(@run_failed)
94
+ @run_failed
95
+ end
96
+
97
+ def should_write_recorded_interactions_to_disk?
98
+ !run_failed? || record_on_error
99
+ end
100
+
72
101
  # @private
73
102
  def http_interactions
74
- @http_interactions ||= HTTPInteractionList.new \
75
- should_stub_requests? ? previously_recorded_interactions : [],
76
- match_requests_on,
77
- @allow_playback_repeats,
78
- @parent_list,
79
- log_prefix
103
+ # Without this mutex, under threaded access, an HTTPInteractionList will overwrite
104
+ # the first.
105
+ @mutex.synchronize do
106
+ @http_interactions ||= HTTPInteractionList.new \
107
+ should_stub_requests? ? previously_recorded_interactions : [],
108
+ match_requests_on,
109
+ @allow_playback_repeats,
110
+ @parent_list,
111
+ log_prefix
112
+ end
80
113
  end
81
114
 
82
115
  # @private
@@ -143,10 +176,11 @@ module VCR
143
176
 
144
177
  def assert_valid_options!
145
178
  invalid_options = @options.keys - [
146
- :record, :erb, :match_requests_on, :re_record_interval, :tag, :tags,
179
+ :record, :record_on_error, :erb, :match_requests_on, :re_record_interval, :tag, :tags,
147
180
  :update_content_length_header, :allow_playback_repeats, :allow_unused_http_interactions,
148
181
  :exclusive, :serialize_with, :preserve_exact_body_bytes, :decode_compressed_response,
149
- :persist_with
182
+ :recompress_response, :persist_with, :persister_options, :clean_outdated_http_interactions,
183
+ :drop_unused_requests
150
184
  ]
151
185
 
152
186
  if invalid_options.size > 0
@@ -155,24 +189,23 @@ module VCR
155
189
  end
156
190
 
157
191
  def extract_options
158
- [:erb, :match_requests_on, :re_record_interval,
159
- :allow_playback_repeats, :allow_unused_http_interactions, :exclusive].each do |name|
192
+ [:record_on_error, :erb, :match_requests_on, :re_record_interval, :clean_outdated_http_interactions,
193
+ :allow_playback_repeats, :allow_unused_http_interactions, :exclusive, :drop_unused_requests].each do |name|
160
194
  instance_variable_set("@#{name}", @options[name])
161
195
  end
162
196
 
163
197
  assign_tags
164
198
 
165
- @record_mode = @options[:record]
166
199
  @serializer = VCR.cassette_serializers[@options[:serialize_with]]
167
200
  @persister = VCR.cassette_persisters[@options[:persist_with]]
168
- @record_mode = :all if should_re_record?
201
+ @record_mode = should_re_record?(@options[:record]) ? :all : @options[:record]
169
202
  @parent_list = @exclusive ? HTTPInteractionList::NullList : VCR.http_interactions
170
203
  end
171
204
 
172
205
  def assign_tags
173
206
  @tags = Array(@options.fetch(:tags) { @options[:tag] })
174
207
 
175
- [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response].each do |tag|
208
+ [:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response, :recompress_response].each do |tag|
176
209
  @tags << tag if @options[tag]
177
210
  end
178
211
  end
@@ -201,9 +234,10 @@ module VCR
201
234
  end
202
235
  end
203
236
 
204
- def should_re_record?
237
+ def should_re_record?(record_mode)
205
238
  return false unless @re_record_interval
206
239
  return false unless originally_recorded_at
240
+ return false if record_mode == :none
207
241
 
208
242
  now = Time.now
209
243
 
@@ -229,6 +263,10 @@ module VCR
229
263
  record_mode == :all
230
264
  end
231
265
 
266
+ def should_remove_unused_interactions?
267
+ @drop_unused_requests
268
+ end
269
+
232
270
  def should_assert_no_unused_interactions?
233
271
  !(@allow_unused_http_interactions || $!)
234
272
  end
@@ -247,7 +285,16 @@ module VCR
247
285
  end
248
286
  end
249
287
 
250
- old_interactions + new_recorded_interactions
288
+ if should_remove_unused_interactions?
289
+ new_recorded_interactions
290
+ else
291
+ up_to_date_interactions(old_interactions) + new_recorded_interactions
292
+ end
293
+ end
294
+
295
+ def up_to_date_interactions(interactions)
296
+ return interactions unless clean_outdated_http_interactions && re_record_interval
297
+ interactions.take_while { |x| x[:recorded_at] > Time.now - re_record_interval }
251
298
  end
252
299
 
253
300
  def interactions_to_record
@@ -54,16 +54,14 @@ module VCR
54
54
  #
55
55
  # @example
56
56
  # VCR.configure do |c|
57
- # c.hook_into :fakeweb, :typhoeus
57
+ # c.hook_into :webmock, :typhoeus
58
58
  # end
59
59
  #
60
60
  # @param hooks [Array<Symbol>] List of libraries. Valid values are
61
- # `:fakeweb`, `:webmock`, `:typhoeus`, `:excon` and `:faraday`.
61
+ # `:webmock`, `:typhoeus`, `:excon` and `:faraday`.
62
62
  # @raise [ArgumentError] when given an unsupported library name.
63
63
  # @raise [VCR::Errors::LibraryVersionTooLowError] when the version
64
64
  # of a library you are using is too low for VCR to support.
65
- # @note `:fakeweb` and `:webmock` cannot both be used since they both monkey patch
66
- # `Net::HTTP`. Otherwise, you can use any combination of these.
67
65
  def hook_into(*hooks)
68
66
  hooks.each { |a| load_library_hook(a) }
69
67
  invoke_hook(:after_library_hooks_loaded)
@@ -79,6 +77,15 @@ module VCR
79
77
  end
80
78
  alias ignore_host ignore_hosts
81
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
+
82
89
  # Sets whether or not VCR should ignore localhost requests.
83
90
  #
84
91
  # @param value [Boolean] the value to set
@@ -224,7 +231,7 @@ module VCR
224
231
 
225
232
  before_playback(tag) do |interaction|
226
233
  orig_text = call_block(block, interaction)
227
- log "before_playback: replacing #{placeholder.inspect} with #{orig_text.inspect}"
234
+ log "before_playback: replacing #{orig_text.inspect} with #{placeholder.inspect}"
228
235
  interaction.filter!(placeholder, orig_text)
229
236
  end
230
237
  end
@@ -485,10 +492,13 @@ module VCR
485
492
  @rspec_metadata_configured = false
486
493
  @default_cassette_options = {
487
494
  :record => :once,
495
+ :record_on_error => true,
488
496
  :match_requests_on => RequestMatcherRegistry::DEFAULT_MATCHERS,
489
497
  :allow_unused_http_interactions => true,
498
+ :drop_unused_requests => false,
490
499
  :serialize_with => :yaml,
491
- :persist_with => :file_system
500
+ :persist_with => :file_system,
501
+ :persister_options => {}
492
502
  }
493
503
 
494
504
  self.uri_parser = URI
@@ -502,7 +512,7 @@ module VCR
502
512
  file = "vcr/library_hooks/#{hook}"
503
513
  require file
504
514
  rescue LoadError => e
505
- raise e unless e.message.include?(file) # in case FakeWeb/WebMock/etc itself is not available
515
+ raise e unless e.message.include?(file) # in case WebMock itself is not available
506
516
  raise ArgumentError.new("#{hook.inspect} is not a supported VCR HTTP library hook.")
507
517
  end
508
518
 
@@ -552,6 +562,10 @@ module VCR
552
562
  end
553
563
 
554
564
  def register_built_in_hooks
565
+ before_playback(:recompress_response) do |interaction|
566
+ interaction.response.recompress if interaction.response.vcr_decompressed?
567
+ end
568
+
555
569
  before_playback(:update_content_length_header) do |interaction|
556
570
  interaction.response.update_content_length_header
557
571
  end
@@ -573,4 +587,3 @@ module VCR
573
587
  define_hook :after_library_hooks_loaded
574
588
  end
575
589
  end
576
-
@@ -43,67 +43,5 @@ module VCR
43
43
  end
44
44
  end
45
45
  end
46
-
47
- module RSpec
48
- # Contains macro methods to assist with VCR usage. These methods are
49
- # intended to be used directly in an RSpec example group. To make these
50
- # available in your RSpec example groups, extend the module in an individual
51
- # example group, or configure RSpec to extend the module in all example groups.
52
- #
53
- # @example
54
- # RSpec.configure do |c|
55
- # c.extend VCR::RSpec::Macros
56
- # end
57
- #
58
- module Macros
59
- def self.extended(base)
60
- Kernel.warn "WARNING: VCR::RSpec::Macros is deprecated. Use RSpec metadata options instead `:vcr => vcr_options`"
61
- end
62
-
63
- # Sets up a `before` and `after` hook that will insert and eject a
64
- # cassette, respectively.
65
- #
66
- # @example
67
- # describe "Some API Client" do
68
- # use_vcr_cassette "some_api", :record => :new_episodes
69
- # end
70
- #
71
- # @param [(optional) String] name the cassette name; it will be inferred by the example
72
- # group descriptions if not given.
73
- # @param [(optional) Hash] options the cassette options
74
- # @deprecated Use RSpec metadata options
75
- def use_vcr_cassette(*args)
76
- options = args.last.is_a?(Hash) ? args.pop : {}
77
- name = args.first || infer_cassette_name
78
-
79
- before(:each) do
80
- VCR.insert_cassette(name, options)
81
- end
82
-
83
- after(:each) do
84
- VCR.eject_cassette
85
- end
86
- end
87
-
88
- private
89
-
90
- def infer_cassette_name
91
- # RSpec 1 exposes #description_parts; use that if its available
92
- return description_parts.join("/") if respond_to?(:description_parts)
93
-
94
- # Otherwise use RSpec 2/3 metadata...
95
- group_descriptions = []
96
- klass = self
97
-
98
- while klass.respond_to?(:metadata) && klass.metadata
99
- group_descriptions << klass.metadata[:description] ||
100
- klass.metadata[:example_group][:description]
101
- klass = klass.superclass
102
- end
103
-
104
- group_descriptions.compact.reverse.join('/')
105
- end
106
- end
107
- end
108
46
  end
109
47
 
data/lib/vcr/errors.rb CHANGED
@@ -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
@@ -107,6 +96,10 @@ module VCR
107
96
 
108
97
  lines << " #{request.method.to_s.upcase} #{request.uri}"
109
98
 
99
+ if match_request_on_headers?
100
+ lines << " Headers:\n#{formatted_headers}"
101
+ end
102
+
110
103
  if match_request_on_body?
111
104
  lines << " Body: #{request.body}"
112
105
  end
@@ -114,6 +107,10 @@ module VCR
114
107
  lines.join("\n")
115
108
  end
116
109
 
110
+ def match_request_on_headers?
111
+ current_matchers.include?(:headers)
112
+ end
113
+
117
114
  def match_request_on_body?
118
115
  current_matchers.include?(:body)
119
116
  end
@@ -128,6 +125,14 @@ module VCR
128
125
  end
129
126
  end
130
127
 
128
+ def formatted_headers
129
+ request.headers.flat_map do |header, values|
130
+ values.map do |val|
131
+ " #{header}: #{val.inspect}"
132
+ end
133
+ end.join("\n")
134
+ end
135
+
131
136
  def cassettes_description
132
137
  if current_cassettes.size > 0
133
138
  [cassettes_list << "\n",