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