vcr 2.0.0.rc1 → 2.0.0.rc2

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