vcr 2.0.0.beta2 → 2.0.0.rc1

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 (91) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +0 -2
  3. data/CHANGELOG.md +22 -1
  4. data/CONTRIBUTING.md +46 -0
  5. data/Gemfile +1 -9
  6. data/README.md +8 -2
  7. data/Rakefile +12 -1
  8. data/cucumber.yml +8 -9
  9. data/features/.nav +10 -4
  10. data/features/cassettes/format.feature +1 -1
  11. data/features/cassettes/no_cassette.feature +8 -11
  12. data/features/configuration/allow_http_connections_when_no_cassette.feature +4 -4
  13. data/features/configuration/filter_sensitive_data.feature +3 -0
  14. data/features/configuration/hook_into.feature +4 -8
  15. data/features/configuration/ignore_request.feature +191 -0
  16. data/features/getting_started.md +38 -21
  17. data/features/hooks/after_http_request.feature +44 -0
  18. data/features/hooks/around_http_request.feature +56 -0
  19. data/features/hooks/before_http_request.feature +44 -0
  20. data/features/hooks/before_playback.feature +181 -0
  21. data/features/hooks/before_record.feature +172 -0
  22. data/features/middleware/faraday.feature +7 -3
  23. data/features/record_modes/none.feature +2 -1
  24. data/features/record_modes/once.feature +2 -1
  25. data/features/request_matching/body.feature +2 -2
  26. data/features/request_matching/custom_matcher.feature +2 -2
  27. data/features/request_matching/headers.feature +2 -2
  28. data/features/request_matching/host.feature +2 -2
  29. data/features/request_matching/identical_request_sequence.feature +2 -2
  30. data/features/request_matching/method.feature +2 -2
  31. data/features/request_matching/path.feature +2 -2
  32. data/features/request_matching/playback_repeats.feature +2 -1
  33. data/features/request_matching/uri.feature +2 -2
  34. data/features/support/env.rb +21 -12
  35. data/features/test_frameworks/cucumber.feature +9 -4
  36. data/features/test_frameworks/{rspec.feature → rspec_macro.feature} +7 -7
  37. data/features/test_frameworks/rspec_metadata.feature +90 -0
  38. data/lib/vcr.rb +1 -1
  39. data/lib/vcr/cassette.rb +3 -3
  40. data/lib/vcr/cassette/http_interaction_list.rb +13 -9
  41. data/lib/vcr/cassette/migrator.rb +1 -1
  42. data/lib/vcr/configuration.rb +37 -0
  43. data/lib/vcr/errors.rb +172 -6
  44. data/lib/vcr/library_hooks.rb +4 -6
  45. data/lib/vcr/library_hooks/excon.rb +23 -11
  46. data/lib/vcr/library_hooks/fakeweb.rb +85 -24
  47. data/lib/vcr/library_hooks/faraday.rb +30 -2
  48. data/lib/vcr/library_hooks/typhoeus.rb +25 -3
  49. data/lib/vcr/library_hooks/webmock.rb +25 -36
  50. data/lib/vcr/middleware/faraday.rb +23 -5
  51. data/lib/vcr/request_handler.rb +12 -1
  52. data/lib/vcr/request_ignorer.rb +12 -1
  53. data/lib/vcr/request_matcher_registry.rb +1 -9
  54. data/lib/vcr/structs.rb +32 -2
  55. data/lib/vcr/test_frameworks/rspec.rb +28 -0
  56. data/lib/vcr/util/hooks.rb +12 -4
  57. data/lib/vcr/util/version_checker.rb +2 -0
  58. data/lib/vcr/version.rb +1 -1
  59. data/spec/fixtures/cassette_spec/example.yml +1 -1
  60. data/spec/fixtures/{fake_example.com_responses.yml → fake_example_responses.yml} +0 -0
  61. data/spec/monkey_patches.rb +1 -1
  62. data/spec/spec_helper.rb +3 -1
  63. data/spec/support/http_library_adapters.rb +4 -3
  64. data/spec/support/shared_example_groups/hook_into_http_library.rb +194 -12
  65. data/spec/support/shared_example_groups/request_hooks.rb +58 -0
  66. data/spec/support/shared_example_groups/version_checking.rb +5 -0
  67. data/spec/support/sinatra_app.rb +17 -9
  68. data/spec/support/vcr_stub_helpers.rb +17 -0
  69. data/spec/vcr/cassette/http_interaction_list_spec.rb +28 -29
  70. data/spec/vcr/cassette/migrator_spec.rb +6 -7
  71. data/spec/vcr/cassette_spec.rb +5 -5
  72. data/spec/vcr/configuration_spec.rb +51 -32
  73. data/spec/vcr/deprecations_spec.rb +0 -8
  74. data/spec/vcr/errors_spec.rb +129 -0
  75. data/spec/vcr/library_hooks/excon_spec.rb +21 -4
  76. data/spec/vcr/library_hooks/fakeweb_spec.rb +71 -3
  77. data/spec/vcr/library_hooks/faraday_spec.rb +45 -0
  78. data/spec/vcr/library_hooks/typhoeus_spec.rb +31 -1
  79. data/spec/vcr/library_hooks/webmock_spec.rb +26 -3
  80. data/spec/vcr/middleware/faraday_spec.rb +84 -1
  81. data/spec/vcr/request_ignorer_spec.rb +16 -0
  82. data/spec/vcr/request_matcher_registry_spec.rb +0 -26
  83. data/spec/vcr/structs_spec.rb +61 -1
  84. data/spec/vcr/test_frameworks/rspec_spec.rb +32 -0
  85. data/spec/vcr/util/hooks_spec.rb +73 -63
  86. data/spec/vcr_spec.rb +2 -2
  87. data/vcr.gemspec +5 -5
  88. metadata +51 -31
  89. data/features/configuration/hooks.feature +0 -270
  90. data/features/configuration/ignore_hosts.feature +0 -61
  91. data/features/configuration/ignore_localhost.feature +0 -97
@@ -1,3 +1,31 @@
1
- Kernel.warn "WARNING: `VCR.config { |c| c.stub_with :faraday }` is deprecated. " +
2
- "Just use `VCR::Middleware::Faraday` in your faraday stack."
1
+ require 'faraday'
2
+
3
+ module VCR
4
+ class LibraryHooks
5
+ module Faraday
6
+ module BuilderClassExtension
7
+ def new(*args)
8
+ super.extend BuilderInstanceExtension
9
+ end
10
+
11
+ ::Faraday::Builder.extend self
12
+ end
13
+
14
+ module BuilderInstanceExtension
15
+ def lock!(*args)
16
+ insert_vcr_middleware
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def insert_vcr_middleware
23
+ return if handlers.any? { |h| h.klass == VCR::Middleware::Faraday }
24
+ adapter_index = handlers.index { |h| h.klass < ::Faraday::Adapter }
25
+ insert_before(adapter_index, VCR::Middleware::Faraday)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
3
31
 
@@ -35,6 +35,11 @@ module VCR
35
35
 
36
36
  private
37
37
 
38
+ def on_connection_not_allowed
39
+ invoke_after_request_hook(nil)
40
+ super
41
+ end
42
+
38
43
  def vcr_request
39
44
  @vcr_request ||= vcr_request_from(request)
40
45
  end
@@ -59,9 +64,15 @@ module VCR
59
64
 
60
65
  extend Helpers
61
66
  ::Typhoeus::Hydra.after_request_before_on_complete do |request|
62
- unless VCR.library_hooks.disabled?(:typhoeus) || request.response.mock?
63
- http_interaction = VCR::HTTPInteraction.new(vcr_request_from(request), vcr_response_from(request.response))
64
- VCR.record_http_interaction(http_interaction)
67
+ unless VCR.library_hooks.disabled?(:typhoeus)
68
+ vcr_request, vcr_response = vcr_request_from(request), vcr_response_from(request.response)
69
+
70
+ unless request.response.mock?
71
+ http_interaction = VCR::HTTPInteraction.new(vcr_request, vcr_response)
72
+ VCR.record_http_interaction(http_interaction)
73
+ end
74
+
75
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request, vcr_response)
65
76
  end
66
77
  end
67
78
 
@@ -72,6 +83,17 @@ module VCR
72
83
  end
73
84
  end
74
85
 
86
+ class << Typhoeus::Hydra
87
+ # ensure HTTP requests are always allowed; VCR takes care of disallowing
88
+ # them at the appropriate times in its hook
89
+ def allow_net_connect_with_vcr?(*args)
90
+ VCR.turned_on? ? true : allow_net_connect_without_vcr?
91
+ end
92
+
93
+ alias allow_net_connect_without_vcr? allow_net_connect?
94
+ alias allow_net_connect? allow_net_connect_with_vcr?
95
+ end unless Typhoeus::Hydra.respond_to?(:allow_net_connect_with_vcr?)
96
+
75
97
  VCR.configuration.after_library_hooks_loaded do
76
98
  # ensure WebMock's Typhoeus adapter does not conflict with us here
77
99
  # (i.e. to double record requests or whatever).
@@ -2,20 +2,12 @@ require 'vcr/util/version_checker'
2
2
  require 'vcr/request_handler'
3
3
  require 'webmock'
4
4
 
5
- VCR::VersionChecker.new('WebMock', WebMock.version, '1.7.0', '1.7').check_version!
5
+ VCR::VersionChecker.new('WebMock', WebMock.version, '1.7.8', '1.7').check_version!
6
6
 
7
7
  module VCR
8
8
  class LibraryHooks
9
9
  module WebMock
10
10
  module Helpers
11
- def response_hash_for(response)
12
- {
13
- :body => response.body,
14
- :status => [response.status.code.to_i, response.status.message],
15
- :headers => response.headers
16
- }
17
- end
18
-
19
11
  def vcr_request_from(webmock_request)
20
12
  VCR::Request.new \
21
13
  webmock_request.method,
@@ -43,30 +35,27 @@ module VCR
43
35
 
44
36
  private
45
37
 
46
- def stubbed_response
47
- VCR.http_interactions.has_interaction_matching?(vcr_request)
48
- end
49
-
50
38
  def vcr_request
51
39
  @vcr_request ||= vcr_request_from(request)
52
40
  end
53
41
 
54
- def on_ignored_request; false; end
55
- def on_stubbed_request; true; end
56
- def on_recordable_request; false; end
42
+ def on_connection_not_allowed
43
+ invoke_after_request_hook(nil)
44
+ super
45
+ end
46
+
47
+ def on_stubbed_request
48
+ {
49
+ :body => stubbed_response.body,
50
+ :status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
51
+ :headers => stubbed_response.headers
52
+ }
53
+ end
57
54
  end
58
55
 
59
56
  extend Helpers
60
57
 
61
- GLOBAL_VCR_HOOK = ::WebMock::RequestStub.new(:any, /.*/).tap do |stub|
62
- stub.with { |request|
63
- RequestHandler.new(request).handle
64
- }.to_return(lambda { |request|
65
- response_hash_for VCR.http_interactions.response_for(vcr_request_from(request))
66
- })
67
- end
68
-
69
- ::WebMock::StubRegistry.instance.register_request_stub(GLOBAL_VCR_HOOK)
58
+ ::WebMock.globally_stub_request { |req| RequestHandler.new(req).handle }
70
59
 
71
60
  ::WebMock.after_request(:real_requests_only => true) do |request, response|
72
61
  unless VCR.library_hooks.disabled?(:webmock)
@@ -77,6 +66,12 @@ module VCR
77
66
  VCR.record_http_interaction(http_interaction)
78
67
  end
79
68
  end
69
+
70
+ ::WebMock.after_request do |request, response|
71
+ unless VCR.library_hooks.disabled?(:webmock)
72
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request_from(request), vcr_response_from(response))
73
+ end
74
+ end
80
75
  end
81
76
  end
82
77
  end
@@ -84,17 +79,11 @@ end
84
79
  class << WebMock
85
80
  # ensure HTTP requests are always allowed; VCR takes care of disallowing
86
81
  # them at the appropriate times in its hook
87
- undef net_connect_allowed?
88
- def net_connect_allowed?(*args)
89
- true
82
+ def net_connect_allowed_with_vcr?(*args)
83
+ VCR.turned_on? ? true : net_connect_allowed_without_vcr?
90
84
  end
91
- end
92
85
 
93
- WebMock::StubRegistry.class_eval do
94
- # ensure our VCR hook is not removed when WebMock is reset
95
- undef reset!
96
- def reset!
97
- self.request_stubs = [VCR::LibraryHooks::WebMock::GLOBAL_VCR_HOOK]
98
- end
99
- end
86
+ alias net_connect_allowed_without_vcr? net_connect_allowed?
87
+ alias net_connect_allowed? net_connect_allowed_with_vcr?
88
+ end unless WebMock.respond_to?(:net_connect_allowed_with_vcr?)
100
89
 
@@ -15,21 +15,31 @@ module VCR
15
15
  end
16
16
 
17
17
  def call(env)
18
- # Faraday must be exlusive here in case another library hook is being used.
19
- # We don't want double recording/double playback.
20
- VCR.library_hooks.exclusively_enabled(:faraday) do
21
- RequestHandler.new(@app, env).handle
22
- end
18
+ RequestHandler.new(@app, env).handle
23
19
  end
24
20
 
25
21
  class RequestHandler < ::VCR::RequestHandler
26
22
  attr_reader :app, :env
27
23
  def initialize(app, env)
28
24
  @app, @env = app, env
25
+ @has_on_complete_hook = false
26
+ end
27
+
28
+ def handle
29
+ # Faraday must be exlusive here in case another library hook is being used.
30
+ # We don't want double recording/double playback.
31
+ VCR.library_hooks.exclusive_hook = :faraday
32
+ super
33
+ ensure
34
+ invoke_after_request_hook(response_for(env)) unless delay_finishing?
29
35
  end
30
36
 
31
37
  private
32
38
 
39
+ def delay_finishing?
40
+ !!env[:parallel_manager] && @has_on_complete_hook
41
+ end
42
+
33
43
  def vcr_request
34
44
  @vcr_request ||= VCR::Request.new \
35
45
  env[:method],
@@ -40,6 +50,7 @@ module VCR
40
50
 
41
51
  def response_for(env)
42
52
  response = env[:response]
53
+ return nil unless response
43
54
 
44
55
  VCR::Response.new(
45
56
  VCR::ResponseStatus.new(response.status, nil),
@@ -64,10 +75,17 @@ module VCR
64
75
  end
65
76
 
66
77
  def on_recordable_request
78
+ @has_on_complete_hook = true
67
79
  app.call(env).on_complete do |env|
68
80
  VCR.record_http_interaction(VCR::HTTPInteraction.new(vcr_request, response_for(env)))
81
+ invoke_after_request_hook(response_for(env)) if delay_finishing?
69
82
  end
70
83
  end
84
+
85
+ def invoke_after_request_hook(response)
86
+ super
87
+ VCR.library_hooks.exclusive_hook = nil
88
+ end
71
89
  end
72
90
  end
73
91
  end
@@ -1,6 +1,7 @@
1
1
  module VCR
2
2
  class RequestHandler
3
3
  def handle
4
+ invoke_before_request_hook
4
5
  return on_ignored_request if should_ignore?
5
6
  return on_stubbed_request if stubbed_response
6
7
  return on_recordable_request if VCR.real_http_connections_allowed?
@@ -9,6 +10,16 @@ module VCR
9
10
 
10
11
  private
11
12
 
13
+ def invoke_before_request_hook
14
+ return if disabled?
15
+ VCR.configuration.invoke_hook(:before_http_request, vcr_request)
16
+ end
17
+
18
+ def invoke_after_request_hook(vcr_response)
19
+ return if disabled?
20
+ VCR.configuration.invoke_hook(:after_http_request, vcr_request, vcr_response)
21
+ end
22
+
12
23
  def should_ignore?
13
24
  disabled? || VCR.request_ignorer.ignore?(vcr_request)
14
25
  end
@@ -37,7 +48,7 @@ module VCR
37
48
  end
38
49
 
39
50
  def on_connection_not_allowed
40
- raise VCR::HTTPConnectionNotAllowedError.new(vcr_request)
51
+ raise VCR::Errors::UnhandledHTTPRequestError.new(vcr_request)
41
52
  end
42
53
  end
43
54
  end
@@ -1,10 +1,21 @@
1
1
  require 'uri'
2
2
  require 'set'
3
+ require 'vcr/util/hooks'
3
4
 
4
5
  module VCR
5
6
  class RequestIgnorer
7
+ include VCR::Hooks
8
+
9
+ define_hook :ignore_request
10
+
6
11
  LOCALHOST_ALIASES = %w( localhost 127.0.0.1 0.0.0.0 )
7
12
 
13
+ def initialize
14
+ ignore_request do |request|
15
+ ignored_hosts.include?(URI(request.uri).host)
16
+ end
17
+ end
18
+
8
19
  def ignore_localhost=(value)
9
20
  if value
10
21
  ignore_hosts(*LOCALHOST_ALIASES)
@@ -18,7 +29,7 @@ module VCR
18
29
  end
19
30
 
20
31
  def ignore?(request)
21
- ignored_hosts.include?(URI(request.uri).host)
32
+ invoke_hook(:ignore_request, request).any?
22
33
  end
23
34
 
24
35
  private
@@ -80,20 +80,12 @@ module VCR
80
80
 
81
81
  def register_built_ins
82
82
  register(:method) { |r1, r2| r1.method == r2.method }
83
- register(:uri) { |r1, r2| without_standard_port(r1.uri) == without_standard_port(r2.uri) }
83
+ register(:uri) { |r1, r2| r1.uri == r2.uri }
84
84
  register(:host) { |r1, r2| URI(r1.uri).host == URI(r2.uri).host }
85
85
  register(:path) { |r1, r2| URI(r1.uri).path == URI(r2.uri).path }
86
86
  register(:body) { |r1, r2| r1.body == r2.body }
87
87
  register(:headers) { |r1, r2| r1.headers == r2.headers }
88
88
  end
89
-
90
- def without_standard_port(uri)
91
- URI(uri).tap do |u|
92
- if [['http', 80], ['https', 443]].include?([u.scheme, u.port])
93
- u.port = nil
94
- end
95
- end
96
- end
97
89
  end
98
90
  end
99
91
 
data/lib/vcr/structs.rb CHANGED
@@ -78,6 +78,12 @@ module VCR
78
78
  include Normalizers::Header
79
79
  include Normalizers::Body
80
80
 
81
+ def initialize(*args)
82
+ super
83
+ self.method = self.method.to_s.downcase.to_sym if self.method
84
+ self.uri = without_standard_port(self.uri)
85
+ end
86
+
81
87
  def to_hash
82
88
  {
83
89
  'method' => method.to_s,
@@ -101,6 +107,31 @@ module VCR
101
107
  return super if args.empty?
102
108
  @@object_method.bind(self).call(*args)
103
109
  end
110
+
111
+ # transforms the request into a fiber aware one
112
+ def fiber_aware
113
+ extend FiberAware
114
+ end
115
+
116
+ module FiberAware
117
+ def proceed
118
+ Fiber.yield
119
+ end
120
+
121
+ def to_proc
122
+ lambda { proceed }
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def without_standard_port(uri)
129
+ return uri if uri.nil?
130
+ u = URI(uri)
131
+ return uri unless [['http', 80], ['https', 443]].include?([u.scheme, u.port])
132
+ u.port = nil
133
+ u.to_s
134
+ end
104
135
  end
105
136
 
106
137
  class HTTPInteraction < Struct.new(:request, :response, :recorded_at)
@@ -188,8 +219,7 @@ module VCR
188
219
  end
189
220
 
190
221
  def update_content_length_header
191
- # TODO: should this be the bytesize?
192
- value = body ? body.length.to_s : '0'
222
+ value = body ? body.bytesize.to_s : '0'
193
223
  key = %w[ Content-Length content-length ].find { |k| headers.has_key?(k) }
194
224
  headers[key] = [value] if key
195
225
  end
@@ -32,6 +32,34 @@ module VCR
32
32
  group_descriptions.compact.reverse.join('/')
33
33
  end
34
34
  end
35
+
36
+ module Metadata
37
+ extend self
38
+
39
+ def configure!
40
+ ::RSpec.configure do |config|
41
+ vcr_cassette_name_for = lambda do |metadata|
42
+ description = metadata[:description]
43
+
44
+ if example_group = metadata[:example_group]
45
+ [vcr_cassette_name_for[example_group], description].join('/')
46
+ else
47
+ description
48
+ end
49
+ end
50
+
51
+ config.around(:each, :vcr => lambda { |v| !!v }) do |example|
52
+ options = example.metadata[:vcr]
53
+ options = {} unless options.is_a?(Hash) # in case it's just :vcr => true
54
+
55
+ cassette_name = options.delete(:cassette_name) ||
56
+ vcr_cassette_name_for[example.metadata]
57
+
58
+ VCR.use_cassette(cassette_name, options, &example)
59
+ end
60
+ end
61
+ end
62
+ end
35
63
  end
36
64
  end
37
65
 
@@ -1,3 +1,5 @@
1
+ require 'vcr/util/variable_args_block_caller'
2
+
1
3
  module VCR
2
4
  module Hooks
3
5
  include VariableArgsBlockCaller
@@ -6,8 +8,12 @@ module VCR
6
8
  klass.extend(ClassMethods)
7
9
  end
8
10
 
9
- def invoke_hook(hook, tag=nil, *args)
10
- hooks_for(hook, tag).each do |callback|
11
+ def invoke_hook(hook, *args)
12
+ invoke_tagged_hook(hook, nil, *args)
13
+ end
14
+
15
+ def invoke_tagged_hook(hook, tag, *args)
16
+ hooks_for(hook, tag).map do |callback|
11
17
  call_block(callback, *args)
12
18
  end
13
19
  end
@@ -34,7 +40,9 @@ module VCR
34
40
  end
35
41
 
36
42
  module ClassMethods
37
- def define_hook(hook)
43
+ def define_hook(hook, prepend = false)
44
+ placement_method = prepend ? :unshift : :<<
45
+
38
46
  # We use splat args here because 1.8.7 doesn't allow default
39
47
  # values for block arguments, so we have to fake it.
40
48
  define_method hook do |*args, &block|
@@ -43,7 +51,7 @@ module VCR
43
51
  end
44
52
 
45
53
  tag = args.first
46
- hooks[hook][tag] << block
54
+ hooks[hook][tag].send(placement_method, block)
47
55
  end
48
56
  end
49
57
  end