vcr 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.limited_red +1 -0
- data/.travis.yml +10 -1
- data/.yardopts +9 -0
- data/CHANGELOG.md +51 -1
- data/Gemfile +5 -1
- data/LICENSE +1 -1
- data/README.md +23 -28
- data/Rakefile +63 -18
- data/Upgrade.md +200 -0
- data/features/.nav +2 -0
- data/features/cassettes/automatic_re_recording.feature +19 -15
- data/features/cassettes/dynamic_erb.feature +12 -4
- data/features/cassettes/exclusive.feature +31 -23
- data/features/cassettes/format.feature +54 -30
- data/features/cassettes/naming.feature +1 -1
- data/features/cassettes/update_content_length_header.feature +16 -12
- data/features/configuration/allow_http_connections_when_no_cassette.feature +1 -1
- data/features/configuration/debug_logging.feature +52 -0
- data/features/configuration/filter_sensitive_data.feature +4 -4
- data/features/configuration/hook_into.feature +5 -2
- data/features/configuration/ignore_request.feature +5 -3
- data/features/configuration/preserve_exact_body_bytes.feature +103 -0
- data/features/hooks/after_http_request.feature +17 -4
- data/features/hooks/around_http_request.feature +2 -1
- data/features/hooks/before_http_request.feature +25 -8
- data/features/hooks/before_playback.feature +16 -12
- data/features/hooks/before_record.feature +2 -2
- data/features/http_libraries/em_http_request.feature +82 -58
- data/features/http_libraries/net_http.feature +6 -6
- data/features/middleware/faraday.feature +2 -1
- data/features/middleware/rack.feature +2 -2
- data/features/record_modes/all.feature +19 -15
- data/features/record_modes/new_episodes.feature +17 -13
- data/features/record_modes/none.feature +15 -11
- data/features/record_modes/once.feature +16 -12
- data/features/request_matching/body.feature +28 -20
- data/features/request_matching/custom_matcher.feature +28 -20
- data/features/request_matching/headers.feature +34 -26
- data/features/request_matching/host.feature +28 -20
- data/features/request_matching/identical_request_sequence.feature +28 -20
- data/features/request_matching/method.feature +28 -20
- data/features/request_matching/path.feature +28 -20
- data/features/request_matching/playback_repeats.feature +28 -20
- data/features/request_matching/uri.feature +28 -20
- data/features/request_matching/uri_without_param.feature +28 -20
- data/features/support/env.rb +7 -6
- data/features/support/vcr_cucumber_helpers.rb +1 -0
- data/features/test_frameworks/cucumber.feature +8 -8
- data/features/test_frameworks/rspec_macro.feature +4 -4
- data/features/test_frameworks/rspec_metadata.feature +6 -6
- data/features/test_frameworks/shoulda.feature +1 -1
- data/features/test_frameworks/test_unit.feature +1 -1
- data/lib/vcr.rb +156 -5
- data/lib/vcr/cassette.rb +80 -30
- data/lib/vcr/cassette/http_interaction_list.rb +33 -4
- data/lib/vcr/cassette/migrator.rb +2 -3
- data/lib/vcr/cassette/reader.rb +1 -0
- data/lib/vcr/cassette/serializers.rb +22 -0
- data/lib/vcr/cassette/serializers/json.rb +27 -2
- data/lib/vcr/cassette/serializers/psych.rb +26 -2
- data/lib/vcr/cassette/serializers/syck.rb +28 -2
- data/lib/vcr/cassette/serializers/yaml.rb +28 -2
- data/lib/vcr/configuration.rb +348 -10
- data/lib/vcr/deprecations.rb +8 -0
- data/lib/vcr/errors.rb +40 -0
- data/lib/vcr/extensions/net_http_response.rb +12 -11
- data/lib/vcr/library_hooks.rb +1 -0
- data/lib/vcr/library_hooks/excon.rb +24 -3
- data/lib/vcr/library_hooks/fakeweb.rb +32 -16
- data/lib/vcr/library_hooks/faraday.rb +3 -0
- data/lib/vcr/library_hooks/typhoeus.rb +40 -37
- data/lib/vcr/library_hooks/webmock.rb +54 -34
- data/lib/vcr/middleware/faraday.rb +13 -0
- data/lib/vcr/middleware/rack.rb +35 -0
- data/lib/vcr/request_handler.rb +60 -8
- data/lib/vcr/request_ignorer.rb +1 -0
- data/lib/vcr/request_matcher_registry.rb +28 -0
- data/lib/vcr/structs.rb +245 -38
- data/lib/vcr/test_frameworks/cucumber.rb +10 -0
- data/lib/vcr/test_frameworks/rspec.rb +26 -1
- data/lib/vcr/util/hooks.rb +29 -27
- data/lib/vcr/util/internet_connection.rb +2 -0
- data/lib/vcr/util/logger.rb +25 -0
- data/lib/vcr/util/variable_args_block_caller.rb +1 -0
- data/lib/vcr/util/version_checker.rb +1 -0
- data/lib/vcr/version.rb +8 -1
- data/spec/capture_warnings.rb +3 -3
- data/spec/monkey_patches.rb +28 -13
- data/spec/spec_helper.rb +17 -0
- data/spec/support/http_library_adapters.rb +7 -4
- data/spec/support/shared_example_groups/hook_into_http_library.rb +96 -32
- data/spec/support/shared_example_groups/request_hooks.rb +9 -8
- data/spec/support/sinatra_app.rb +3 -1
- data/spec/support/vcr_localhost_server.rb +1 -0
- data/spec/vcr/cassette/http_interaction_list_spec.rb +119 -54
- data/spec/vcr/cassette/migrator_spec.rb +19 -6
- data/spec/vcr/cassette/serializers_spec.rb +51 -6
- data/spec/vcr/cassette_spec.rb +44 -19
- data/spec/vcr/configuration_spec.rb +91 -6
- data/spec/vcr/library_hooks/excon_spec.rb +54 -16
- data/spec/vcr/library_hooks/fakeweb_spec.rb +12 -21
- data/spec/vcr/library_hooks/typhoeus_spec.rb +2 -29
- data/spec/vcr/library_hooks/webmock_spec.rb +4 -18
- data/spec/vcr/middleware/faraday_spec.rb +1 -16
- data/spec/vcr/structs_spec.rb +194 -61
- data/spec/vcr/test_frameworks/rspec_spec.rb +10 -0
- data/spec/vcr/util/hooks_spec.rb +104 -56
- data/spec/vcr/util/version_checker_spec.rb +45 -0
- data/spec/vcr_spec.rb +11 -0
- data/vcr.gemspec +30 -34
- metadata +149 -95
- data/spec/support/shared_example_groups/version_checking.rb +0 -34
data/features/support/env.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler'
|
3
3
|
Bundler.setup
|
4
|
+
require 'limited_red/plugins/cucumber' unless ENV['CI']
|
4
5
|
|
5
6
|
require 'ruby-debug' if !defined?(RUBY_ENGINE) && RUBY_VERSION != '1.9.3' && !ENV['CI']
|
6
7
|
|
@@ -14,6 +15,12 @@ end
|
|
14
15
|
Before do
|
15
16
|
load_paths, requires = ['../../lib'], []
|
16
17
|
|
18
|
+
# Put any bundler-managed gems (such as :git gems) on the load path for when aruba shells out.
|
19
|
+
# Alternatively, we could hook up aruba to use bundler when it shells out, but invoking bundler
|
20
|
+
# for each and every time aruba starts ruby would slow everything down. We really only need it for
|
21
|
+
# bundler-managed gems.
|
22
|
+
load_paths.push($LOAD_PATH.grep %r|bundler/gems|)
|
23
|
+
|
17
24
|
if RUBY_VERSION < '1.9'
|
18
25
|
requires << "rubygems"
|
19
26
|
else
|
@@ -24,12 +31,6 @@ Before do
|
|
24
31
|
requires.map! { |r| "-r#{r}" }
|
25
32
|
set_env('RUBYOPT', "-I#{load_paths.join(':')} #{requires.join(' ')}")
|
26
33
|
|
27
|
-
if RUBY_PLATFORM == 'java'
|
28
|
-
# ideas taken from: http://blog.headius.com/2010/03/jruby-startup-time-tips.html
|
29
|
-
set_env('JRUBY_OPTS', '-X-C') # disable JIT since these processes are so short lived
|
30
|
-
set_env('JAVA_OPTS', '-d32') # force jRuby to use client JVM for faster startup times
|
31
|
-
end
|
32
|
-
|
33
34
|
if additional_paths.any?
|
34
35
|
existing_paths = ENV['PATH'].split(':')
|
35
36
|
set_env('PATH', (additional_paths + existing_paths).join(':'))
|
@@ -96,10 +96,10 @@ Feature: Usage with Cucumber
|
|
96
96
|
| GET http://localhost:7777/disallowed_1 |
|
97
97
|
| An HTTP request has been made that VCR does not know how to handle: |
|
98
98
|
| GET http://localhost:7777/disallowed_2 |
|
99
|
-
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "
|
100
|
-
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "
|
101
|
-
And the file "features/cassettes/nested_cassette.yml" should contain "
|
102
|
-
And the file "features/cassettes/allowed.yml" should contain "
|
99
|
+
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "Hello localhost_request_1"
|
100
|
+
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "Hello localhost_request_2"
|
101
|
+
And the file "features/cassettes/nested_cassette.yml" should contain "Hello nested_cassette"
|
102
|
+
And the file "features/cassettes/allowed.yml" should contain "Hello allowed"
|
103
103
|
|
104
104
|
# Run again without the server; we'll get the same responses because VCR
|
105
105
|
# will replay the recorded responses.
|
@@ -110,8 +110,8 @@ Feature: Usage with Cucumber
|
|
110
110
|
| GET http://localhost:7777/disallowed_1 |
|
111
111
|
| An HTTP request has been made that VCR does not know how to handle: |
|
112
112
|
| GET http://localhost:7777/disallowed_2 |
|
113
|
-
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "
|
114
|
-
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "
|
115
|
-
And the file "features/cassettes/nested_cassette.yml" should contain "
|
116
|
-
And the file "features/cassettes/allowed.yml" should contain "
|
113
|
+
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "Hello localhost_request_1"
|
114
|
+
And the file "features/cassettes/cucumber_tags/localhost_request.yml" should contain "Hello localhost_request_2"
|
115
|
+
And the file "features/cassettes/nested_cassette.yml" should contain "Hello nested_cassette"
|
116
|
+
And the file "features/cassettes/allowed.yml" should contain "Hello allowed"
|
117
117
|
|
@@ -76,8 +76,8 @@ Feature: Usage with RSpec macro
|
|
76
76
|
"""
|
77
77
|
When I run `rspec spec/vcr_example_spec.rb`
|
78
78
|
Then the output should contain "2 examples, 0 failures"
|
79
|
-
And the file "spec/cassettes/VCR-RSpec_integration/without_an_explicit_cassette_name.yml" should contain "
|
80
|
-
And the file "spec/cassettes/net_http_example.yml" should contain "
|
79
|
+
And the file "spec/cassettes/VCR-RSpec_integration/without_an_explicit_cassette_name.yml" should contain "Hello"
|
80
|
+
And the file "spec/cassettes/net_http_example.yml" should contain "Hello"
|
81
81
|
|
82
82
|
@rspec-1 @exclude-jruby
|
83
83
|
Scenario: Use `use_vcr_cassette` macro with RSpec 1
|
@@ -101,6 +101,6 @@ Feature: Usage with RSpec macro
|
|
101
101
|
"""
|
102
102
|
When I run `spec spec/vcr_example_spec.rb`
|
103
103
|
Then the output should contain "2 examples, 0 failures"
|
104
|
-
And the file "spec/cassettes/VCR-RSpec_integration/without_an_explicit_cassette_name.yml" should contain "
|
105
|
-
And the file "spec/cassettes/net_http_example.yml" should contain "
|
104
|
+
And the file "spec/cassettes/VCR-RSpec_integration/without_an_explicit_cassette_name.yml" should contain "Hello"
|
105
|
+
And the file "spec/cassettes/net_http_example.yml" should contain "Hello"
|
106
106
|
|
@@ -31,7 +31,7 @@ Feature: Usage with RSpec metadata
|
|
31
31
|
|
32
32
|
Scenario: Use `:vcr` metadata
|
33
33
|
Given a file named "spec/vcr_example_spec.rb" with:
|
34
|
-
"""
|
34
|
+
"""ruby
|
35
35
|
start_sinatra_app(:port => 7777) do
|
36
36
|
get('/') { "Hello" }
|
37
37
|
end
|
@@ -66,14 +66,14 @@ Feature: Usage with RSpec metadata
|
|
66
66
|
"""
|
67
67
|
When I run `rspec spec/vcr_example_spec.rb`
|
68
68
|
Then it should pass with "4 examples, 0 failures"
|
69
|
-
And the file "spec/cassettes/VCR_example_group_metadata/records_an_http_request.yml" should contain "
|
70
|
-
And the file "spec/cassettes/VCR_example_group_metadata/records_another_http_request.yml" should contain "
|
71
|
-
And the file "spec/cassettes/VCR_example_group_metadata/in_a_nested_example_group/records_another_one.yml" should contain "
|
72
|
-
And the file "spec/cassettes/VCR_example_metadata/records_an_http_request.yml" should contain "
|
69
|
+
And the file "spec/cassettes/VCR_example_group_metadata/records_an_http_request.yml" should contain "Hello"
|
70
|
+
And the file "spec/cassettes/VCR_example_group_metadata/records_another_http_request.yml" should contain "Hello"
|
71
|
+
And the file "spec/cassettes/VCR_example_group_metadata/in_a_nested_example_group/records_another_one.yml" should contain "Hello"
|
72
|
+
And the file "spec/cassettes/VCR_example_metadata/records_an_http_request.yml" should contain "Hello"
|
73
73
|
|
74
74
|
Scenario: Pass a hash to set the cassette options
|
75
75
|
Given a file named "spec/vcr_example_spec.rb" with:
|
76
|
-
"""
|
76
|
+
"""ruby
|
77
77
|
require 'spec_helper'
|
78
78
|
|
79
79
|
describe "Using an options hash", :vcr => { :cassette_name => "example", :record => :new_episodes } do
|
@@ -56,7 +56,7 @@ Feature: Usage with Shoulda
|
|
56
56
|
When I set the "SERVER" environment variable to "true"
|
57
57
|
And I run `ruby -Itest test/vcr_example_test.rb`
|
58
58
|
Then it should pass with "1 tests, 1 assertions, 0 failures, 0 errors"
|
59
|
-
And the file "test/fixtures/vcr_cassettes/shoulda_example.yml" should contain "
|
59
|
+
And the file "test/fixtures/vcr_cassettes/shoulda_example.yml" should contain "Hello"
|
60
60
|
|
61
61
|
# Run again without starting the sinatra server so the response will be replayed
|
62
62
|
When I set the "SERVER" environment variable to "false"
|
@@ -38,7 +38,7 @@ Feature: Usage with Test::Unit
|
|
38
38
|
When I set the "SERVER" environment variable to "true"
|
39
39
|
And I run `ruby -Itest test/vcr_example_test.rb`
|
40
40
|
Then it should pass with "1 tests, 1 assertions, 0 failures, 0 errors"
|
41
|
-
And the file "test/fixtures/vcr_cassettes/test_unit_example.yml" should contain "
|
41
|
+
And the file "test/fixtures/vcr_cassettes/test_unit_example.yml" should contain "Hello"
|
42
42
|
|
43
43
|
# Run again without starting the sinatra server so the response will be replayed
|
44
44
|
When I set the "SERVER" environment variable to "false"
|
data/lib/vcr.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'vcr/util/logger'
|
1
2
|
require 'vcr/util/variable_args_block_caller'
|
2
3
|
|
3
4
|
require 'vcr/cassette'
|
@@ -11,7 +12,9 @@ require 'vcr/request_matcher_registry'
|
|
11
12
|
require 'vcr/structs'
|
12
13
|
require 'vcr/version'
|
13
14
|
|
14
|
-
|
15
|
+
# The main entry point for VCR.
|
16
|
+
# @note This module is extended onto itself; thus, the methods listed
|
17
|
+
# here as instance methods are available directly off of VCR.
|
15
18
|
module VCR
|
16
19
|
include VariableArgsBlockCaller
|
17
20
|
include Errors
|
@@ -27,10 +30,75 @@ module VCR
|
|
27
30
|
autoload :Rack, 'vcr/middleware/rack'
|
28
31
|
end
|
29
32
|
|
33
|
+
# The currently active cassette.
|
34
|
+
#
|
35
|
+
# @return [nil, VCR::Cassette] The current cassette or nil if there is
|
36
|
+
# no current cassette.
|
30
37
|
def current_cassette
|
31
38
|
cassettes.last
|
32
39
|
end
|
33
40
|
|
41
|
+
# Inserts the named cassette using the given cassette options.
|
42
|
+
# New HTTP interactions, if allowed by the cassette's `:record` option, will
|
43
|
+
# be recorded to the cassette. The cassette's existing HTTP interactions
|
44
|
+
# will be used to stub requests, unless prevented by the cassete's
|
45
|
+
# `:record` option.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# VCR.insert_cassette('twitter', :record => :new_episodes)
|
49
|
+
#
|
50
|
+
# # ...later, after making an HTTP request:
|
51
|
+
#
|
52
|
+
# VCR.eject_cassette
|
53
|
+
#
|
54
|
+
# @param name [#to_s] The name of the cassette. VCR will sanitize
|
55
|
+
# this to ensure it is a valid file name.
|
56
|
+
# @param options [Hash] The cassette options. The given options will
|
57
|
+
# be merged with the configured default_cassette_options.
|
58
|
+
# @option options :record [:all, :none, :new_episodes, :once] The record mode.
|
59
|
+
# @option options :erb [Boolean, Hash] Whether or not to evaluate the
|
60
|
+
# cassette as an ERB template. Defaults to false. A hash can be used
|
61
|
+
# to provide the ERB template with local variables.
|
62
|
+
# @option options :match_requests_on [Array<Symbol, #call>] List of request matchers
|
63
|
+
# to use to determine what recorded HTTP interaction to replay. Defaults to
|
64
|
+
# [:method, :uri]. The built-in matchers are :method, :uri, :host, :path, :headers
|
65
|
+
# and :body. You can also pass the name of a registered custom request matcher or
|
66
|
+
# any object that responds to #call.
|
67
|
+
# @option options :re_record_interval [Integer] When given, the
|
68
|
+
# cassette will be re-recorded at the given interval, in seconds.
|
69
|
+
# @option options :tag [Symbol] Used to apply tagged `before_record`
|
70
|
+
# and `before_playback` hooks to the cassette.
|
71
|
+
# @option options :tags [Array<Symbol>] Used to apply multiple tags to
|
72
|
+
# a cassette so that tagged `before_record` and `before_playback` hooks
|
73
|
+
# will apply to the cassette.
|
74
|
+
# @option options :update_content_length_header [Boolean] Whether or
|
75
|
+
# not to overwrite the Content-Length header of the responses to
|
76
|
+
# match the length of the response body. Defaults to false.
|
77
|
+
# @option options :allow_playback_repeats [Boolean] Whether or not to
|
78
|
+
# allow a single HTTP interaction to be played back multiple times.
|
79
|
+
# Defaults to false.
|
80
|
+
# @option options :exclusive [Boolean] Whether or not to use only this
|
81
|
+
# cassette and to completely ignore any cassettes in the cassettes stack.
|
82
|
+
# Defaults to false.
|
83
|
+
# @option options :serialize_with [Symbol] Which serializer to use.
|
84
|
+
# Valid values are :yaml, :syck, :psych, :json or any registered
|
85
|
+
# custom serializer. Defaults to :yaml.
|
86
|
+
# @option options :preserve_exact_body_bytes [Boolean] Whether or not
|
87
|
+
# to base64 encode the bytes of the requests and responses for this cassette
|
88
|
+
# when serializing it. See also `VCR::Configuration#preserve_exact_body_bytes`.
|
89
|
+
#
|
90
|
+
# @return [VCR::Cassette] the inserted cassette
|
91
|
+
#
|
92
|
+
# @raise [ArgumentError] when the given cassette is already being used.
|
93
|
+
# @raise [VCR::Errors::TurnedOffError] when VCR has been turned off
|
94
|
+
# without using the :ignore_cassettes option.
|
95
|
+
# @raise [VCR::Errors::MissingERBVariableError] when the `:erb` option
|
96
|
+
# is used and the ERB template requires variables that you did not provide.
|
97
|
+
#
|
98
|
+
# @note If you use this method you _must_ call `eject_cassette` when you
|
99
|
+
# are done. It is generally recommended that you use {#use_cassette}
|
100
|
+
# unless your code-under-test cannot be run as a block.
|
101
|
+
#
|
34
102
|
def insert_cassette(name, options = {})
|
35
103
|
if turned_on?
|
36
104
|
if cassettes.any? { |c| c.name == name }
|
@@ -47,14 +115,36 @@ module VCR
|
|
47
115
|
end
|
48
116
|
end
|
49
117
|
|
118
|
+
# Ejects the current cassette. The cassette will no longer be used.
|
119
|
+
# In addition, any newly recorded HTTP interactions will be written to
|
120
|
+
# disk.
|
121
|
+
#
|
122
|
+
# @return [VCR::Cassette, nil] the ejected cassette if there was one
|
50
123
|
def eject_cassette
|
51
|
-
cassette = cassettes.
|
124
|
+
cassette = cassettes.last
|
52
125
|
cassette.eject if cassette
|
53
|
-
|
126
|
+
cassettes.pop
|
54
127
|
end
|
55
128
|
|
56
|
-
|
57
|
-
|
129
|
+
# Inserts a cassette using the given name and options, runs the given
|
130
|
+
# block, and ejects the cassette.
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# VCR.use_cassette('twitter', :record => :new_episodes) do
|
134
|
+
# # make an HTTP request
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# @param (see #insert_cassette)
|
138
|
+
# @option (see #insert_cassette)
|
139
|
+
# @yield Block to run while this cassette is in use.
|
140
|
+
# @yieldparam cassette [(optional) VCR::Cassette] the cassette that has
|
141
|
+
# been inserted.
|
142
|
+
# @raise (see #insert_cassette)
|
143
|
+
# @return [void]
|
144
|
+
# @see #insert_cassette
|
145
|
+
# @see #eject_cassette
|
146
|
+
def use_cassette(name, options = {}, &block)
|
147
|
+
cassette = insert_cassette(name, options)
|
58
148
|
|
59
149
|
begin
|
60
150
|
call_block(block, cassette)
|
@@ -63,45 +153,76 @@ module VCR
|
|
63
153
|
end
|
64
154
|
end
|
65
155
|
|
156
|
+
# @private
|
66
157
|
def http_interactions
|
67
158
|
return current_cassette.http_interactions if current_cassette
|
68
159
|
VCR::Cassette::HTTPInteractionList::NullList
|
69
160
|
end
|
70
161
|
|
162
|
+
# @private
|
71
163
|
def real_http_connections_allowed?
|
72
164
|
return current_cassette.recording? if current_cassette
|
73
165
|
configuration.allow_http_connections_when_no_cassette? || @turned_off
|
74
166
|
end
|
75
167
|
|
168
|
+
# @return [RequestMatcherRegistry] the request matcher registry
|
76
169
|
def request_matchers
|
77
170
|
@request_matchers ||= RequestMatcherRegistry.new
|
78
171
|
end
|
79
172
|
|
173
|
+
# @private
|
80
174
|
def request_ignorer
|
81
175
|
@request_ignorer ||= RequestIgnorer.new
|
82
176
|
end
|
83
177
|
|
178
|
+
# @private
|
84
179
|
def library_hooks
|
85
180
|
@library_hooks ||= LibraryHooks.new
|
86
181
|
end
|
87
182
|
|
183
|
+
# @private
|
88
184
|
def cassette_serializers
|
89
185
|
@cassette_serializers ||= Cassette::Serializers.new
|
90
186
|
end
|
91
187
|
|
188
|
+
# @return [VCR::Configuration] the VCR configuration.
|
92
189
|
def configuration
|
93
190
|
@configuration ||= Configuration.new
|
94
191
|
end
|
95
192
|
|
193
|
+
# Used to configure VCR.
|
194
|
+
#
|
195
|
+
# @example
|
196
|
+
# VCR.configure do |c|
|
197
|
+
# c.some_config_option = true
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# @yield the configuration block
|
201
|
+
# @yieldparam config [VCR::Configuration] the configuration object
|
202
|
+
# @return [void]
|
96
203
|
def configure
|
97
204
|
yield configuration
|
98
205
|
end
|
99
206
|
|
207
|
+
# Sets up `Before` and `After` cucumber hooks in order to
|
208
|
+
# use VCR with particular cucumber tags.
|
209
|
+
#
|
210
|
+
# @example
|
211
|
+
# VCR.cucumber_tags do |t|
|
212
|
+
# t.tags "tag1", "tag2"
|
213
|
+
# t.tag "@some_other_tag", :record => :new_episodes
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
# @yield the cucumber tags configuration block
|
217
|
+
# @yieldparam t [VCR::CucumberTags] Cucumber tags config object
|
218
|
+
# @return [void]
|
219
|
+
# @see VCR::CucumberTags#tags
|
100
220
|
def cucumber_tags(&block)
|
101
221
|
main_object = eval('self', block.binding)
|
102
222
|
yield VCR::CucumberTags.new(main_object)
|
103
223
|
end
|
104
224
|
|
225
|
+
# @private
|
105
226
|
def record_http_interaction(interaction)
|
106
227
|
return unless cassette = current_cassette
|
107
228
|
return if VCR.request_ignorer.ignore?(interaction.request)
|
@@ -109,6 +230,14 @@ module VCR
|
|
109
230
|
cassette.record_http_interaction(interaction)
|
110
231
|
end
|
111
232
|
|
233
|
+
# Turns VCR off for the duration of a block.
|
234
|
+
#
|
235
|
+
# @param (see #turn_off!)
|
236
|
+
# @return [void]
|
237
|
+
# @raise (see #turn_off!)
|
238
|
+
# @see #turn_off!
|
239
|
+
# @see #turn_on!
|
240
|
+
# @see #turned_on?
|
112
241
|
def turned_off(options = {})
|
113
242
|
turn_off!(options)
|
114
243
|
|
@@ -119,6 +248,16 @@ module VCR
|
|
119
248
|
end
|
120
249
|
end
|
121
250
|
|
251
|
+
# Turns VCR off, so that it no longer handles every HTTP request.
|
252
|
+
#
|
253
|
+
# @param options [Hash] hash of options
|
254
|
+
# @option options :ignore_cassettes [Boolean] controls what happens when a cassette is
|
255
|
+
# inserted while VCR is turned off. If +true+ is passed, the cassette insertion
|
256
|
+
# will be ignored; otherwise a {VCR::Errors::TurnedOffError} will be raised.
|
257
|
+
#
|
258
|
+
# @return [void]
|
259
|
+
# @raise [VCR::Errors::CassetteInUseError] if there is currently a cassette in use
|
260
|
+
# @raise [ArgumentError] if you pass an invalid option
|
122
261
|
def turn_off!(options = {})
|
123
262
|
if VCR.current_cassette
|
124
263
|
raise CassetteInUseError.new("A VCR cassette is currently in use. You must eject it before you can turn VCR off.")
|
@@ -133,14 +272,26 @@ module VCR
|
|
133
272
|
@turned_off = true
|
134
273
|
end
|
135
274
|
|
275
|
+
# Turns on VCR, if it has previously been turned off.
|
276
|
+
# @return [void]
|
277
|
+
# @see #turn_off!
|
278
|
+
# @see #turned_off
|
279
|
+
# @see #turned_on?
|
136
280
|
def turn_on!
|
137
281
|
@turned_off = false
|
138
282
|
end
|
139
283
|
|
284
|
+
# @return whether or not VCR is turned on
|
285
|
+
# @note Normally VCR is _always_ turned on; it will only be off if you have
|
286
|
+
# explicitly turned it off.
|
287
|
+
# @see #turn_on!
|
288
|
+
# @see #turn_off!
|
289
|
+
# @see #turned_off
|
140
290
|
def turned_on?
|
141
291
|
!@turned_off
|
142
292
|
end
|
143
293
|
|
294
|
+
# @private
|
144
295
|
def ignore_cassettes?
|
145
296
|
@ignore_cassettes
|
146
297
|
end
|
data/lib/vcr/cassette.rb
CHANGED
@@ -6,17 +6,51 @@ require 'vcr/cassette/reader'
|
|
6
6
|
require 'vcr/cassette/serializers'
|
7
7
|
|
8
8
|
module VCR
|
9
|
+
# The media VCR uses to store HTTP interactions for later re-use.
|
9
10
|
class Cassette
|
11
|
+
include Logger
|
12
|
+
|
13
|
+
# The supported record modes.
|
14
|
+
#
|
15
|
+
# * :all -- Record every HTTP interactions; do not play any back.
|
16
|
+
# * :none -- Do not record any HTTP interactions; play them back.
|
17
|
+
# * :new_episodes -- Playback previously recorded HTTP interactions and record new ones.
|
18
|
+
# * :once -- Record the HTTP interactions if the cassette has not already been recorded;
|
19
|
+
# otherwise, playback the HTTP interactions.
|
10
20
|
VALID_RECORD_MODES = [:all, :none, :new_episodes, :once]
|
11
21
|
|
12
|
-
|
22
|
+
# @return [#to_s] The name of the cassette. Used to determine the cassette's file name.
|
23
|
+
# @see #file
|
24
|
+
attr_reader :name
|
13
25
|
|
26
|
+
# @return [Symbol] The record mode. Determines whether the cassette records HTTP interactions,
|
27
|
+
# plays them back, or does both.
|
28
|
+
attr_reader :record_mode
|
29
|
+
|
30
|
+
# @return [Array<Symbol, #call>] List of request matchers. Used to find a response from an
|
31
|
+
# existing HTTP interaction to play back.
|
32
|
+
attr_reader :match_requests_on
|
33
|
+
|
34
|
+
# @return [Boolean, Hash] The cassette's ERB option. The file will be treated as an
|
35
|
+
# ERB template if this has a truthy value. A hash, if provided, will be used as local
|
36
|
+
# variables for the ERB template.
|
37
|
+
attr_reader :erb
|
38
|
+
|
39
|
+
# @return [Integer, nil] How frequently (in seconds) the cassette should be re-recorded.
|
40
|
+
attr_reader :re_record_interval
|
41
|
+
|
42
|
+
# @return [Array<Symbol>] If set, {VCR::Configuration#before_record} and
|
43
|
+
# {VCR::Configuration#before_playback} hooks with a corresponding tag will apply.
|
44
|
+
attr_reader :tags
|
45
|
+
|
46
|
+
# @param (see VCR#insert_cassette)
|
47
|
+
# @see VCR#insert_cassette
|
14
48
|
def initialize(name, options = {})
|
15
49
|
options = VCR.configuration.default_cassette_options.merge(options)
|
16
50
|
invalid_options = options.keys - [
|
17
|
-
:record, :erb, :match_requests_on, :re_record_interval, :tag,
|
51
|
+
:record, :erb, :match_requests_on, :re_record_interval, :tag, :tags,
|
18
52
|
:update_content_length_header, :allow_playback_repeats, :exclusive,
|
19
|
-
:serialize_with
|
53
|
+
:serialize_with, :preserve_exact_body_bytes
|
20
54
|
]
|
21
55
|
|
22
56
|
if invalid_options.size > 0
|
@@ -28,8 +62,9 @@ module VCR
|
|
28
62
|
@erb = options[:erb]
|
29
63
|
@match_requests_on = options[:match_requests_on]
|
30
64
|
@re_record_interval = options[:re_record_interval]
|
31
|
-
@
|
32
|
-
@update_content_length_header
|
65
|
+
@tags = Array(options.fetch(:tags) { options[:tag] })
|
66
|
+
@tags << :update_content_length_header if options[:update_content_length_header]
|
67
|
+
@tags << :preserve_exact_body_bytes if options[:preserve_exact_body_bytes]
|
33
68
|
@allow_playback_repeats = options[:allow_playback_repeats]
|
34
69
|
@exclusive = options[:exclusive]
|
35
70
|
@serializer = VCR.cassette_serializers[options[:serialize_with]]
|
@@ -37,55 +72,46 @@ module VCR
|
|
37
72
|
@parent_list = @exclusive ? HTTPInteractionList::NullList : VCR.http_interactions
|
38
73
|
|
39
74
|
raise_error_unless_valid_record_mode
|
75
|
+
|
76
|
+
log "Initialized with options: #{options.inspect}"
|
40
77
|
end
|
41
78
|
|
79
|
+
# Ejects the current cassette. The cassette will no longer be used.
|
80
|
+
# In addition, any newly recorded HTTP interactions will be written to
|
81
|
+
# disk.
|
42
82
|
def eject
|
43
83
|
write_recorded_interactions_to_disk
|
44
84
|
end
|
45
85
|
|
46
|
-
|
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
|
-
|
86
|
+
# @private
|
64
87
|
def http_interactions
|
65
88
|
@http_interactions ||= HTTPInteractionList.new \
|
66
89
|
should_stub_requests? ? previously_recorded_interactions : [],
|
67
90
|
match_requests_on,
|
68
91
|
@allow_playback_repeats,
|
69
|
-
@parent_list
|
92
|
+
@parent_list,
|
93
|
+
log_prefix
|
70
94
|
end
|
71
95
|
|
96
|
+
# @private
|
72
97
|
def record_http_interaction(interaction)
|
98
|
+
log "Recorded HTTP interaction #{request_summary(interaction.request)} => #{response_summary(interaction.response)}"
|
73
99
|
new_recorded_interactions << interaction
|
74
100
|
end
|
75
101
|
|
102
|
+
# @private
|
76
103
|
def new_recorded_interactions
|
77
104
|
@new_recorded_interactions ||= []
|
78
105
|
end
|
79
106
|
|
107
|
+
# @return [String] The file for this cassette.
|
108
|
+
# @note VCR will take care of sanitizing the cassette name to make it a valid file name.
|
80
109
|
def file
|
81
110
|
return nil unless VCR.configuration.cassette_library_dir
|
82
111
|
File.join(VCR.configuration.cassette_library_dir, "#{sanitized_name}.#{@serializer.file_extension}")
|
83
112
|
end
|
84
113
|
|
85
|
-
|
86
|
-
@update_content_length_header
|
87
|
-
end
|
88
|
-
|
114
|
+
# @return [Boolean] Whether or not the cassette is recording.
|
89
115
|
def recording?
|
90
116
|
case record_mode
|
91
117
|
when :none; false
|
@@ -94,6 +120,7 @@ module VCR
|
|
94
120
|
end
|
95
121
|
end
|
96
122
|
|
123
|
+
# @return [Hash] The hash that will be serialized when the cassette is written to disk.
|
97
124
|
def serializable_hash
|
98
125
|
{
|
99
126
|
"http_interactions" => interactions_to_record.map(&:to_hash),
|
@@ -103,6 +130,20 @@ module VCR
|
|
103
130
|
|
104
131
|
private
|
105
132
|
|
133
|
+
def previously_recorded_interactions
|
134
|
+
@previously_recorded_interactions ||= if file && File.size?(file)
|
135
|
+
deserialized_hash['http_interactions'].map { |h| HTTPInteraction.from_hash(h) }.tap do |interactions|
|
136
|
+
invoke_hook(:before_playback, interactions)
|
137
|
+
|
138
|
+
interactions.reject! do |i|
|
139
|
+
i.request.uri.is_a?(String) && VCR.request_ignorer.ignore?(i.request)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
else
|
143
|
+
[]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
106
147
|
def sanitized_name
|
107
148
|
name.to_s.gsub(/[^\w\-\/]+/, '_')
|
108
149
|
end
|
@@ -170,8 +211,9 @@ module VCR
|
|
170
211
|
|
171
212
|
def invoke_hook(type, interactions)
|
172
213
|
interactions.delete_if do |i|
|
173
|
-
|
174
|
-
|
214
|
+
i.hook_aware.tap do |hw|
|
215
|
+
VCR.configuration.invoke_hook(type, hw, self)
|
216
|
+
end.ignored?
|
175
217
|
end
|
176
218
|
end
|
177
219
|
|
@@ -187,5 +229,13 @@ module VCR
|
|
187
229
|
end
|
188
230
|
end
|
189
231
|
end
|
232
|
+
|
233
|
+
def log_prefix
|
234
|
+
@log_prefix ||= "[Cassette: '#{name}'] "
|
235
|
+
end
|
236
|
+
|
237
|
+
def request_summary(request)
|
238
|
+
super(request, match_requests_on)
|
239
|
+
end
|
190
240
|
end
|
191
241
|
end
|