mustwin-vcr 2.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/features/about_these_examples.md +18 -0
  3. data/features/cassettes/allow_unused_http_interactions.feature +100 -0
  4. data/features/cassettes/automatic_re_recording.feature +72 -0
  5. data/features/cassettes/decompress.feature +74 -0
  6. data/features/cassettes/dynamic_erb.feature +100 -0
  7. data/features/cassettes/exclusive.feature +126 -0
  8. data/features/cassettes/format.feature +323 -0
  9. data/features/cassettes/freezing_time.feature +68 -0
  10. data/features/cassettes/naming.feature +28 -0
  11. data/features/cassettes/no_cassette.feature +152 -0
  12. data/features/cassettes/update_content_length_header.feature +112 -0
  13. data/features/configuration/allow_http_connections_when_no_cassette.feature +55 -0
  14. data/features/configuration/cassette_library_dir.feature +31 -0
  15. data/features/configuration/debug_logging.feature +59 -0
  16. data/features/configuration/default_cassette_options.feature +100 -0
  17. data/features/configuration/filter_sensitive_data.feature +153 -0
  18. data/features/configuration/hook_into.feature +172 -0
  19. data/features/configuration/ignore_request.feature +192 -0
  20. data/features/configuration/preserve_exact_body_bytes.feature +108 -0
  21. data/features/configuration/query_parser.feature +84 -0
  22. data/features/configuration/uri_parser.feature +89 -0
  23. data/features/getting_started.md +82 -0
  24. data/features/hooks/after_http_request.feature +58 -0
  25. data/features/hooks/around_http_request.feature +57 -0
  26. data/features/hooks/before_http_request.feature +63 -0
  27. data/features/hooks/before_playback.feature +184 -0
  28. data/features/hooks/before_record.feature +172 -0
  29. data/features/http_libraries/em_http_request.feature +250 -0
  30. data/features/http_libraries/net_http.feature +179 -0
  31. data/features/middleware/faraday.feature +56 -0
  32. data/features/middleware/rack.feature +92 -0
  33. data/features/record_modes/all.feature +82 -0
  34. data/features/record_modes/new_episodes.feature +79 -0
  35. data/features/record_modes/none.feature +72 -0
  36. data/features/record_modes/once.feature +95 -0
  37. data/features/request_matching/README.md +30 -0
  38. data/features/request_matching/body.feature +91 -0
  39. data/features/request_matching/body_as_json.feature +90 -0
  40. data/features/request_matching/custom_matcher.feature +135 -0
  41. data/features/request_matching/headers.feature +85 -0
  42. data/features/request_matching/host.feature +95 -0
  43. data/features/request_matching/identical_request_sequence.feature +89 -0
  44. data/features/request_matching/method.feature +96 -0
  45. data/features/request_matching/path.feature +96 -0
  46. data/features/request_matching/playback_repeats.feature +98 -0
  47. data/features/request_matching/query.feature +97 -0
  48. data/features/request_matching/uri.feature +94 -0
  49. data/features/request_matching/uri_without_param.feature +101 -0
  50. data/features/step_definitions/cli_steps.rb +193 -0
  51. data/features/support/env.rb +44 -0
  52. data/features/support/http_lib_filters.rb +53 -0
  53. data/features/test_frameworks/cucumber.feature +211 -0
  54. data/features/test_frameworks/rspec_macro.feature +81 -0
  55. data/features/test_frameworks/rspec_metadata.feature +150 -0
  56. data/features/test_frameworks/test_unit.feature +49 -0
  57. data/lib/vcr.rb +347 -0
  58. data/lib/vcr/cassette.rb +291 -0
  59. data/lib/vcr/cassette/erb_renderer.rb +55 -0
  60. data/lib/vcr/cassette/http_interaction_list.rb +108 -0
  61. data/lib/vcr/cassette/migrator.rb +118 -0
  62. data/lib/vcr/cassette/persisters.rb +42 -0
  63. data/lib/vcr/cassette/persisters/file_system.rb +64 -0
  64. data/lib/vcr/cassette/serializers.rb +57 -0
  65. data/lib/vcr/cassette/serializers/json.rb +48 -0
  66. data/lib/vcr/cassette/serializers/psych.rb +48 -0
  67. data/lib/vcr/cassette/serializers/syck.rb +61 -0
  68. data/lib/vcr/cassette/serializers/yaml.rb +50 -0
  69. data/lib/vcr/configuration.rb +555 -0
  70. data/lib/vcr/deprecations.rb +109 -0
  71. data/lib/vcr/errors.rb +266 -0
  72. data/lib/vcr/extensions/net_http_response.rb +36 -0
  73. data/lib/vcr/library_hooks.rb +18 -0
  74. data/lib/vcr/library_hooks/excon.rb +27 -0
  75. data/lib/vcr/library_hooks/fakeweb.rb +196 -0
  76. data/lib/vcr/library_hooks/faraday.rb +51 -0
  77. data/lib/vcr/library_hooks/typhoeus.rb +120 -0
  78. data/lib/vcr/library_hooks/typhoeus_0.4.rb +103 -0
  79. data/lib/vcr/library_hooks/webmock.rb +164 -0
  80. data/lib/vcr/middleware/excon.rb +221 -0
  81. data/lib/vcr/middleware/excon/legacy_methods.rb +33 -0
  82. data/lib/vcr/middleware/faraday.rb +118 -0
  83. data/lib/vcr/middleware/rack.rb +79 -0
  84. data/lib/vcr/request_handler.rb +114 -0
  85. data/lib/vcr/request_ignorer.rb +43 -0
  86. data/lib/vcr/request_matcher_registry.rb +149 -0
  87. data/lib/vcr/structs.rb +578 -0
  88. data/lib/vcr/tasks/vcr.rake +9 -0
  89. data/lib/vcr/test_frameworks/cucumber.rb +64 -0
  90. data/lib/vcr/test_frameworks/rspec.rb +47 -0
  91. data/lib/vcr/util/hooks.rb +61 -0
  92. data/lib/vcr/util/internet_connection.rb +43 -0
  93. data/lib/vcr/util/logger.rb +59 -0
  94. data/lib/vcr/util/variable_args_block_caller.rb +13 -0
  95. data/lib/vcr/util/version_checker.rb +48 -0
  96. data/lib/vcr/version.rb +34 -0
  97. data/spec/acceptance/threading_spec.rb +34 -0
  98. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  99. data/spec/fixtures/cassette_spec/empty.yml +0 -0
  100. data/spec/fixtures/cassette_spec/example.yml +111 -0
  101. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +111 -0
  102. data/spec/fixtures/fake_example_responses.yml +110 -0
  103. data/spec/fixtures/match_requests_on.yml +187 -0
  104. data/spec/lib/vcr/cassette/erb_renderer_spec.rb +53 -0
  105. data/spec/lib/vcr/cassette/http_interaction_list_spec.rb +295 -0
  106. data/spec/lib/vcr/cassette/migrator_spec.rb +195 -0
  107. data/spec/lib/vcr/cassette/persisters/file_system_spec.rb +69 -0
  108. data/spec/lib/vcr/cassette/persisters_spec.rb +39 -0
  109. data/spec/lib/vcr/cassette/serializers_spec.rb +176 -0
  110. data/spec/lib/vcr/cassette_spec.rb +618 -0
  111. data/spec/lib/vcr/configuration_spec.rb +326 -0
  112. data/spec/lib/vcr/deprecations_spec.rb +85 -0
  113. data/spec/lib/vcr/errors_spec.rb +162 -0
  114. data/spec/lib/vcr/extensions/net_http_response_spec.rb +86 -0
  115. data/spec/lib/vcr/library_hooks/excon_spec.rb +104 -0
  116. data/spec/lib/vcr/library_hooks/fakeweb_spec.rb +169 -0
  117. data/spec/lib/vcr/library_hooks/faraday_spec.rb +68 -0
  118. data/spec/lib/vcr/library_hooks/typhoeus_0.4_spec.rb +36 -0
  119. data/spec/lib/vcr/library_hooks/typhoeus_spec.rb +162 -0
  120. data/spec/lib/vcr/library_hooks/webmock_spec.rb +118 -0
  121. data/spec/lib/vcr/library_hooks_spec.rb +51 -0
  122. data/spec/lib/vcr/middleware/faraday_spec.rb +182 -0
  123. data/spec/lib/vcr/middleware/rack_spec.rb +115 -0
  124. data/spec/lib/vcr/request_ignorer_spec.rb +70 -0
  125. data/spec/lib/vcr/request_matcher_registry_spec.rb +345 -0
  126. data/spec/lib/vcr/structs_spec.rb +732 -0
  127. data/spec/lib/vcr/test_frameworks/cucumber_spec.rb +107 -0
  128. data/spec/lib/vcr/test_frameworks/rspec_spec.rb +83 -0
  129. data/spec/lib/vcr/util/hooks_spec.rb +158 -0
  130. data/spec/lib/vcr/util/internet_connection_spec.rb +37 -0
  131. data/spec/lib/vcr/util/version_checker_spec.rb +31 -0
  132. data/spec/lib/vcr/version_spec.rb +27 -0
  133. data/spec/lib/vcr_spec.rb +349 -0
  134. data/spec/monkey_patches.rb +182 -0
  135. data/spec/spec_helper.rb +62 -0
  136. data/spec/support/configuration_stubbing.rb +8 -0
  137. data/spec/support/cucumber_helpers.rb +35 -0
  138. data/spec/support/fixnum_extension.rb +10 -0
  139. data/spec/support/http_library_adapters.rb +289 -0
  140. data/spec/support/limited_uri.rb +21 -0
  141. data/spec/support/ruby_interpreter.rb +7 -0
  142. data/spec/support/shared_example_groups/excon.rb +63 -0
  143. data/spec/support/shared_example_groups/hook_into_http_library.rb +594 -0
  144. data/spec/support/shared_example_groups/request_hooks.rb +59 -0
  145. data/spec/support/sinatra_app.rb +86 -0
  146. data/spec/support/vcr_localhost_server.rb +76 -0
  147. data/spec/support/vcr_stub_helpers.rb +17 -0
  148. 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
+