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.
- 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
|