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
@@ -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
 
@@ -2,79 +2,72 @@ require 'vcr/util/version_checker'
2
2
  require 'vcr/request_handler'
3
3
  require 'typhoeus'
4
4
 
5
- if Float(Typhoeus::VERSION[/^\d+\.\d+/]) < 0.5
6
- require 'vcr/library_hooks/typhoeus_0.4'
7
- else
8
- VCR::VersionChecker.new('Typhoeus', Typhoeus::VERSION, '0.5.0').check_version!
9
-
10
- module VCR
11
- class LibraryHooks
5
+ module VCR
6
+ class LibraryHooks
7
+ # @private
8
+ module Typhoeus
12
9
  # @private
13
- module Typhoeus
14
- # @private
15
- class RequestHandler < ::VCR::RequestHandler
16
- attr_reader :request
17
- def initialize(request)
18
- @request = request
19
- request.block_connection = false if VCR.turned_on?
20
- end
21
-
22
- def vcr_request
23
- @vcr_request ||= VCR::Request.new \
24
- request.options.fetch(:method, :get),
25
- request.url,
26
- request_body,
27
- request.options.fetch(:headers, {})
28
- end
10
+ class RequestHandler < ::VCR::RequestHandler
11
+ attr_reader :request
12
+ def initialize(request)
13
+ @request = request
14
+ request.block_connection = false if VCR.turned_on?
15
+ end
29
16
 
30
- private
17
+ def vcr_request
18
+ @vcr_request ||= VCR::Request.new \
19
+ request.options.fetch(:method, :get),
20
+ request.url,
21
+ request.encoded_body,
22
+ request.options.fetch(:headers, {})
23
+ end
31
24
 
32
- def externally_stubbed?
33
- ::Typhoeus::Expectation.find_by(request)
34
- end
25
+ private
35
26
 
36
- def set_typed_request_for_after_hook(*args)
37
- super
38
- request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
39
- end
27
+ def externally_stubbed?
28
+ ::Typhoeus::Expectation.find_by(request)
29
+ end
40
30
 
41
- def on_unhandled_request
42
- invoke_after_request_hook(nil)
43
- super
44
- end
31
+ def set_typed_request_for_after_hook(*args)
32
+ super
33
+ request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
34
+ end
45
35
 
46
- def on_stubbed_by_vcr_request
47
- ::Typhoeus::Response.new \
48
- :http_version => stubbed_response.http_version,
49
- :code => stubbed_response.status.code,
50
- :status_message => stubbed_response.status.message,
51
- :headers => stubbed_response_headers,
52
- :body => stubbed_response.body,
53
- :effective_url => stubbed_response.adapter_metadata.fetch('effective_url', request.url),
54
- :mock => true
55
- end
36
+ def on_unhandled_request
37
+ invoke_after_request_hook(nil)
38
+ super
39
+ end
56
40
 
57
- def stubbed_response_headers
58
- @stubbed_response_headers ||= {}.tap do |hash|
59
- stubbed_response.headers.each do |key, values|
60
- hash[key] = values.size == 1 ? values.first : values
61
- end if stubbed_response.headers
62
- end
63
- end
41
+ def on_stubbed_by_vcr_request
42
+ response = ::Typhoeus::Response.new \
43
+ :http_version => stubbed_response.http_version,
44
+ :code => stubbed_response.status.code,
45
+ :status_message => stubbed_response.status.message,
46
+ :headers => stubbed_response_headers,
47
+ :body => stubbed_response.body,
48
+ :effective_url => stubbed_response.adapter_metadata.fetch('effective_url', request.url),
49
+ :mock => true
50
+
51
+ first_header_line = "HTTP/#{stubbed_response.http_version} #{response.code} #{response.status_message}\r\n"
52
+ response.instance_variable_set(:@first_header_line, first_header_line)
53
+ response.instance_variable_get(:@options)[:response_headers] =
54
+ first_header_line + response.headers.map { |k,v| "#{k}: #{v}"}.join("\r\n")
55
+
56
+ response
57
+ end
64
58
 
65
- if ::Typhoeus::Request.method_defined?(:encoded_body)
66
- def request_body
67
- request.encoded_body
68
- end
69
- else
70
- def request_body
71
- request.options.fetch(:body, "")
72
- end
59
+ def stubbed_response_headers
60
+ @stubbed_response_headers ||= {}.tap do |hash|
61
+ stubbed_response.headers.each do |key, values|
62
+ hash[key] = values.size == 1 ? values.first : values
63
+ end if stubbed_response.headers
73
64
  end
74
65
  end
66
+ end
75
67
 
76
- # @private
77
- def self.vcr_response_from(response)
68
+ # @private
69
+ class << self
70
+ def vcr_response_from(response)
78
71
  VCR::Response.new \
79
72
  VCR::ResponseStatus.new(response.code, response.status_message),
80
73
  response.headers,
@@ -83,27 +76,47 @@ else
83
76
  { "effective_url" => response.effective_url }
84
77
  end
85
78
 
86
- ::Typhoeus.on_complete do |response|
87
- request = response.request
88
- unless VCR.library_hooks.disabled?(:typhoeus)
89
- vcr_response = vcr_response_from(response)
90
- typed_vcr_request = request.send(:remove_instance_variable, :@__typed_vcr_request)
91
-
92
- unless request.response.mock
93
- http_interaction = VCR::HTTPInteraction.new(typed_vcr_request, vcr_response)
94
- VCR.record_http_interaction(http_interaction)
79
+ def collect_chunks(request)
80
+ chunks = ''
81
+ request.on_body.unshift(
82
+ Proc.new do |body, response|
83
+ chunks += body
84
+ request.instance_variable_set(:@chunked_body, chunks)
95
85
  end
86
+ )
87
+ end
96
88
 
97
- VCR.configuration.invoke_hook(:after_http_request, typed_vcr_request, vcr_response)
98
- end
89
+ def restore_body_from_chunks(response, request)
90
+ response.options[:response_body] = request.instance_variable_get(:@chunked_body)
99
91
  end
92
+ end
100
93
 
101
- ::Typhoeus.before do |request|
102
- if response = VCR::LibraryHooks::Typhoeus::RequestHandler.new(request).handle
103
- request.finish(response)
104
- else
105
- true
94
+ ::Typhoeus.on_complete do |response|
95
+ request = response.request
96
+
97
+ restore_body_from_chunks(response, request) if request.streaming?
98
+
99
+ unless VCR.library_hooks.disabled?(:typhoeus)
100
+ vcr_response = vcr_response_from(response)
101
+ typed_vcr_request = request.send(:remove_instance_variable, :@__typed_vcr_request)
102
+
103
+ unless request.response.mock
104
+ http_interaction = VCR::HTTPInteraction.new(typed_vcr_request, vcr_response)
105
+ VCR.record_http_interaction(http_interaction)
106
106
  end
107
+
108
+ VCR.configuration.invoke_hook(:after_http_request, typed_vcr_request, vcr_response)
109
+ end
110
+ end
111
+
112
+ ::Typhoeus.before do |request|
113
+ collect_chunks(request) if request.streaming?
114
+ if response = VCR::LibraryHooks::Typhoeus::RequestHandler.new(request).handle
115
+ request.on_headers.each { |cb| cb.call(response) }
116
+ request.on_body.each { |cb| cb.call(response.body, response) }
117
+ request.finish(response)
118
+ else
119
+ true
107
120
  end
108
121
  end
109
122
  end
@@ -117,4 +130,3 @@ VCR.configuration.after_library_hooks_loaded do
117
130
  WebMock::HttpLibAdapters::TyphoeusAdapter.disable!
118
131
  end
119
132
  end
120
-
@@ -12,8 +12,6 @@ module VCR
12
12
  module WebMock
13
13
  extend self
14
14
 
15
- @global_hook_disabled_requests = {}
16
-
17
15
  def with_global_hook_disabled(request)
18
16
  global_hook_disabled_requests << request
19
17
 
@@ -25,19 +23,12 @@ module VCR
25
23
  end
26
24
 
27
25
  def global_hook_disabled?(request)
28
- requests = @global_hook_disabled_requests[Thread.current.object_id]
26
+ requests = Thread.current[:_vcr_webmock_disabled_requests]
29
27
  requests && requests.include?(request)
30
28
  end
31
29
 
32
30
  def global_hook_disabled_requests
33
- requests = @global_hook_disabled_requests[Thread.current.object_id]
34
- return requests if requests
35
-
36
- ObjectSpace.define_finalizer(Thread.current, lambda {
37
- @global_hook_disabled_requests.delete(Thread.current.object_id)
38
- })
39
-
40
- @global_hook_disabled_requests[Thread.current.object_id] = []
31
+ Thread.current[:_vcr_webmock_disabled_requests] ||= []
41
32
  end
42
33
 
43
34
  # @private
@@ -9,8 +9,8 @@ module VCR
9
9
  include Enumerable
10
10
 
11
11
  # Creates a new list of context-owned cassettes and linked cassettes
12
- # @param [Array] context-owned cassettes
13
- # @param [Array] context-unowned (linked) cassettes
12
+ # @param cassettes [Array] context-owned cassettes
13
+ # @param linked_cassettes [Array] context-unowned (linked) cassettes
14
14
  def initialize(cassettes, linked_cassettes)
15
15
  @cassettes = cassettes
16
16
  @linked_cassettes = linked_cassettes
@@ -52,8 +52,8 @@ module VCR
52
52
  end
53
53
 
54
54
  # Create a new CassetteList
55
- # @param [Array] context-owned cassettes
56
- # @param [Array] context-unowned (linked) cassettes
55
+ # @param cassettes [Array] context-owned cassettes
56
+ # @param linked_cassettes [Array] context-unowned (linked) cassettes
57
57
  def self.list(cassettes, linked_cassettes)
58
58
  CassetteList.new(cassettes, linked_cassettes)
59
59
  end
@@ -74,7 +74,7 @@ module VCR
74
74
  def after_request(response)
75
75
  vcr_response = vcr_response_for(response)
76
76
 
77
- if should_record?
77
+ if vcr_response && should_record?
78
78
  VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, vcr_response))
79
79
  end
80
80
 
@@ -31,6 +31,11 @@ module VCR
31
31
  RequestHandler.new(@app, env).handle
32
32
  end
33
33
 
34
+ # Close any persistent connections.
35
+ def close
36
+ @app.close if @app.respond_to?(:close)
37
+ end
38
+
34
39
  # @private
35
40
  class RequestHandler < ::VCR::RequestHandler
36
41
  attr_reader :app, :env
@@ -40,9 +45,11 @@ module VCR
40
45
  end
41
46
 
42
47
  def handle
43
- # Faraday must be exlusive here in case another library hook is being used.
48
+ # Faraday must be exclusive here in case another library hook is being used.
44
49
  # We don't want double recording/double playback.
45
50
  VCR.library_hooks.exclusive_hook = :faraday
51
+ collect_chunks if env.request.stream_response?
52
+
46
53
  super
47
54
  ensure
48
55
  response = defined?(@vcr_response) ? @vcr_response : nil
@@ -72,8 +79,12 @@ module VCR
72
79
  end
73
80
 
74
81
  def response_for(response)
82
+ # reason_phrase is a new addition to Faraday::Response,
83
+ # so maintain backward compatibility
84
+ reason = response.respond_to?(:reason_phrase) ? response.reason_phrase : nil
85
+
75
86
  VCR::Response.new(
76
- VCR::ResponseStatus.new(response.status, nil),
87
+ VCR::ResponseStatus.new(response.status, reason),
77
88
  response.headers,
78
89
  raw_body_from(response.body),
79
90
  nil
@@ -94,6 +105,7 @@ module VCR
94
105
  @vcr_response = stubbed_response
95
106
 
96
107
  faraday_response = ::Faraday::Response.new
108
+ env.request.on_data.call(stubbed_response.body, stubbed_response.body.length) if env.request.stream_response?
97
109
  faraday_response.finish(env)
98
110
  env[:response] = faraday_response
99
111
  end
@@ -102,6 +114,7 @@ module VCR
102
114
  @has_on_complete_hook = true
103
115
  response = app.call(env)
104
116
  response.on_complete do
117
+ restore_body_from_chunks(env.request) if env.request.stream_response?
105
118
  @vcr_response = response_for(response)
106
119
  VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, @vcr_response))
107
120
  invoke_after_request_hook(@vcr_response) if delay_finishing?
@@ -112,6 +125,20 @@ module VCR
112
125
  super
113
126
  VCR.library_hooks.exclusive_hook = nil
114
127
  end
128
+
129
+ def collect_chunks
130
+ caller_on_data = env.request.on_data
131
+ chunks = ''
132
+ env.request.on_data = Proc.new do |chunk, overall_received_bytes|
133
+ chunks += chunk
134
+ env.request.instance_variable_set(:@chunked_body, chunks)
135
+ caller_on_data.call(chunk, overall_received_bytes)
136
+ end
137
+ end
138
+
139
+ def restore_body_from_chunks(request)
140
+ env[:body] = request.instance_variable_get(:@chunked_body)
141
+ end
115
142
  end
116
143
  end
117
144
  end
@@ -25,10 +25,18 @@ module VCR
25
25
  end
26
26
  end
27
27
 
28
+ def localhost_ignored?
29
+ (LOCALHOST_ALIASES & ignore_hosts.to_a).any?
30
+ end
31
+
28
32
  def ignore_hosts(*hosts)
29
33
  ignored_hosts.merge(hosts)
30
34
  end
31
35
 
36
+ def unignore_hosts(*hosts)
37
+ ignored_hosts.subtract(hosts)
38
+ end
39
+
32
40
  def ignore?(request)
33
41
  invoke_hook(:ignore_request, request).any?
34
42
  end
@@ -40,4 +48,3 @@ module VCR
40
48
  end
41
49
  end
42
50
  end
43
-
@@ -110,12 +110,12 @@ module VCR
110
110
 
111
111
  def register_built_ins
112
112
  register(:method) { |r1, r2| r1.method == r2.method }
113
- register(:uri) { |r1, r2| r1.uri == r2.uri }
113
+ register(:uri) { |r1, r2| r1.parsed_uri == r2.parsed_uri }
114
114
  register(:body) { |r1, r2| r1.body == r2.body }
115
115
  register(:headers) { |r1, r2| r1.headers == r2.headers }
116
116
 
117
117
  register(:host) do |r1, r2|
118
- r1.parsed_uri.host == r2.parsed_uri.host
118
+ r1.parsed_uri.host.chomp('.') == r2.parsed_uri.host.chomp('.')
119
119
  end
120
120
  register(:path) do |r1, r2|
121
121
  r1.parsed_uri.path == r2.parsed_uri.path
@@ -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
data/lib/vcr/structs.rb CHANGED
@@ -62,7 +62,7 @@ module VCR
62
62
  super
63
63
 
64
64
  if body && !body.is_a?(String)
65
- raise ArgumentError, "#{self.class} initialized with an invalid body: #{body.inspect}."
65
+ raise ArgumentError, "#{self.class} initialized with an invalid (non-String) body of class #{body.class}: #{body.inspect}."
66
66
  end
67
67
 
68
68
  # Ensure that the body is a raw string, in case the string instance
@@ -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.
@@ -274,7 +255,7 @@ module VCR
274
255
  end
275
256
 
276
257
  # @return [Boolean] whether or not this request is being stubbed by an
277
- # external library (such as WebMock or FakeWeb).
258
+ # external library (such as WebMock).
278
259
  # @see #stubbed_by_vcr?
279
260
  # @see #stubbed?
280
261
  def externally_stubbed?
@@ -365,11 +346,10 @@ module VCR
365
346
  {
366
347
  'status' => status.to_hash,
367
348
  'headers' => headers,
368
- 'body' => serializable_body,
369
- 'http_version' => http_version
349
+ 'body' => serializable_body
370
350
  }.tap do |hash|
351
+ hash['http_version'] = http_version if http_version
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'
@@ -437,9 +454,10 @@ module VCR
437
454
 
438
455
  case type
439
456
  when 'gzip'
440
- args = [StringIO.new(body)]
441
- args << { :encoding => 'ASCII-8BIT' } if ''.respond_to?(:encoding)
442
- yield Zlib::GzipReader.new(*args).read
457
+ gzip_reader_options = {}
458
+ gzip_reader_options[:encoding] = 'ASCII-8BIT' if ''.respond_to?(:encoding)
459
+ yield Zlib::GzipReader.new(StringIO.new(body),
460
+ **gzip_reader_options).read
443
461
  when 'deflate'
444
462
  yield Zlib::Inflate.inflate(body)
445
463
  when 'identity', NilClass
@@ -463,7 +481,7 @@ module VCR
463
481
  def to_hash
464
482
  {
465
483
  'code' => code, 'message' => message
466
- }.tap { |h| OrderedHashSerializer.apply_to(h, members) }
484
+ }
467
485
  end
468
486
 
469
487
  # Constructs a new instance from a hash.
@@ -496,9 +514,7 @@ module VCR
496
514
  'request' => request.to_hash,
497
515
  'response' => response.to_hash,
498
516
  'recorded_at' => recorded_at.httpdate
499
- }.tap do |hash|
500
- OrderedHashSerializer.apply_to(hash, members)
501
- end
517
+ }
502
518
  end
503
519
 
504
520
  # Constructs a new instance from a hash.
@@ -23,10 +23,10 @@ module VCR
23
23
  # Adds `Before` and `After` cucumber hooks for the named tags that
24
24
  # will cause a VCR cassette to be used for scenarios with matching tags.
25
25
  #
26
- # @param [Array<String>] tag_names the cucumber scenario tags
27
- # @param [(optional) Hash] options the cassette options. Specify
28
- # `:use_scenario_name => true` to automatically name the
29
- # cassette according to the scenario name.
26
+ # @param tag_names [Array<String,Hash>] the cucumber scenario tags. If
27
+ # the last argument is a hash it is treated as cassette options.
28
+ # - `:use_scenario_name => true` to automatically name the
29
+ # cassette according to the scenario name.
30
30
  def tags(*tag_names)
31
31
  original_options = tag_names.last.is_a?(::Hash) ? tag_names.pop : {}
32
32
  tag_names.each do |tag_name|
@@ -47,10 +47,21 @@ module VCR
47
47
  scenario.scenario_outline.name,
48
48
  scenario.name.split("\n").first
49
49
  ].join("/")
50
- else
50
+ elsif scenario.respond_to?(:feature)
51
51
  [ scenario.feature.name.split("\n").first,
52
52
  scenario.name.split("\n").first
53
53
  ].join("/")
54
+ elsif scenario.location.lines.min == scenario.location.lines.max
55
+ # test case from a regular scenario in cucumber version 4
56
+ [ scenario.location.file.split("/").last.split(".").first,
57
+ scenario.name.split("\n").first
58
+ ].join("/")
59
+ else
60
+ # test case from a scenario with examples ("scenario outline") in cucumber version 4
61
+ [ scenario.location.file.split("/").last.split(".").first,
62
+ scenario.name.split("\n").first,
63
+ "Example at line #{scenario.location.lines.max}"
64
+ ].join("/")
54
65
  end
55
66
  else
56
67
  "cucumber_tags/#{tag_name.gsub(/\A@/, '')}"
@@ -5,38 +5,50 @@ module VCR
5
5
  module Metadata
6
6
  extend self
7
7
 
8
+ def vcr_cassette_name_for(metadata)
9
+ description =
10
+ if metadata[:description].empty?
11
+ # we have an "it { is_expected.to be something }" block
12
+ metadata[:scoped_id]
13
+ else
14
+ metadata[:description]
15
+ end
16
+ example_group =
17
+ if metadata.key?(:example_group)
18
+ metadata[:example_group]
19
+ else
20
+ metadata[:parent_example_group]
21
+ end
22
+
23
+ if example_group
24
+ [vcr_cassette_name_for(example_group), description].join('/')
25
+ else
26
+ description
27
+ end
28
+ end
29
+
8
30
  def configure!
9
31
  ::RSpec.configure do |config|
10
- vcr_cassette_name_for = lambda do |metadata|
11
- description = if metadata[:description].empty?
12
- # we have an "it { is_expected.to be something }" block
13
- metadata[:scoped_id]
14
- else
15
- metadata[:description]
16
- end
17
- example_group = if metadata.key?(:example_group)
18
- metadata[:example_group]
19
- else
20
- metadata[:parent_example_group]
21
- end
22
-
23
- if example_group
24
- [vcr_cassette_name_for[example_group], description].join('/')
25
- else
26
- description
27
- end
28
- end
29
32
 
30
33
  when_tagged_with_vcr = { :vcr => lambda { |v| !!v } }
31
34
 
32
35
  config.before(:each, when_tagged_with_vcr) do |ex|
33
36
  example = ex.respond_to?(:metadata) ? ex : ex.example
34
37
 
38
+ cassette_name = nil
35
39
  options = example.metadata[:vcr]
36
- options = options.is_a?(Hash) ? options.dup : {} # in case it's just :vcr => true
40
+ options = case options
41
+ when Hash #=> vcr: { cassette_name: 'foo' }
42
+ options.dup
43
+ when String #=> vcr: 'bar'
44
+ cassette_name = options.dup
45
+ {}
46
+ else #=> :vcr or vcr: true
47
+ {}
48
+ end
37
49
 
38
- cassette_name = options.delete(:cassette_name) ||
39
- vcr_cassette_name_for[example.metadata]
50
+ cassette_name ||= options.delete(:cassette_name) ||
51
+ VCR::RSpec::Metadata.vcr_cassette_name_for(example.metadata)
40
52
  VCR.insert_cassette(cassette_name, options)
41
53
  end
42
54
 
@@ -5,6 +5,7 @@ module VCR
5
5
  module Hooks
6
6
  include VariableArgsBlockCaller
7
7
 
8
+ # @private
8
9
  FilteredHook = Struct.new(:hook, :filters) do
9
10
  include VariableArgsBlockCaller
10
11