vcr 2.0.0.beta1 → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +37 -2
- data/Gemfile +2 -2
- data/README.md +10 -1
- data/Rakefile +43 -7
- data/Upgrade.md +45 -0
- data/features/.nav +1 -0
- data/features/cassettes/automatic_re_recording.feature +19 -17
- data/features/cassettes/dynamic_erb.feature +32 -28
- data/features/cassettes/exclusive.feature +28 -24
- data/features/cassettes/format.feature +213 -31
- data/features/cassettes/update_content_length_header.feature +20 -18
- data/features/configuration/filter_sensitive_data.feature +4 -4
- data/features/configuration/hooks.feature +27 -23
- data/features/http_libraries/em_http_request.feature +79 -75
- data/features/record_modes/all.feature +14 -14
- data/features/record_modes/new_episodes.feature +15 -15
- data/features/record_modes/none.feature +15 -15
- data/features/record_modes/once.feature +15 -15
- data/features/request_matching/body.feature +25 -23
- data/features/request_matching/custom_matcher.feature +25 -23
- data/features/request_matching/headers.feature +32 -36
- data/features/request_matching/host.feature +27 -25
- data/features/request_matching/identical_request_sequence.feature +27 -25
- data/features/request_matching/method.feature +27 -25
- data/features/request_matching/path.feature +27 -25
- data/features/request_matching/playback_repeats.feature +27 -25
- data/features/request_matching/uri.feature +27 -25
- data/features/request_matching/uri_without_param.feature +28 -26
- data/features/step_definitions/cli_steps.rb +71 -17
- data/features/support/env.rb +3 -1
- data/features/support/http_lib_filters.rb +6 -3
- data/features/support/vcr_cucumber_helpers.rb +4 -2
- data/lib/vcr.rb +6 -2
- data/lib/vcr/cassette.rb +75 -51
- data/lib/vcr/cassette/migrator.rb +111 -0
- data/lib/vcr/cassette/serializers.rb +35 -0
- data/lib/vcr/cassette/serializers/json.rb +23 -0
- data/lib/vcr/cassette/serializers/psych.rb +24 -0
- data/lib/vcr/cassette/serializers/syck.rb +35 -0
- data/lib/vcr/cassette/serializers/yaml.rb +24 -0
- data/lib/vcr/configuration.rb +6 -1
- data/lib/vcr/errors.rb +1 -1
- data/lib/vcr/library_hooks/excon.rb +1 -7
- data/lib/vcr/library_hooks/typhoeus.rb +6 -22
- data/lib/vcr/library_hooks/webmock.rb +1 -1
- data/lib/vcr/middleware/faraday.rb +1 -1
- data/lib/vcr/request_matcher_registry.rb +43 -30
- data/lib/vcr/structs.rb +209 -0
- data/lib/vcr/tasks/vcr.rake +9 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
- data/spec/fixtures/cassette_spec/example.yml +79 -78
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
- data/spec/fixtures/fake_example.com_responses.yml +78 -76
- data/spec/fixtures/match_requests_on.yml +147 -145
- data/spec/monkey_patches.rb +5 -5
- data/spec/support/http_library_adapters.rb +48 -0
- data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
- data/spec/support/sinatra_app.rb +12 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
- data/spec/vcr/cassette/migrator_spec.rb +183 -0
- data/spec/vcr/cassette/serializers_spec.rb +122 -0
- data/spec/vcr/cassette_spec.rb +147 -83
- data/spec/vcr/configuration_spec.rb +11 -1
- data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
- data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
- data/spec/vcr/request_ignorer_spec.rb +1 -1
- data/spec/vcr/request_matcher_registry_spec.rb +46 -4
- data/spec/vcr/structs_spec.rb +309 -0
- data/spec/vcr_spec.rb +7 -0
- data/vcr.gemspec +9 -12
- metadata +75 -61
- data/lib/vcr/structs/http_interaction.rb +0 -58
- data/lib/vcr/structs/normalizers/body.rb +0 -24
- data/lib/vcr/structs/normalizers/header.rb +0 -64
- data/lib/vcr/structs/normalizers/status_message.rb +0 -17
- data/lib/vcr/structs/normalizers/uri.rb +0 -34
- data/lib/vcr/structs/request.rb +0 -13
- data/lib/vcr/structs/response.rb +0 -13
- data/lib/vcr/structs/response_status.rb +0 -5
- data/lib/vcr/util/yaml.rb +0 -11
- data/spec/support/shared_example_groups/normalizers.rb +0 -94
- data/spec/vcr/structs/http_interaction_spec.rb +0 -89
- data/spec/vcr/structs/request_spec.rb +0 -39
- data/spec/vcr/structs/response_spec.rb +0 -44
- 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
|
-
|
21
|
-
|
22
|
-
method:
|
23
|
-
uri: http://example.com
|
24
|
-
body:
|
25
|
-
headers:
|
26
|
-
response:
|
27
|
-
status:
|
19
|
+
---
|
20
|
+
http_interactions:
|
21
|
+
- request:
|
22
|
+
method: get
|
23
|
+
uri: http://example.com/search?q=foo×tamp=1316920490
|
24
|
+
body: ''
|
25
|
+
headers: {}
|
26
|
+
response:
|
27
|
+
status:
|
28
28
|
code: 200
|
29
29
|
message: OK
|
30
|
-
headers:
|
31
|
-
|
32
|
-
-
|
30
|
+
headers:
|
31
|
+
Content-Length:
|
32
|
+
- '12'
|
33
33
|
body: foo response
|
34
|
-
http_version:
|
35
|
-
|
36
|
-
|
37
|
-
method:
|
38
|
-
uri: http://example.com
|
39
|
-
body:
|
40
|
-
headers:
|
41
|
-
response:
|
42
|
-
status:
|
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×tamp=1296723437
|
39
|
+
body: ''
|
40
|
+
headers: {}
|
41
|
+
response:
|
42
|
+
status:
|
43
43
|
code: 200
|
44
44
|
message: OK
|
45
|
-
headers:
|
46
|
-
|
47
|
-
-
|
45
|
+
headers:
|
46
|
+
Content-Length:
|
47
|
+
- '12'
|
48
48
|
body: bar response
|
49
|
-
http_version:
|
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
|
70
|
+
"http://example.com/search?q=#{q}×tamp=#{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
|
6
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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 /^(
|
52
|
-
set_env('
|
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
|
-
|
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
|
data/features/support/env.rb
CHANGED
@@ -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 :
|
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
|
-
|
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
|
-
@
|
50
|
+
@scenario_parameters = scenario.cell_values
|
48
51
|
else
|
49
|
-
@
|
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['
|
20
|
+
if ENV['DATE_STRING']
|
19
21
|
require 'timecop'
|
20
|
-
Timecop.travel(
|
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
|
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
|
46
|
-
@
|
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
|
-
|
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
|
-
|
88
|
-
File.
|
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 =
|
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.
|
150
|
-
|
151
|
-
|
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
|
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
|