vcr 2.0.0.rc1 → 2.0.0.rc2
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 +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
|