mustwin-vcr 2.9.3
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.
- checksums.yaml +7 -0
- data/features/about_these_examples.md +18 -0
- data/features/cassettes/allow_unused_http_interactions.feature +100 -0
- data/features/cassettes/automatic_re_recording.feature +72 -0
- data/features/cassettes/decompress.feature +74 -0
- data/features/cassettes/dynamic_erb.feature +100 -0
- data/features/cassettes/exclusive.feature +126 -0
- data/features/cassettes/format.feature +323 -0
- data/features/cassettes/freezing_time.feature +68 -0
- data/features/cassettes/naming.feature +28 -0
- data/features/cassettes/no_cassette.feature +152 -0
- data/features/cassettes/update_content_length_header.feature +112 -0
- data/features/configuration/allow_http_connections_when_no_cassette.feature +55 -0
- data/features/configuration/cassette_library_dir.feature +31 -0
- data/features/configuration/debug_logging.feature +59 -0
- data/features/configuration/default_cassette_options.feature +100 -0
- data/features/configuration/filter_sensitive_data.feature +153 -0
- data/features/configuration/hook_into.feature +172 -0
- data/features/configuration/ignore_request.feature +192 -0
- data/features/configuration/preserve_exact_body_bytes.feature +108 -0
- data/features/configuration/query_parser.feature +84 -0
- data/features/configuration/uri_parser.feature +89 -0
- data/features/getting_started.md +82 -0
- data/features/hooks/after_http_request.feature +58 -0
- data/features/hooks/around_http_request.feature +57 -0
- data/features/hooks/before_http_request.feature +63 -0
- data/features/hooks/before_playback.feature +184 -0
- data/features/hooks/before_record.feature +172 -0
- data/features/http_libraries/em_http_request.feature +250 -0
- data/features/http_libraries/net_http.feature +179 -0
- data/features/middleware/faraday.feature +56 -0
- data/features/middleware/rack.feature +92 -0
- data/features/record_modes/all.feature +82 -0
- data/features/record_modes/new_episodes.feature +79 -0
- data/features/record_modes/none.feature +72 -0
- data/features/record_modes/once.feature +95 -0
- data/features/request_matching/README.md +30 -0
- data/features/request_matching/body.feature +91 -0
- data/features/request_matching/body_as_json.feature +90 -0
- data/features/request_matching/custom_matcher.feature +135 -0
- data/features/request_matching/headers.feature +85 -0
- data/features/request_matching/host.feature +95 -0
- data/features/request_matching/identical_request_sequence.feature +89 -0
- data/features/request_matching/method.feature +96 -0
- data/features/request_matching/path.feature +96 -0
- data/features/request_matching/playback_repeats.feature +98 -0
- data/features/request_matching/query.feature +97 -0
- data/features/request_matching/uri.feature +94 -0
- data/features/request_matching/uri_without_param.feature +101 -0
- data/features/step_definitions/cli_steps.rb +193 -0
- data/features/support/env.rb +44 -0
- data/features/support/http_lib_filters.rb +53 -0
- data/features/test_frameworks/cucumber.feature +211 -0
- data/features/test_frameworks/rspec_macro.feature +81 -0
- data/features/test_frameworks/rspec_metadata.feature +150 -0
- data/features/test_frameworks/test_unit.feature +49 -0
- data/lib/vcr.rb +347 -0
- data/lib/vcr/cassette.rb +291 -0
- data/lib/vcr/cassette/erb_renderer.rb +55 -0
- data/lib/vcr/cassette/http_interaction_list.rb +108 -0
- data/lib/vcr/cassette/migrator.rb +118 -0
- data/lib/vcr/cassette/persisters.rb +42 -0
- data/lib/vcr/cassette/persisters/file_system.rb +64 -0
- data/lib/vcr/cassette/serializers.rb +57 -0
- data/lib/vcr/cassette/serializers/json.rb +48 -0
- data/lib/vcr/cassette/serializers/psych.rb +48 -0
- data/lib/vcr/cassette/serializers/syck.rb +61 -0
- data/lib/vcr/cassette/serializers/yaml.rb +50 -0
- data/lib/vcr/configuration.rb +555 -0
- data/lib/vcr/deprecations.rb +109 -0
- data/lib/vcr/errors.rb +266 -0
- data/lib/vcr/extensions/net_http_response.rb +36 -0
- data/lib/vcr/library_hooks.rb +18 -0
- data/lib/vcr/library_hooks/excon.rb +27 -0
- data/lib/vcr/library_hooks/fakeweb.rb +196 -0
- data/lib/vcr/library_hooks/faraday.rb +51 -0
- data/lib/vcr/library_hooks/typhoeus.rb +120 -0
- data/lib/vcr/library_hooks/typhoeus_0.4.rb +103 -0
- data/lib/vcr/library_hooks/webmock.rb +164 -0
- data/lib/vcr/middleware/excon.rb +221 -0
- data/lib/vcr/middleware/excon/legacy_methods.rb +33 -0
- data/lib/vcr/middleware/faraday.rb +118 -0
- data/lib/vcr/middleware/rack.rb +79 -0
- data/lib/vcr/request_handler.rb +114 -0
- data/lib/vcr/request_ignorer.rb +43 -0
- data/lib/vcr/request_matcher_registry.rb +149 -0
- data/lib/vcr/structs.rb +578 -0
- data/lib/vcr/tasks/vcr.rake +9 -0
- data/lib/vcr/test_frameworks/cucumber.rb +64 -0
- data/lib/vcr/test_frameworks/rspec.rb +47 -0
- data/lib/vcr/util/hooks.rb +61 -0
- data/lib/vcr/util/internet_connection.rb +43 -0
- data/lib/vcr/util/logger.rb +59 -0
- data/lib/vcr/util/variable_args_block_caller.rb +13 -0
- data/lib/vcr/util/version_checker.rb +48 -0
- data/lib/vcr/version.rb +34 -0
- data/spec/acceptance/threading_spec.rb +34 -0
- data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
- data/spec/fixtures/cassette_spec/empty.yml +0 -0
- data/spec/fixtures/cassette_spec/example.yml +111 -0
- data/spec/fixtures/cassette_spec/with_localhost_requests.yml +111 -0
- data/spec/fixtures/fake_example_responses.yml +110 -0
- data/spec/fixtures/match_requests_on.yml +187 -0
- data/spec/lib/vcr/cassette/erb_renderer_spec.rb +53 -0
- data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +295 -0
- data/spec/lib/vcr/cassette/migrator_spec.rb +195 -0
- data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +69 -0
- data/spec/lib/vcr/cassette/persisters_spec.rb +39 -0
- data/spec/lib/vcr/cassette/serializers_spec.rb +176 -0
- data/spec/lib/vcr/cassette_spec.rb +618 -0
- data/spec/lib/vcr/configuration_spec.rb +326 -0
- data/spec/lib/vcr/deprecations_spec.rb +85 -0
- data/spec/lib/vcr/errors_spec.rb +162 -0
- data/spec/lib/vcr/extensions/net_http_response_spec.rb +86 -0
- data/spec/lib/vcr/library_hooks/excon_spec.rb +104 -0
- data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +169 -0
- data/spec/lib/vcr/library_hooks/faraday_spec.rb +68 -0
- data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +36 -0
- data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +162 -0
- data/spec/lib/vcr/library_hooks/webmock_spec.rb +118 -0
- data/spec/lib/vcr/library_hooks_spec.rb +51 -0
- data/spec/lib/vcr/middleware/faraday_spec.rb +182 -0
- data/spec/lib/vcr/middleware/rack_spec.rb +115 -0
- data/spec/lib/vcr/request_ignorer_spec.rb +70 -0
- data/spec/lib/vcr/request_matcher_registry_spec.rb +345 -0
- data/spec/lib/vcr/structs_spec.rb +732 -0
- data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +107 -0
- data/spec/lib/vcr/test_frameworks/rspec_spec.rb +83 -0
- data/spec/lib/vcr/util/hooks_spec.rb +158 -0
- data/spec/lib/vcr/util/internet_connection_spec.rb +37 -0
- data/spec/lib/vcr/util/version_checker_spec.rb +31 -0
- data/spec/lib/vcr/version_spec.rb +27 -0
- data/spec/lib/vcr_spec.rb +349 -0
- data/spec/monkey_patches.rb +182 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/support/configuration_stubbing.rb +8 -0
- data/spec/support/cucumber_helpers.rb +35 -0
- data/spec/support/fixnum_extension.rb +10 -0
- data/spec/support/http_library_adapters.rb +289 -0
- data/spec/support/limited_uri.rb +21 -0
- data/spec/support/ruby_interpreter.rb +7 -0
- data/spec/support/shared_example_groups/excon.rb +63 -0
- data/spec/support/shared_example_groups/hook_into_http_library.rb +594 -0
- data/spec/support/shared_example_groups/request_hooks.rb +59 -0
- data/spec/support/sinatra_app.rb +86 -0
- data/spec/support/vcr_localhost_server.rb +76 -0
- data/spec/support/vcr_stub_helpers.rb +17 -0
- metadata +677 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
|
|
3
|
+
module VCR
|
|
4
|
+
class Cassette
|
|
5
|
+
class Serializers
|
|
6
|
+
# The JSON serializer. Uses `MultiJson` under the covers.
|
|
7
|
+
#
|
|
8
|
+
# @see Psych
|
|
9
|
+
# @see Syck
|
|
10
|
+
# @see YAML
|
|
11
|
+
module JSON
|
|
12
|
+
extend self
|
|
13
|
+
extend EncodingErrorHandling
|
|
14
|
+
|
|
15
|
+
# @private
|
|
16
|
+
ENCODING_ERRORS = [MultiJson::DecodeError, ArgumentError]
|
|
17
|
+
ENCODING_ERRORS << EncodingError if defined?(EncodingError)
|
|
18
|
+
|
|
19
|
+
# The file extension to use for this serializer.
|
|
20
|
+
#
|
|
21
|
+
# @return [String] "json"
|
|
22
|
+
def file_extension
|
|
23
|
+
"json"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Serializes the given hash using `MultiJson`.
|
|
27
|
+
#
|
|
28
|
+
# @param [Hash] hash the object to serialize
|
|
29
|
+
# @return [String] the JSON string
|
|
30
|
+
def serialize(hash)
|
|
31
|
+
handle_encoding_errors do
|
|
32
|
+
MultiJson.encode(hash)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Deserializes the given string using `MultiJson`.
|
|
37
|
+
#
|
|
38
|
+
# @param [String] string the JSON string
|
|
39
|
+
# @return [Hash] the deserialized object
|
|
40
|
+
def deserialize(string)
|
|
41
|
+
handle_encoding_errors do
|
|
42
|
+
MultiJson.decode(string)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'psych'
|
|
2
|
+
|
|
3
|
+
module VCR
|
|
4
|
+
class Cassette
|
|
5
|
+
class Serializers
|
|
6
|
+
# The Psych serializer. Psych is the new YAML engine in ruby 1.9.
|
|
7
|
+
#
|
|
8
|
+
# @see JSON
|
|
9
|
+
# @see Syck
|
|
10
|
+
# @see YAML
|
|
11
|
+
module Psych
|
|
12
|
+
extend self
|
|
13
|
+
extend EncodingErrorHandling
|
|
14
|
+
|
|
15
|
+
# @private
|
|
16
|
+
ENCODING_ERRORS = [ArgumentError]
|
|
17
|
+
|
|
18
|
+
# The file extension to use for this serializer.
|
|
19
|
+
#
|
|
20
|
+
# @return [String] "yml"
|
|
21
|
+
def file_extension
|
|
22
|
+
"yml"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Serializes the given hash using Psych.
|
|
26
|
+
#
|
|
27
|
+
# @param [Hash] hash the object to serialize
|
|
28
|
+
# @return [String] the YAML string
|
|
29
|
+
def serialize(hash)
|
|
30
|
+
handle_encoding_errors do
|
|
31
|
+
::Psych.dump(hash)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Deserializes the given string using Psych.
|
|
36
|
+
#
|
|
37
|
+
# @param [String] string the YAML string
|
|
38
|
+
# @return [Hash] the deserialized object
|
|
39
|
+
def deserialize(string)
|
|
40
|
+
handle_encoding_errors do
|
|
41
|
+
::Psych.load(string)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module VCR
|
|
4
|
+
class Cassette
|
|
5
|
+
class Serializers
|
|
6
|
+
# The Syck serializer. Syck is the legacy YAML engine in ruby 1.8 and 1.9.
|
|
7
|
+
#
|
|
8
|
+
# @see JSON
|
|
9
|
+
# @see Psych
|
|
10
|
+
# @see YAML
|
|
11
|
+
module Syck
|
|
12
|
+
extend self
|
|
13
|
+
extend EncodingErrorHandling
|
|
14
|
+
|
|
15
|
+
# @private
|
|
16
|
+
ENCODING_ERRORS = [ArgumentError]
|
|
17
|
+
|
|
18
|
+
# The file extension to use for this serializer.
|
|
19
|
+
#
|
|
20
|
+
# @return [String] "yml"
|
|
21
|
+
def file_extension
|
|
22
|
+
"yml"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Serializes the given hash using Syck.
|
|
26
|
+
#
|
|
27
|
+
# @param [Hash] hash the object to serialize
|
|
28
|
+
# @return [String] the YAML string
|
|
29
|
+
def serialize(hash)
|
|
30
|
+
handle_encoding_errors do
|
|
31
|
+
using_syck { ::YAML.dump(hash) }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Deserializes the given string using Syck.
|
|
36
|
+
#
|
|
37
|
+
# @param [String] string the YAML string
|
|
38
|
+
# @return [Hash] the deserialized object
|
|
39
|
+
def deserialize(string)
|
|
40
|
+
handle_encoding_errors do
|
|
41
|
+
using_syck { ::YAML.load(string) }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def using_syck
|
|
48
|
+
return yield unless defined?(::YAML::ENGINE)
|
|
49
|
+
original_engine = ::YAML::ENGINE.yamler
|
|
50
|
+
::YAML::ENGINE.yamler = 'syck'
|
|
51
|
+
|
|
52
|
+
begin
|
|
53
|
+
yield
|
|
54
|
+
ensure
|
|
55
|
+
::YAML::ENGINE.yamler = original_engine
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module VCR
|
|
4
|
+
class Cassette
|
|
5
|
+
class Serializers
|
|
6
|
+
# The YAML serializer. This will use either Psych or Syck, which ever your
|
|
7
|
+
# ruby interpreter defaults to. You can also force VCR to use Psych or Syck by
|
|
8
|
+
# using one of those serializers.
|
|
9
|
+
#
|
|
10
|
+
# @see JSON
|
|
11
|
+
# @see Psych
|
|
12
|
+
# @see Syck
|
|
13
|
+
module YAML
|
|
14
|
+
extend self
|
|
15
|
+
extend EncodingErrorHandling
|
|
16
|
+
|
|
17
|
+
# @private
|
|
18
|
+
ENCODING_ERRORS = [ArgumentError]
|
|
19
|
+
|
|
20
|
+
# The file extension to use for this serializer.
|
|
21
|
+
#
|
|
22
|
+
# @return [String] "yml"
|
|
23
|
+
def file_extension
|
|
24
|
+
"yml"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Serializes the given hash using YAML.
|
|
28
|
+
#
|
|
29
|
+
# @param [Hash] hash the object to serialize
|
|
30
|
+
# @return [String] the YAML string
|
|
31
|
+
def serialize(hash)
|
|
32
|
+
handle_encoding_errors do
|
|
33
|
+
::YAML.dump(hash)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Deserializes the given string using YAML.
|
|
38
|
+
#
|
|
39
|
+
# @param [String] string the YAML string
|
|
40
|
+
# @return [Hash] the deserialized object
|
|
41
|
+
def deserialize(string)
|
|
42
|
+
handle_encoding_errors do
|
|
43
|
+
::YAML.load(string)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
require 'vcr/util/hooks'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'cgi'
|
|
4
|
+
|
|
5
|
+
module VCR
|
|
6
|
+
# Stores the VCR configuration.
|
|
7
|
+
class Configuration
|
|
8
|
+
include Hooks
|
|
9
|
+
include VariableArgsBlockCaller
|
|
10
|
+
include Logger::Mixin
|
|
11
|
+
|
|
12
|
+
# Gets the directory to read cassettes from and write cassettes to.
|
|
13
|
+
#
|
|
14
|
+
# @return [String] the directory to read cassettes from and write cassettes to
|
|
15
|
+
def cassette_library_dir
|
|
16
|
+
VCR.cassette_persisters[:file_system].storage_location
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Sets the directory to read cassettes from and writes cassettes to.
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# VCR.configure do |c|
|
|
23
|
+
# c.cassette_library_dir = 'spec/cassettes'
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# @param dir [String] the directory to read cassettes from and write cassettes to
|
|
27
|
+
# @return [void]
|
|
28
|
+
# @note This is only necessary if you use the `:file_system`
|
|
29
|
+
# cassette persister (the default).
|
|
30
|
+
def cassette_library_dir=(dir)
|
|
31
|
+
VCR.cassette_persisters[:file_system].storage_location = dir
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Default options to apply to every cassette.
|
|
35
|
+
#
|
|
36
|
+
# @overload default_cassette_options
|
|
37
|
+
# @return [Hash] default options to apply to every cassette
|
|
38
|
+
# @overload default_cassette_options=(options)
|
|
39
|
+
# @param options [Hash] default options to apply to every cassette
|
|
40
|
+
# @return [void]
|
|
41
|
+
# @example
|
|
42
|
+
# VCR.configure do |c|
|
|
43
|
+
# c.default_cassette_options = { :record => :new_episodes }
|
|
44
|
+
# end
|
|
45
|
+
# @note {VCR#insert_cassette} for the list of valid options.
|
|
46
|
+
attr_reader :default_cassette_options
|
|
47
|
+
|
|
48
|
+
# Sets the default options that apply to every cassette.
|
|
49
|
+
def default_cassette_options=(overrides)
|
|
50
|
+
@default_cassette_options.merge!(overrides)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Configures which libraries VCR will hook into to intercept HTTP requests.
|
|
54
|
+
#
|
|
55
|
+
# @example
|
|
56
|
+
# VCR.configure do |c|
|
|
57
|
+
# c.hook_into :fakeweb, :typhoeus
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# @param hooks [Array<Symbol>] List of libraries. Valid values are
|
|
61
|
+
# `:fakeweb`, `:webmock`, `:typhoeus`, `:excon` and `:faraday`.
|
|
62
|
+
# @raise [ArgumentError] when given an unsupported library name.
|
|
63
|
+
# @raise [VCR::Errors::LibraryVersionTooLowError] when the version
|
|
64
|
+
# of a library you are using is too low for VCR to support.
|
|
65
|
+
# @note `:fakeweb` and `:webmock` cannot both be used since they both monkey patch
|
|
66
|
+
# `Net::HTTP`. Otherwise, you can use any combination of these.
|
|
67
|
+
def hook_into(*hooks)
|
|
68
|
+
hooks.each { |a| load_library_hook(a) }
|
|
69
|
+
invoke_hook(:after_library_hooks_loaded)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Specifies host(s) that VCR should ignore.
|
|
73
|
+
#
|
|
74
|
+
# @param hosts [Array<String>] List of hosts to ignore
|
|
75
|
+
# @see #ignore_localhost=
|
|
76
|
+
# @see #ignore_request
|
|
77
|
+
def ignore_hosts(*hosts)
|
|
78
|
+
VCR.request_ignorer.ignore_hosts(*hosts)
|
|
79
|
+
end
|
|
80
|
+
alias ignore_host ignore_hosts
|
|
81
|
+
|
|
82
|
+
# Sets whether or not VCR should ignore localhost requests.
|
|
83
|
+
#
|
|
84
|
+
# @param value [Boolean] the value to set
|
|
85
|
+
# @see #ignore_hosts
|
|
86
|
+
# @see #ignore_request
|
|
87
|
+
def ignore_localhost=(value)
|
|
88
|
+
VCR.request_ignorer.ignore_localhost = value
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Defines what requests to ignore using a block.
|
|
92
|
+
#
|
|
93
|
+
# @example
|
|
94
|
+
# VCR.configure do |c|
|
|
95
|
+
# c.ignore_request do |request|
|
|
96
|
+
# uri = URI(request.uri)
|
|
97
|
+
# # ignore only localhost requests to port 7500
|
|
98
|
+
# uri.host == 'localhost' && uri.port == 7500
|
|
99
|
+
# end
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# @yield the callback
|
|
103
|
+
# @yieldparam request [VCR::Request] the HTTP request
|
|
104
|
+
# @yieldreturn [Boolean] whether or not to ignore the request
|
|
105
|
+
def ignore_request(&block)
|
|
106
|
+
VCR.request_ignorer.ignore_request(&block)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Determines how VCR treats HTTP requests that are made when
|
|
110
|
+
# no VCR cassette is in use. When set to `true`, requests made
|
|
111
|
+
# when there is no VCR cassette in use will be allowed. When set
|
|
112
|
+
# to `false` (the default), an {VCR::Errors::UnhandledHTTPRequestError}
|
|
113
|
+
# will be raised for any HTTP request made when there is no
|
|
114
|
+
# cassette in use.
|
|
115
|
+
#
|
|
116
|
+
# @overload allow_http_connections_when_no_cassette?
|
|
117
|
+
# @return [Boolean] whether or not HTTP connections are allowed
|
|
118
|
+
# when there is no cassette.
|
|
119
|
+
# @overload allow_http_connections_when_no_cassette=
|
|
120
|
+
# @param value [Boolean] sets whether or not to allow HTTP
|
|
121
|
+
# connections when there is no cassette.
|
|
122
|
+
attr_writer :allow_http_connections_when_no_cassette
|
|
123
|
+
# @private (documented above)
|
|
124
|
+
def allow_http_connections_when_no_cassette?
|
|
125
|
+
!!@allow_http_connections_when_no_cassette
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Sets a parser for VCR to use when parsing query strings for request
|
|
129
|
+
# comparisons. The new parser must implement a method `call` that returns
|
|
130
|
+
# an object which is both equalivant and consistent when given an HTTP
|
|
131
|
+
# query string of possibly differing value ordering.
|
|
132
|
+
#
|
|
133
|
+
# * `#== # => Boolean`
|
|
134
|
+
#
|
|
135
|
+
# The `#==` method must return true if both objects represent the
|
|
136
|
+
# same query string.
|
|
137
|
+
#
|
|
138
|
+
# This defaults to `CGI.parse` from the ruby standard library.
|
|
139
|
+
#
|
|
140
|
+
# @overload query_parser
|
|
141
|
+
# @return [#call] the current query string parser object
|
|
142
|
+
# @overload query_parser=
|
|
143
|
+
# @param value [#call] sets the query_parser
|
|
144
|
+
attr_accessor :query_parser
|
|
145
|
+
|
|
146
|
+
# Sets a parser for VCR to use when parsing URIs. The new parser
|
|
147
|
+
# must implement a method `parse` that returns an instance of the
|
|
148
|
+
# URI object. This URI object must implement the following
|
|
149
|
+
# interface:
|
|
150
|
+
#
|
|
151
|
+
# * `scheme # => String`
|
|
152
|
+
# * `host # => String`
|
|
153
|
+
# * `port # => Fixnum`
|
|
154
|
+
# * `path # => String`
|
|
155
|
+
# * `query # => String`
|
|
156
|
+
# * `#port=`
|
|
157
|
+
# * `#query=`
|
|
158
|
+
# * `#to_s # => String`
|
|
159
|
+
# * `#== # => Boolean`
|
|
160
|
+
#
|
|
161
|
+
# The `#==` method must return true if both URI objects represent the
|
|
162
|
+
# same URI.
|
|
163
|
+
#
|
|
164
|
+
# This defaults to `URI` from the ruby standard library.
|
|
165
|
+
#
|
|
166
|
+
# @overload uri_parser
|
|
167
|
+
# @return [#parse] the current URI parser object
|
|
168
|
+
# @overload uri_parser=
|
|
169
|
+
# @param value [#parse] sets the uri_parser
|
|
170
|
+
attr_accessor :uri_parser
|
|
171
|
+
|
|
172
|
+
# Registers a request matcher for later use.
|
|
173
|
+
#
|
|
174
|
+
# @example
|
|
175
|
+
# VCR.configure do |c|
|
|
176
|
+
# c.register_request_matcher :port do |request_1, request_2|
|
|
177
|
+
# URI(request_1.uri).port == URI(request_2.uri).port
|
|
178
|
+
# end
|
|
179
|
+
# end
|
|
180
|
+
#
|
|
181
|
+
# VCR.use_cassette("my_cassette", :match_requests_on => [:method, :host, :port]) do
|
|
182
|
+
# # ...
|
|
183
|
+
# end
|
|
184
|
+
#
|
|
185
|
+
# @param name [Symbol] the name of the request matcher
|
|
186
|
+
# @yield the request matcher
|
|
187
|
+
# @yieldparam request_1 [VCR::Request] One request
|
|
188
|
+
# @yieldparam request_2 [VCR::Request] The other request
|
|
189
|
+
# @yieldreturn [Boolean] whether or not these two requests should be considered
|
|
190
|
+
# equivalent
|
|
191
|
+
def register_request_matcher(name, &block)
|
|
192
|
+
VCR.request_matchers.register(name, &block)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Sets up a {#before_record} and a {#before_playback} hook that will
|
|
196
|
+
# insert a placeholder string in the cassette in place of another string.
|
|
197
|
+
# You can use this as a generic way to interpolate a variable into the
|
|
198
|
+
# cassette for a unique string. It's particularly useful for unique
|
|
199
|
+
# sensitive strings like API keys and passwords.
|
|
200
|
+
#
|
|
201
|
+
# @example
|
|
202
|
+
# VCR.configure do |c|
|
|
203
|
+
# # Put "<GITHUB_API_KEY>" in place of the actual API key in
|
|
204
|
+
# # our cassettes so we don't have to commit to source control.
|
|
205
|
+
# c.filter_sensitive_data('<GITHUB_API_KEY>') { GithubClient.api_key }
|
|
206
|
+
#
|
|
207
|
+
# # Put a "<USER_ID>" placeholder variable in our cassettes tagged with
|
|
208
|
+
# # :user_cassette since it can be different for different test runs.
|
|
209
|
+
# c.define_cassette_placeholder('<USER_ID>', :user_cassette) { User.last.id }
|
|
210
|
+
# end
|
|
211
|
+
#
|
|
212
|
+
# @param placeholder [String] The placeholder string.
|
|
213
|
+
# @param tag [Symbol] Set this to apply this only to cassettes
|
|
214
|
+
# with a matching tag; otherwise it will apply to every cassette.
|
|
215
|
+
# @yield block that determines what string to replace
|
|
216
|
+
# @yieldparam interaction [(optional) VCR::HTTPInteraction::HookAware] the HTTP interaction
|
|
217
|
+
# @yieldreturn the string to replace
|
|
218
|
+
def define_cassette_placeholder(placeholder, tag = nil, &block)
|
|
219
|
+
before_record(tag) do |interaction|
|
|
220
|
+
orig_text = call_block(block, interaction)
|
|
221
|
+
log "before_record: replacing #{orig_text.inspect} with #{placeholder.inspect}"
|
|
222
|
+
interaction.filter!(orig_text, placeholder)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
before_playback(tag) do |interaction|
|
|
226
|
+
orig_text = call_block(block, interaction)
|
|
227
|
+
log "before_playback: replacing #{placeholder.inspect} with #{orig_text.inspect}"
|
|
228
|
+
interaction.filter!(placeholder, orig_text)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
alias filter_sensitive_data define_cassette_placeholder
|
|
232
|
+
|
|
233
|
+
# Gets the registry of cassette serializers. Use it to register a custom serializer.
|
|
234
|
+
#
|
|
235
|
+
# @example
|
|
236
|
+
# VCR.configure do |c|
|
|
237
|
+
# c.cassette_serializers[:my_custom_serializer] = my_custom_serializer
|
|
238
|
+
# end
|
|
239
|
+
#
|
|
240
|
+
# @return [VCR::Cassette::Serializers] the cassette serializer registry object.
|
|
241
|
+
# @note Custom serializers must implement the following interface:
|
|
242
|
+
#
|
|
243
|
+
# * `file_extension # => String`
|
|
244
|
+
# * `serialize(Hash) # => String`
|
|
245
|
+
# * `deserialize(String) # => Hash`
|
|
246
|
+
def cassette_serializers
|
|
247
|
+
VCR.cassette_serializers
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Gets the registry of cassette persisters. Use it to register a custom persister.
|
|
251
|
+
#
|
|
252
|
+
# @example
|
|
253
|
+
# VCR.configure do |c|
|
|
254
|
+
# c.cassette_persisters[:my_custom_persister] = my_custom_persister
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# @return [VCR::Cassette::Persisters] the cassette persister registry object.
|
|
258
|
+
# @note Custom persisters must implement the following interface:
|
|
259
|
+
#
|
|
260
|
+
# * `persister[storage_key]` # returns previously persisted content
|
|
261
|
+
# * `persister[storage_key] = content` # persists given content
|
|
262
|
+
def cassette_persisters
|
|
263
|
+
VCR.cassette_persisters
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
define_hook :before_record
|
|
267
|
+
# Adds a callback that will be called before the recorded HTTP interactions
|
|
268
|
+
# are serialized and written to disk.
|
|
269
|
+
#
|
|
270
|
+
# @example
|
|
271
|
+
# VCR.configure do |c|
|
|
272
|
+
# # Don't record transient 5xx errors
|
|
273
|
+
# c.before_record do |interaction|
|
|
274
|
+
# interaction.ignore! if interaction.response.status.code >= 500
|
|
275
|
+
# end
|
|
276
|
+
#
|
|
277
|
+
# # Modify the response body for cassettes tagged with :twilio
|
|
278
|
+
# c.before_record(:twilio) do |interaction|
|
|
279
|
+
# interaction.response.body.downcase!
|
|
280
|
+
# end
|
|
281
|
+
# end
|
|
282
|
+
#
|
|
283
|
+
# @param tag [(optional) Symbol] Used to apply this hook to only cassettes that match
|
|
284
|
+
# the given tag.
|
|
285
|
+
# @yield the callback
|
|
286
|
+
# @yieldparam interaction [VCR::HTTPInteraction::HookAware] The interaction that will be
|
|
287
|
+
# serialized and written to disk.
|
|
288
|
+
# @yieldparam cassette [(optional) VCR::Cassette] The current cassette.
|
|
289
|
+
# @see #before_playback
|
|
290
|
+
def before_record(tag = nil, &block)
|
|
291
|
+
super(tag_filter_from(tag), &block)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
define_hook :before_playback
|
|
295
|
+
# Adds a callback that will be called before a previously recorded
|
|
296
|
+
# HTTP interaction is loaded for playback.
|
|
297
|
+
#
|
|
298
|
+
# @example
|
|
299
|
+
# VCR.configure do |c|
|
|
300
|
+
# # Don't playback transient 5xx errors
|
|
301
|
+
# c.before_playback do |interaction|
|
|
302
|
+
# interaction.ignore! if interaction.response.status.code >= 500
|
|
303
|
+
# end
|
|
304
|
+
#
|
|
305
|
+
# # Change a response header for playback
|
|
306
|
+
# c.before_playback(:twilio) do |interaction|
|
|
307
|
+
# interaction.response.headers['X-Foo-Bar'] = 'Bazz'
|
|
308
|
+
# end
|
|
309
|
+
# end
|
|
310
|
+
#
|
|
311
|
+
# @param tag [(optional) Symbol] Used to apply this hook to only cassettes that match
|
|
312
|
+
# the given tag.
|
|
313
|
+
# @yield the callback
|
|
314
|
+
# @yieldparam interaction [VCR::HTTPInteraction::HookAware] The interaction that is being
|
|
315
|
+
# loaded.
|
|
316
|
+
# @yieldparam cassette [(optional) VCR::Cassette] The current cassette.
|
|
317
|
+
# @see #before_record
|
|
318
|
+
def before_playback(tag = nil, &block)
|
|
319
|
+
super(tag_filter_from(tag), &block)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Adds a callback that will be called with each HTTP request before it is made.
|
|
323
|
+
#
|
|
324
|
+
# @example
|
|
325
|
+
# VCR.configure do |c|
|
|
326
|
+
# c.before_http_request(:real?) do |request|
|
|
327
|
+
# puts "Request: #{request.method} #{request.uri}"
|
|
328
|
+
# end
|
|
329
|
+
# end
|
|
330
|
+
#
|
|
331
|
+
# @param filters [optional splat of #to_proc] one or more filters to apply.
|
|
332
|
+
# The objects provided will be converted to procs using `#to_proc`. If provided,
|
|
333
|
+
# the callback will only be invoked if these procs all return `true`.
|
|
334
|
+
# @yield the callback
|
|
335
|
+
# @yieldparam request [VCR::Request::Typed] the request that is being made
|
|
336
|
+
# @see #after_http_request
|
|
337
|
+
# @see #around_http_request
|
|
338
|
+
define_hook :before_http_request
|
|
339
|
+
|
|
340
|
+
define_hook :after_http_request, :prepend
|
|
341
|
+
# Adds a callback that will be called with each HTTP request after it is complete.
|
|
342
|
+
#
|
|
343
|
+
# @example
|
|
344
|
+
# VCR.configure do |c|
|
|
345
|
+
# c.after_http_request(:ignored?) do |request, response|
|
|
346
|
+
# puts "Request: #{request.method} #{request.uri}"
|
|
347
|
+
# puts "Response: #{response.status.code}"
|
|
348
|
+
# end
|
|
349
|
+
# end
|
|
350
|
+
#
|
|
351
|
+
# @param filters [optional splat of #to_proc] one or more filters to apply.
|
|
352
|
+
# The objects provided will be converted to procs using `#to_proc`. If provided,
|
|
353
|
+
# the callback will only be invoked if these procs all return `true`.
|
|
354
|
+
# @yield the callback
|
|
355
|
+
# @yieldparam request [VCR::Request::Typed] the request that is being made
|
|
356
|
+
# @yieldparam response [VCR::Response] the response from the request
|
|
357
|
+
# @see #before_http_request
|
|
358
|
+
# @see #around_http_request
|
|
359
|
+
def after_http_request(*filters)
|
|
360
|
+
super(*filters.map { |f| request_filter_from(f) })
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Adds a callback that will be executed around each HTTP request.
|
|
364
|
+
#
|
|
365
|
+
# @example
|
|
366
|
+
# VCR.configure do |c|
|
|
367
|
+
# c.around_http_request(lambda {|r| r.uri =~ /api.geocoder.com/}) do |request|
|
|
368
|
+
# # extract an address like "1700 E Pine St, Seattle, WA"
|
|
369
|
+
# # from a query like "address=1700+E+Pine+St%2C+Seattle%2C+WA"
|
|
370
|
+
# address = CGI.unescape(URI(request.uri).query.split('=').last)
|
|
371
|
+
# VCR.use_cassette("geocoding/#{address}", &request)
|
|
372
|
+
# end
|
|
373
|
+
# end
|
|
374
|
+
#
|
|
375
|
+
# @yield the callback
|
|
376
|
+
# @yieldparam request [VCR::Request::FiberAware] the request that is being made
|
|
377
|
+
# @raise [VCR::Errors::NotSupportedError] if the fiber library cannot be loaded.
|
|
378
|
+
# @param filters [optional splat of #to_proc] one or more filters to apply.
|
|
379
|
+
# The objects provided will be converted to procs using `#to_proc`. If provided,
|
|
380
|
+
# the callback will only be invoked if these procs all return `true`.
|
|
381
|
+
# @note This method can only be used on ruby interpreters that support
|
|
382
|
+
# fibers (i.e. 1.9+). On 1.8 you can use separate `before_http_request` and
|
|
383
|
+
# `after_http_request` hooks.
|
|
384
|
+
# @note You _must_ call `request.proceed` or pass the request as a proc on to a
|
|
385
|
+
# method that yields to a block (i.e. `some_method(&request)`).
|
|
386
|
+
# @see #before_http_request
|
|
387
|
+
# @see #after_http_request
|
|
388
|
+
def around_http_request(*filters, &block)
|
|
389
|
+
require 'fiber'
|
|
390
|
+
rescue LoadError
|
|
391
|
+
raise Errors::NotSupportedError.new \
|
|
392
|
+
"VCR::Configuration#around_http_request requires fibers, " +
|
|
393
|
+
"which are not available on your ruby intepreter."
|
|
394
|
+
else
|
|
395
|
+
fibers = {}
|
|
396
|
+
hook_allowed, hook_decaration = false, caller.first
|
|
397
|
+
before_http_request(*filters) do |request|
|
|
398
|
+
hook_allowed = true
|
|
399
|
+
fiber = start_new_fiber_for(request, block)
|
|
400
|
+
fibers[Thread.current] = fiber
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
after_http_request(lambda { hook_allowed }) do |request, response|
|
|
404
|
+
fiber = fibers.delete(Thread.current)
|
|
405
|
+
resume_fiber(fiber, response, hook_decaration)
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Configures RSpec to use a VCR cassette for any example
|
|
410
|
+
# tagged with `:vcr`.
|
|
411
|
+
def configure_rspec_metadata!
|
|
412
|
+
unless @rspec_metadata_configured
|
|
413
|
+
VCR::RSpec::Metadata.configure!
|
|
414
|
+
@rspec_metadata_configured = true
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# An object to log debug output to.
|
|
419
|
+
#
|
|
420
|
+
# @overload debug_logger
|
|
421
|
+
# @return [#puts] the logger
|
|
422
|
+
# @overload debug_logger=(logger)
|
|
423
|
+
# @param logger [#puts] the logger
|
|
424
|
+
# @return [void]
|
|
425
|
+
# @example
|
|
426
|
+
# VCR.configure do |c|
|
|
427
|
+
# c.debug_logger = $stderr
|
|
428
|
+
# end
|
|
429
|
+
# @example
|
|
430
|
+
# VCR.configure do |c|
|
|
431
|
+
# c.debug_logger = File.open('vcr.log', 'w')
|
|
432
|
+
# end
|
|
433
|
+
attr_reader :debug_logger
|
|
434
|
+
# @private (documented above)
|
|
435
|
+
def debug_logger=(value)
|
|
436
|
+
@debug_logger = value
|
|
437
|
+
|
|
438
|
+
if value
|
|
439
|
+
@logger = Logger.new(value)
|
|
440
|
+
else
|
|
441
|
+
@logger = Logger::Null
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# @private
|
|
446
|
+
# Logger object that provides logging APIs and helper methods.
|
|
447
|
+
attr_reader :logger
|
|
448
|
+
|
|
449
|
+
# Sets a callback that determines whether or not to base64 encode
|
|
450
|
+
# the bytes of a request or response body during serialization in
|
|
451
|
+
# order to preserve them exactly.
|
|
452
|
+
#
|
|
453
|
+
# @example
|
|
454
|
+
# VCR.configure do |c|
|
|
455
|
+
# c.preserve_exact_body_bytes do |http_message|
|
|
456
|
+
# http_message.body.encoding.name == 'ASCII-8BIT' ||
|
|
457
|
+
# !http_message.body.valid_encoding?
|
|
458
|
+
# end
|
|
459
|
+
# end
|
|
460
|
+
#
|
|
461
|
+
# @yield the callback
|
|
462
|
+
# @yieldparam http_message [#body, #headers] the `VCR::Request` or `VCR::Response` object being serialized
|
|
463
|
+
# @yieldparam cassette [VCR::Cassette] the cassette the http message belongs to
|
|
464
|
+
# @yieldreturn [Boolean] whether or not to preserve the exact bytes for the body of the given HTTP message
|
|
465
|
+
# @return [void]
|
|
466
|
+
# @see #preserve_exact_body_bytes_for?
|
|
467
|
+
# @note This is usually only necessary when the HTTP server returns a response
|
|
468
|
+
# with a non-standard encoding or with a body containing invalid bytes for the given
|
|
469
|
+
# encoding. Note that when you set this, and the block returns true, you sacrifice
|
|
470
|
+
# the human readability of the data in the cassette.
|
|
471
|
+
define_hook :preserve_exact_body_bytes
|
|
472
|
+
|
|
473
|
+
# @return [Boolean] whether or not the body of the given HTTP message should
|
|
474
|
+
# be base64 encoded during serialization in order to preserve the bytes exactly.
|
|
475
|
+
# @param http_message [#body, #headers] the `VCR::Request` or `VCR::Response` object being serialized
|
|
476
|
+
# @see #preserve_exact_body_bytes
|
|
477
|
+
def preserve_exact_body_bytes_for?(http_message)
|
|
478
|
+
invoke_hook(:preserve_exact_body_bytes, http_message, VCR.current_cassette).any?
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
private
|
|
482
|
+
|
|
483
|
+
def initialize
|
|
484
|
+
@allow_http_connections_when_no_cassette = nil
|
|
485
|
+
@rspec_metadata_configured = false
|
|
486
|
+
@default_cassette_options = {
|
|
487
|
+
:record => :once,
|
|
488
|
+
:match_requests_on => RequestMatcherRegistry::DEFAULT_MATCHERS,
|
|
489
|
+
:allow_unused_http_interactions => true,
|
|
490
|
+
:serialize_with => :yaml,
|
|
491
|
+
:persist_with => :file_system
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
self.uri_parser = URI
|
|
495
|
+
self.query_parser = CGI.method(:parse)
|
|
496
|
+
self.debug_logger = nil
|
|
497
|
+
|
|
498
|
+
register_built_in_hooks
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def load_library_hook(hook)
|
|
502
|
+
file = "vcr/library_hooks/#{hook}"
|
|
503
|
+
require file
|
|
504
|
+
rescue LoadError => e
|
|
505
|
+
raise e unless e.message.include?(file) # in case FakeWeb/WebMock/etc itself is not available
|
|
506
|
+
raise ArgumentError.new("#{hook.inspect} is not a supported VCR HTTP library hook.")
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def resume_fiber(fiber, response, hook_declaration)
|
|
510
|
+
fiber.resume(response)
|
|
511
|
+
rescue FiberError
|
|
512
|
+
raise Errors::AroundHTTPRequestHookError.new \
|
|
513
|
+
"Your around_http_request hook declared at #{hook_declaration}" +
|
|
514
|
+
" must call #proceed on the yielded request but did not."
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def start_new_fiber_for(request, block)
|
|
518
|
+
Fiber.new(&block).tap do |fiber|
|
|
519
|
+
fiber.resume(Request::FiberAware.new(request))
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def tag_filter_from(tag)
|
|
524
|
+
return lambda { true } unless tag
|
|
525
|
+
lambda { |_, cassette| cassette.tags.include?(tag) }
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def request_filter_from(object)
|
|
529
|
+
return object unless object.is_a?(Symbol)
|
|
530
|
+
lambda { |arg| arg.send(object) }
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def register_built_in_hooks
|
|
534
|
+
before_playback(:update_content_length_header) do |interaction|
|
|
535
|
+
interaction.response.update_content_length_header
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
before_record(:decode_compressed_response) do |interaction|
|
|
539
|
+
interaction.response.decompress if interaction.response.compressed?
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
preserve_exact_body_bytes do |http_message, cassette|
|
|
543
|
+
cassette && cassette.tags.include?(:preserve_exact_body_bytes)
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def log_prefix
|
|
548
|
+
"[VCR::Configuration] "
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# @private
|
|
552
|
+
define_hook :after_library_hooks_loaded
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|