vcr 2.0.0.beta1 → 2.0.0.beta2

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 (88) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +37 -2
  4. data/Gemfile +2 -2
  5. data/README.md +10 -1
  6. data/Rakefile +43 -7
  7. data/Upgrade.md +45 -0
  8. data/features/.nav +1 -0
  9. data/features/cassettes/automatic_re_recording.feature +19 -17
  10. data/features/cassettes/dynamic_erb.feature +32 -28
  11. data/features/cassettes/exclusive.feature +28 -24
  12. data/features/cassettes/format.feature +213 -31
  13. data/features/cassettes/update_content_length_header.feature +20 -18
  14. data/features/configuration/filter_sensitive_data.feature +4 -4
  15. data/features/configuration/hooks.feature +27 -23
  16. data/features/http_libraries/em_http_request.feature +79 -75
  17. data/features/record_modes/all.feature +14 -14
  18. data/features/record_modes/new_episodes.feature +15 -15
  19. data/features/record_modes/none.feature +15 -15
  20. data/features/record_modes/once.feature +15 -15
  21. data/features/request_matching/body.feature +25 -23
  22. data/features/request_matching/custom_matcher.feature +25 -23
  23. data/features/request_matching/headers.feature +32 -36
  24. data/features/request_matching/host.feature +27 -25
  25. data/features/request_matching/identical_request_sequence.feature +27 -25
  26. data/features/request_matching/method.feature +27 -25
  27. data/features/request_matching/path.feature +27 -25
  28. data/features/request_matching/playback_repeats.feature +27 -25
  29. data/features/request_matching/uri.feature +27 -25
  30. data/features/request_matching/uri_without_param.feature +28 -26
  31. data/features/step_definitions/cli_steps.rb +71 -17
  32. data/features/support/env.rb +3 -1
  33. data/features/support/http_lib_filters.rb +6 -3
  34. data/features/support/vcr_cucumber_helpers.rb +4 -2
  35. data/lib/vcr.rb +6 -2
  36. data/lib/vcr/cassette.rb +75 -51
  37. data/lib/vcr/cassette/migrator.rb +111 -0
  38. data/lib/vcr/cassette/serializers.rb +35 -0
  39. data/lib/vcr/cassette/serializers/json.rb +23 -0
  40. data/lib/vcr/cassette/serializers/psych.rb +24 -0
  41. data/lib/vcr/cassette/serializers/syck.rb +35 -0
  42. data/lib/vcr/cassette/serializers/yaml.rb +24 -0
  43. data/lib/vcr/configuration.rb +6 -1
  44. data/lib/vcr/errors.rb +1 -1
  45. data/lib/vcr/library_hooks/excon.rb +1 -7
  46. data/lib/vcr/library_hooks/typhoeus.rb +6 -22
  47. data/lib/vcr/library_hooks/webmock.rb +1 -1
  48. data/lib/vcr/middleware/faraday.rb +1 -1
  49. data/lib/vcr/request_matcher_registry.rb +43 -30
  50. data/lib/vcr/structs.rb +209 -0
  51. data/lib/vcr/tasks/vcr.rake +9 -0
  52. data/lib/vcr/version.rb +1 -1
  53. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  54. data/spec/fixtures/cassette_spec/example.yml +79 -78
  55. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
  56. data/spec/fixtures/fake_example.com_responses.yml +78 -76
  57. data/spec/fixtures/match_requests_on.yml +147 -145
  58. data/spec/monkey_patches.rb +5 -5
  59. data/spec/support/http_library_adapters.rb +48 -0
  60. data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
  61. data/spec/support/sinatra_app.rb +12 -0
  62. data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
  63. data/spec/vcr/cassette/migrator_spec.rb +183 -0
  64. data/spec/vcr/cassette/serializers_spec.rb +122 -0
  65. data/spec/vcr/cassette_spec.rb +147 -83
  66. data/spec/vcr/configuration_spec.rb +11 -1
  67. data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
  68. data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
  69. data/spec/vcr/request_ignorer_spec.rb +1 -1
  70. data/spec/vcr/request_matcher_registry_spec.rb +46 -4
  71. data/spec/vcr/structs_spec.rb +309 -0
  72. data/spec/vcr_spec.rb +7 -0
  73. data/vcr.gemspec +9 -12
  74. metadata +75 -61
  75. data/lib/vcr/structs/http_interaction.rb +0 -58
  76. data/lib/vcr/structs/normalizers/body.rb +0 -24
  77. data/lib/vcr/structs/normalizers/header.rb +0 -64
  78. data/lib/vcr/structs/normalizers/status_message.rb +0 -17
  79. data/lib/vcr/structs/normalizers/uri.rb +0 -34
  80. data/lib/vcr/structs/request.rb +0 -13
  81. data/lib/vcr/structs/response.rb +0 -13
  82. data/lib/vcr/structs/response_status.rb +0 -5
  83. data/lib/vcr/util/yaml.rb +0 -11
  84. data/spec/support/shared_example_groups/normalizers.rb +0 -94
  85. data/spec/vcr/structs/http_interaction_spec.rb +0 -89
  86. data/spec/vcr/structs/request_spec.rb +0 -39
  87. data/spec/vcr/structs/response_spec.rb +0 -44
  88. data/spec/vcr/structs/response_status_spec.rb +0 -9
@@ -16,37 +16,39 @@ Feature: URI without param(s)
16
16
  Background:
17
17
  Given a previously recorded cassette file "cassettes/example.yml" with:
18
18
  """
19
- ---
20
- - !ruby/struct:VCR::HTTPInteraction
21
- request: !ruby/struct:VCR::Request
22
- method: :get
23
- uri: http://example.com:80/search?q=foo&timestamp=1316920490
24
- body:
25
- headers:
26
- response: !ruby/struct:VCR::Response
27
- status: !ruby/struct:VCR::ResponseStatus
19
+ ---
20
+ http_interactions:
21
+ - request:
22
+ method: get
23
+ uri: http://example.com/search?q=foo&timestamp=1316920490
24
+ body: ''
25
+ headers: {}
26
+ response:
27
+ status:
28
28
  code: 200
29
29
  message: OK
30
- headers:
31
- content-length:
32
- - "12"
30
+ headers:
31
+ Content-Length:
32
+ - '12'
33
33
  body: foo response
34
- http_version: "1.1"
35
- - !ruby/struct:VCR::HTTPInteraction
36
- request: !ruby/struct:VCR::Request
37
- method: :get
38
- uri: http://example.com:80/search?q=bar&timestamp=1296723437
39
- body:
40
- headers:
41
- response: !ruby/struct:VCR::Response
42
- status: !ruby/struct:VCR::ResponseStatus
34
+ http_version: '1.1'
35
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
36
+ - request:
37
+ method: get
38
+ uri: http://example.com/search?q=bar&timestamp=1296723437
39
+ body: ''
40
+ headers: {}
41
+ response:
42
+ status:
43
43
  code: 200
44
44
  message: OK
45
- headers:
46
- content-length:
47
- - "12"
45
+ headers:
46
+ Content-Length:
47
+ - '12'
48
48
  body: bar response
49
- http_version: "1.1"
49
+ http_version: '1.1'
50
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
51
+ recorded_with: VCR 2.0.0
50
52
  """
51
53
 
52
54
  Scenario: Match the URI on all but the timestamp query parameter
@@ -65,7 +67,7 @@ Feature: URI without param(s)
65
67
  end
66
68
 
67
69
  def search_uri(q)
68
- "http://example.com:80/search?q=#{q}&timestamp=#{Time.now.to_i}"
70
+ "http://example.com/search?q=#{q}&timestamp=#{Time.now.to_i}"
69
71
  end
70
72
 
71
73
  VCR.use_cassette('example') do
@@ -1,26 +1,66 @@
1
1
  require 'vcr'
2
+ require 'multi_json'
2
3
 
3
4
  module VCRHelpers
4
5
 
5
- def normalize_cassette_structs(content)
6
- structs = YAML.load(content)
6
+ def normalize_cassette_hash(cassette_hash)
7
+ cassette_hash['recorded_with'] = "VCR #{VCR.version}"
8
+ cassette_hash['http_interactions'].map! { |h| normalize_http_interaction(h) }
9
+ cassette_hash
10
+ end
7
11
 
8
- # Remove non-deterministic headers
9
- structs.each do |s|
10
- s.response.headers.reject! { |k, v| %w[ server date ].include?(k) }
12
+ def normalize_headers(object)
13
+ object.headers = {} and return if object.headers.nil?
14
+ object.headers = {}.tap do |hash|
15
+ object.headers.each do |key, value|
16
+ hash[key.downcase] = value
17
+ end
11
18
  end
19
+ end
20
+
21
+ def static_timestamp
22
+ @static_timestamp ||= Time.now
23
+ end
24
+
25
+ def normalize_http_interaction(hash)
26
+ VCR::HTTPInteraction.from_hash(hash).tap do |i|
27
+ normalize_headers(i.request)
28
+ normalize_headers(i.response)
29
+
30
+ i.recorded_at &&= static_timestamp
31
+ i.request.body ||= ''
32
+ i.response.body ||= ''
33
+ i.response.status.message ||= ''
34
+
35
+ # Remove non-deterministic headers and headers
36
+ # that get added by a particular HTTP library (but not by others)
37
+ i.response.headers.reject! { |k, v| %w[ server date connection ].include?(k) }
38
+ i.request.headers.reject! { |k, v| %w[ accept user-agent connection expect ].include?(k) }
39
+
40
+ # Some HTTP libraries include an extra space ("OK " instead of "OK")
41
+ i.response.status.message = i.response.status.message.strip
12
42
 
13
- case @stubbing_lib_for_current_scenario
14
- when /excon|faraday/
43
+ if @scenario_parameters.to_s =~ /excon|faraday/
15
44
  # Excon/Faraday do not expose the status message or http version,
16
45
  # so we have no way to record these attributes.
17
- structs.each do |s|
18
- s.response.status.message = nil
19
- s.response.http_version = nil
20
- end
46
+ i.response.status.message = nil
47
+ i.response.http_version = nil
48
+ elsif @scenario_parameters.to_s.include?('webmock')
49
+ # WebMock does not expose the HTTP version so we have no way to record it
50
+ i.response.http_version = nil
51
+ end
21
52
  end
53
+ end
22
54
 
23
- structs
55
+ def normalize_cassette_content(content)
56
+ return content unless @scenario_parameters.to_s.include?('patron')
57
+ cassette_hash = YAML.load(content)
58
+ cassette_hash['http_interactions'].map! do |hash|
59
+ VCR::HTTPInteraction.from_hash(hash).tap do |i|
60
+ i.request.headers = (i.request.headers || {}).merge!('Expect' => [''])
61
+ end.to_hash
62
+ end
63
+ YAML.dump(cassette_hash)
24
64
  end
25
65
 
26
66
  def modify_file(file_name, orig_text, new_text)
@@ -45,11 +85,11 @@ Given /^the directory "([^"]*)" does not exist$/ do |dir|
45
85
  end
46
86
 
47
87
  Given /^a previously recorded cassette file "([^"]*)" with:$/ do |file_name, content|
48
- write_file(file_name, content)
88
+ write_file(file_name, normalize_cassette_content(content))
49
89
  end
50
90
 
51
- Given /^(\d+) days have passed since the cassette was recorded$/ do |day_count|
52
- set_env('DAYS_PASSED', day_count)
91
+ Given /^it is (.*)$/ do |date_string|
92
+ set_env('DATE_STRING', date_string)
53
93
  end
54
94
 
55
95
  When /^I modify the file "([^"]*)" to replace "([^"]*)" with "([^"]*)"$/ do |file_name, orig_text, new_text|
@@ -76,7 +116,21 @@ end
76
116
 
77
117
  Then /^the file "([^"]*)" should contain YAML like:$/ do |file_name, expected_content|
78
118
  actual_content = in_current_dir { File.read(file_name) }
79
- normalize_cassette_structs(actual_content).should == normalize_cassette_structs(expected_content)
119
+ normalize_cassette_hash(YAML.load(actual_content)).should == normalize_cassette_hash(YAML.load(expected_content))
120
+ end
121
+
122
+ Then /^the file "([^"]*)" should contain JSON like:$/ do |file_name, expected_content|
123
+ actual_content = in_current_dir { File.read(file_name) }
124
+ actual = MultiJson.decode(actual_content)
125
+ expected = MultiJson.decode(expected_content)
126
+ normalize_cassette_hash(actual).should == normalize_cassette_hash(expected)
127
+ end
128
+
129
+ Then /^the file "([^"]*)" should contain ruby like:$/ do |file_name, expected_content|
130
+ actual_content = in_current_dir { File.read(file_name) }
131
+ actual = eval(actual_content)
132
+ expected = eval(expected_content)
133
+ normalize_cassette_hash(actual).should == normalize_cassette_hash(expected)
80
134
  end
81
135
 
82
136
  Then /^the file "([^"]*)" should contain each of these:$/ do |file_name, table|
@@ -103,7 +157,7 @@ Then /^the file "([^"]*)" should contain a YAML fragment like:$/ do |file_name,
103
157
  end
104
158
 
105
159
  Then /^the cassette "([^"]*)" should have the following response bodies:$/ do |file, table|
106
- interactions = in_current_dir { YAML.load_file(file) }
160
+ interactions = in_current_dir { YAML.load_file(file) }['http_interactions'].map { |h| VCR::HTTPInteraction.from_hash(h) }
107
161
  actual_response_bodies = interactions.map { |i| i.response.body }
108
162
  expected_response_bodies = table.raw.flatten
109
163
  actual_response_bodies.should =~ expected_response_bodies
@@ -2,6 +2,8 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  Bundler.setup
4
4
 
5
+ require 'ruby-debug' if !defined?(RUBY_ENGINE) && RUBY_VERSION != '1.9.3' && !ENV['CI']
6
+
5
7
  require 'aruba/cucumber'
6
8
 
7
9
  cucumer_helpers_file = '../../features/support/vcr_cucumber_helpers'
@@ -24,6 +26,6 @@ else
24
26
  end
25
27
 
26
28
  Before do
27
- @aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 60 : 10
29
+ @aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 60 : 20
28
30
  end
29
31
 
@@ -18,7 +18,10 @@ elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
18
18
  # it's probably a bug in it or rbx...so ignore it, for now.
19
19
 
20
20
  # I'm getting errors in the curb C extension in rbx.
21
- UNSUPPORTED_HTTP_LIBS = %w[ patron em-http-request curb ]
21
+
22
+ # Faraday and Typhoeus should be buildable on rbx, but the travis build times out,
23
+ # so we skip them to speed up the build on travis.
24
+ UNSUPPORTED_HTTP_LIBS = %w[ patron em-http-request curb faraday typhoeus ]
22
25
  elsif RUBY_PLATFORM == 'java'
23
26
  # These gems have C extensions and can't install on JRuby.
24
27
  c_dependent_libs = %w[ typhoeus patron curb em-http-request ]
@@ -44,8 +47,8 @@ end
44
47
  # logic in our step definitions based on the http stubbing library.
45
48
  Before do |scenario|
46
49
  if scenario.respond_to?(:cell_values)
47
- @stubbing_lib_for_current_scenario = scenario.cell_values.find { |v| v =~ /fakeweb|webmock|typhoeus|faraday|excon/ }
50
+ @scenario_parameters = scenario.cell_values
48
51
  else
49
- @stubbing_lib_for_current_scenario = nil
52
+ @scenario_parameters = nil
50
53
  end
51
54
  end
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  # This file gets symlinked into the tmp/aruba directory before
2
4
  # each scenario so that it is available to be required in them.
3
5
  $LOAD_PATH << '../../spec' unless $LOAD_PATH.include?('../../spec')
@@ -15,9 +17,9 @@ if running_under_aruba
15
17
  end
16
18
  end
17
19
 
18
- if ENV['DAYS_PASSED']
20
+ if ENV['DATE_STRING']
19
21
  require 'timecop'
20
- Timecop.travel(Time.now + ENV['DAYS_PASSED'].to_i.days)
22
+ Timecop.travel(Date.parse(ENV['DATE_STRING']))
21
23
  end
22
24
 
23
25
  def include_http_adapter_for(lib)
data/lib/vcr.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  require 'vcr/util/variable_args_block_caller'
2
- require 'vcr/util/yaml'
3
2
 
4
3
  require 'vcr/cassette'
4
+ require 'vcr/cassette/serializers'
5
5
  require 'vcr/configuration'
6
6
  require 'vcr/deprecations'
7
7
  require 'vcr/errors'
8
8
  require 'vcr/library_hooks'
9
9
  require 'vcr/request_ignorer'
10
10
  require 'vcr/request_matcher_registry'
11
+ require 'vcr/structs'
11
12
  require 'vcr/version'
12
13
 
13
- require 'vcr/structs/http_interaction'
14
14
 
15
15
  module VCR
16
16
  include VariableArgsBlockCaller
@@ -85,6 +85,10 @@ module VCR
85
85
  @library_hooks ||= LibraryHooks.new
86
86
  end
87
87
 
88
+ def cassette_serializers
89
+ @cassette_serializers ||= Cassette::Serializers.new
90
+ end
91
+
88
92
  def configuration
89
93
  @configuration ||= Configuration.new
90
94
  end
data/lib/vcr/cassette.rb CHANGED
@@ -1,21 +1,22 @@
1
1
  require 'fileutils'
2
2
  require 'erb'
3
- require 'set'
4
3
 
5
- require 'vcr/cassette/reader'
6
4
  require 'vcr/cassette/http_interaction_list'
5
+ require 'vcr/cassette/reader'
6
+ require 'vcr/cassette/serializers'
7
7
 
8
8
  module VCR
9
9
  class Cassette
10
10
  VALID_RECORD_MODES = [:all, :none, :new_episodes, :once]
11
11
 
12
- attr_reader :name, :record_mode, :match_requests_on, :erb, :re_record_interval, :tag, :http_interactions
12
+ attr_reader :name, :record_mode, :match_requests_on, :erb, :re_record_interval, :tag
13
13
 
14
14
  def initialize(name, options = {})
15
15
  options = VCR.configuration.default_cassette_options.merge(options)
16
16
  invalid_options = options.keys - [
17
17
  :record, :erb, :match_requests_on, :re_record_interval, :tag,
18
- :update_content_length_header, :allow_playback_repeats, :exclusive
18
+ :update_content_length_header, :allow_playback_repeats, :exclusive,
19
+ :serialize_with
19
20
  ]
20
21
 
21
22
  if invalid_options.size > 0
@@ -28,22 +29,44 @@ module VCR
28
29
  @match_requests_on = options[:match_requests_on]
29
30
  @re_record_interval = options[:re_record_interval]
30
31
  @tag = options[:tag]
31
- @record_mode = :all if should_re_record?
32
32
  @update_content_length_header = options[:update_content_length_header]
33
33
  @allow_playback_repeats = options[:allow_playback_repeats]
34
34
  @exclusive = options[:exclusive]
35
+ @serializer = VCR.cassette_serializers[options[:serialize_with]]
36
+ @record_mode = :all if should_re_record?
37
+ @parent_list = @exclusive ? HTTPInteractionList::NullList.new : VCR.http_interactions
35
38
 
36
39
  raise_error_unless_valid_record_mode
37
-
38
- load_recorded_interactions
39
40
  end
40
41
 
41
42
  def eject
42
43
  write_recorded_interactions_to_disk
43
44
  end
44
45
 
45
- def recorded_interactions
46
- @recorded_interactions ||= []
46
+ def previously_recorded_interactions
47
+ @previously_recorded_interactions ||= if file && File.size?(file)
48
+ deserialized_hash['http_interactions'].map { |h| HTTPInteraction.from_hash(h) }.tap do |interactions|
49
+ invoke_hook(:before_playback, interactions)
50
+
51
+ interactions.reject! do |i|
52
+ i.request.uri.is_a?(String) && VCR.request_ignorer.ignore?(i.request)
53
+ end
54
+
55
+ if update_content_length_header?
56
+ interactions.each { |i| i.response.update_content_length_header }
57
+ end
58
+ end
59
+ else
60
+ []
61
+ end
62
+ end
63
+
64
+ def http_interactions
65
+ @http_interactions ||= HTTPInteractionList.new \
66
+ should_stub_requests? ? previously_recorded_interactions : [],
67
+ match_requests_on,
68
+ @allow_playback_repeats,
69
+ @parent_list
47
70
  end
48
71
 
49
72
  def record_http_interaction(interaction)
@@ -55,7 +78,8 @@ module VCR
55
78
  end
56
79
 
57
80
  def file
58
- File.join(VCR.configuration.cassette_library_dir, "#{sanitized_name}.yml") if VCR.configuration.cassette_library_dir
81
+ return nil unless VCR.configuration.cassette_library_dir
82
+ File.join(VCR.configuration.cassette_library_dir, "#{sanitized_name}.#{@serializer.file_extension}")
59
83
  end
60
84
 
61
85
  def update_content_length_header?
@@ -70,6 +94,13 @@ module VCR
70
94
  end
71
95
  end
72
96
 
97
+ def serializable_hash
98
+ {
99
+ "http_interactions" => interactions_to_record.map(&:to_hash),
100
+ "recorded_with" => "VCR #{VCR.version}"
101
+ }
102
+ end
103
+
73
104
  private
74
105
 
75
106
  def sanitized_name
@@ -83,10 +114,16 @@ module VCR
83
114
  end
84
115
 
85
116
  def should_re_record?
86
- @re_record_interval &&
87
- File.exist?(file) &&
88
- File.stat(file).mtime + @re_record_interval < Time.now &&
89
- InternetConnection.available?
117
+ return false unless @re_record_interval
118
+ return false unless earliest_interaction_recorded_at
119
+ return false unless File.exist?(file)
120
+ return false unless InternetConnection.available?
121
+
122
+ earliest_interaction_recorded_at + @re_record_interval < Time.now
123
+ end
124
+
125
+ def earliest_interaction_recorded_at
126
+ previously_recorded_interactions.map(&:recorded_at).min
90
127
  end
91
128
 
92
129
  def should_stub_requests?
@@ -97,42 +134,12 @@ module VCR
97
134
  record_mode == :all
98
135
  end
99
136
 
100
- def load_recorded_interactions
101
- if file && File.size?(file)
102
- interactions = VCR::YAML.load(raw_yaml_content)
103
-
104
- invoke_hook(:before_playback, interactions)
105
-
106
- interactions.reject! do |i|
107
- i.request.uri.is_a?(String) && VCR.request_ignorer.ignore?(i.request)
108
- end
109
-
110
- if update_content_length_header?
111
- interactions.each { |i| i.response.update_content_length_header }
112
- end
113
-
114
- recorded_interactions.replace(interactions)
115
- end
116
-
117
- interactions = should_stub_requests? ? recorded_interactions : []
118
-
119
- @http_interactions = HTTPInteractionList.new(
120
- interactions,
121
- match_requests_on,
122
- @allow_playback_repeats,
123
- parent_list)
124
- end
125
-
126
- def parent_list
127
- @exclusive ? HTTPInteractionList::NullList.new : VCR.http_interactions
128
- end
129
-
130
137
  def raw_yaml_content
131
138
  VCR::Cassette::Reader.new(file, erb).read
132
139
  end
133
140
 
134
141
  def merged_interactions
135
- old_interactions = recorded_interactions
142
+ old_interactions = previously_recorded_interactions
136
143
 
137
144
  if should_remove_matching_existing_interactions?
138
145
  new_interaction_list = HTTPInteractionList.new(new_recorded_interactions, match_requests_on)
@@ -144,17 +151,21 @@ module VCR
144
151
  old_interactions + new_recorded_interactions
145
152
  end
146
153
 
154
+ def interactions_to_record
155
+ merged_interactions.tap do |interactions|
156
+ invoke_hook(:before_record, interactions)
157
+ end
158
+ end
159
+
147
160
  def write_recorded_interactions_to_disk
148
161
  return unless VCR.configuration.cassette_library_dir
149
- return if new_recorded_interactions.empty?
150
-
151
- interactions = merged_interactions
152
- invoke_hook(:before_record, interactions)
153
- return if interactions.empty?
162
+ return if new_recorded_interactions.none?
163
+ hash = serializable_hash
164
+ return if hash["http_interactions"].none?
154
165
 
155
166
  directory = File.dirname(file)
156
167
  FileUtils.mkdir_p directory unless File.exist?(directory)
157
- File.open(file, 'w') { |f| f.write VCR::YAML.dump(interactions) }
168
+ File.open(file, 'w') { |f| f.write @serializer.serialize(hash) }
158
169
  end
159
170
 
160
171
  def invoke_hook(type, interactions)
@@ -163,5 +174,18 @@ module VCR
163
174
  i.ignored?
164
175
  end
165
176
  end
177
+
178
+ def deserialized_hash
179
+ @deserialized_hash ||= @serializer.deserialize(raw_yaml_content).tap do |hash|
180
+ unless hash.is_a?(Hash) && hash['http_interactions'].is_a?(Array)
181
+ raise Errors::InvalidCassetteFormatError.new \
182
+ "#{file} does not appear to be a valid VCR 2.0 cassette. " +
183
+ "VCR 1.x cassettes are not valid with VCR 2.0. When upgrading from " +
184
+ "VCR 1.x, it is recommended that you delete all your existing cassettes and " +
185
+ "re-record them, or use the provided vcr:migrate_cassettes rake task to migrate " +
186
+ "them. For more info, see the VCR upgrade guide."
187
+ end
188
+ end
189
+ end
166
190
  end
167
191
  end