vcr 1.3.3 → 1.4.0

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 (46) hide show
  1. data/.rspec +2 -0
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +23 -4
  5. data/Guardfile +6 -0
  6. data/README.md +5 -4
  7. data/Rakefile +1 -3
  8. data/TODO.md +3 -0
  9. data/cucumber.yml +3 -3
  10. data/features/cassettes/no_cassette.feature +67 -0
  11. data/features/configuration/allow_http_connections_when_no_cassette.feature +54 -0
  12. data/features/configuration/ignore_localhost.feature +2 -2
  13. data/features/configuration/stub_with.feature +40 -16
  14. data/features/http_libraries/em_http_request.feature +217 -0
  15. data/features/middleware/faraday.feature +89 -0
  16. data/features/middleware/rack.feature +95 -0
  17. data/features/step_definitions/cli_steps.rb +7 -0
  18. data/lib/vcr.rb +48 -4
  19. data/lib/vcr/config.rb +11 -2
  20. data/lib/vcr/extensions/net_http.rb +4 -0
  21. data/lib/vcr/http_stubbing_adapters/common.rb +35 -4
  22. data/lib/vcr/http_stubbing_adapters/faraday.rb +80 -0
  23. data/lib/vcr/http_stubbing_adapters/typhoeus.rb +1 -1
  24. data/lib/vcr/http_stubbing_adapters/webmock.rb +18 -16
  25. data/lib/vcr/middleware/cassette_arguments.rb +18 -0
  26. data/lib/vcr/middleware/common.rb +22 -0
  27. data/lib/vcr/middleware/faraday.rb +79 -0
  28. data/lib/vcr/middleware/rack.rb +13 -0
  29. data/lib/vcr/request_matcher.rb +16 -1
  30. data/lib/vcr/version.rb +1 -1
  31. data/spec/config_spec.rb +27 -2
  32. data/spec/fixtures/1.9.1/fake_example.com_responses.yml +0 -29
  33. data/spec/fixtures/1.9.1/match_requests_on.yml +2 -2
  34. data/spec/fixtures/not_1.9.1/fake_example.com_responses.yml +0 -29
  35. data/spec/http_stubbing_adapters/faraday_spec.rb +84 -0
  36. data/spec/middleware/cassette_arguments_spec.rb +32 -0
  37. data/spec/middleware/faraday_spec.rb +52 -0
  38. data/spec/middleware/rack_spec.rb +54 -0
  39. data/spec/monkey_patches.rb +1 -0
  40. data/spec/request_matcher_spec.rb +36 -0
  41. data/spec/spec_helper.rb +10 -0
  42. data/spec/support/http_library_adapters.rb +113 -25
  43. data/spec/support/http_stubbing_adapter.rb +55 -16
  44. data/spec/vcr_spec.rb +92 -4
  45. data/vcr.gemspec +1 -0
  46. metadata +72 -34
data/lib/vcr/config.rb CHANGED
@@ -25,12 +25,21 @@ module VCR
25
25
  end
26
26
 
27
27
  def ignore_localhost=(value)
28
- VCR.http_stubbing_adapter && VCR.http_stubbing_adapter.ignore_localhost = value
29
28
  @ignore_localhost = value
29
+ VCR.http_stubbing_adapter.ignore_localhost = value if http_stubbing_libraries.any?
30
30
  end
31
31
 
32
32
  def ignore_localhost?
33
- VCR.http_stubbing_adapter ? VCR.http_stubbing_adapter.ignore_localhost? : @ignore_localhost
33
+ @ignore_localhost
34
+ end
35
+
36
+ def allow_http_connections_when_no_cassette=(value)
37
+ @allow_http_connections_when_no_cassette = value
38
+ VCR.http_stubbing_adapter.set_http_connections_allowed_to_default if http_stubbing_libraries.any?
39
+ end
40
+
41
+ def allow_http_connections_when_no_cassette?
42
+ !!@allow_http_connections_when_no_cassette
34
43
  end
35
44
  end
36
45
  end
@@ -4,6 +4,10 @@ require 'vcr/extensions/net_http_response'
4
4
  module Net
5
5
  class HTTP
6
6
  def request_with_vcr(request, body = nil, &block)
7
+ unless VCR::HttpStubbingAdapters::FakeWeb.enabled?
8
+ return request_without_vcr(request, body, &block)
9
+ end
10
+
7
11
  vcr_request = VCR::Request.from_net_http_request(self, request)
8
12
  response = request_without_vcr(request, body)
9
13
 
@@ -1,6 +1,7 @@
1
1
  module VCR
2
2
  module HttpStubbingAdapters
3
3
  autoload :FakeWeb, 'vcr/http_stubbing_adapters/fakeweb'
4
+ autoload :Faraday, 'vcr/http_stubbing_adapters/faraday'
4
5
  autoload :MultiObjectProxy, 'vcr/http_stubbing_adapters/multi_object_proxy'
5
6
  autoload :Typhoeus, 'vcr/http_stubbing_adapters/typhoeus'
6
7
  autoload :WebMock, 'vcr/http_stubbing_adapters/webmock'
@@ -8,17 +9,43 @@ module VCR
8
9
  class UnsupportedRequestMatchAttributeError < ArgumentError; end
9
10
 
10
11
  module Common
11
- def self.add_vcr_info_to_exception_message(exception_klass)
12
- exception_klass.class_eval do
13
- def initialize(message)
14
- super(message + '. ' + VCR::HttpStubbingAdapters::Common::RECORDING_INSTRUCTIONS)
12
+ class << self
13
+ attr_accessor :exclusively_enabled_adapter
14
+
15
+ def add_vcr_info_to_exception_message(exception_klass)
16
+ exception_klass.class_eval do
17
+ def initialize(message)
18
+ super(message + '. ' + VCR::HttpStubbingAdapters::Common::RECORDING_INSTRUCTIONS)
19
+ end
15
20
  end
16
21
  end
22
+
23
+ def adapters
24
+ @adapters ||= []
25
+ end
26
+
27
+ def included(adapter)
28
+ adapters << adapter
29
+ end
17
30
  end
18
31
 
19
32
  RECORDING_INSTRUCTIONS = "You can use VCR to automatically record this request and replay it later. " +
20
33
  "For more details, visit the VCR wiki at: http://github.com/myronmarston/vcr/wiki"
21
34
 
35
+ def enabled?
36
+ [nil, self].include? VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter
37
+ end
38
+
39
+ def exclusively_enabled
40
+ VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter = self
41
+
42
+ begin
43
+ yield
44
+ ensure
45
+ VCR::HttpStubbingAdapters::Common.exclusively_enabled_adapter = nil
46
+ end
47
+ end
48
+
22
49
  def check_version!
23
50
  version_too_low, version_too_high = compare_version
24
51
 
@@ -33,6 +60,10 @@ module VCR
33
60
  @library_name ||= self.to_s.split('::').last
34
61
  end
35
62
 
63
+ def set_http_connections_allowed_to_default
64
+ self.http_connections_allowed = VCR::Config.allow_http_connections_when_no_cassette?
65
+ end
66
+
36
67
  private
37
68
 
38
69
  def compare_version
@@ -0,0 +1,80 @@
1
+ require 'faraday'
2
+
3
+ module VCR
4
+ module HttpStubbingAdapters
5
+ module Faraday
6
+ include Common
7
+ extend self
8
+
9
+ MINIMUM_VERSION = '0.5.3'
10
+ MAXIMUM_VERSION = '0.5'
11
+
12
+ attr_writer :http_connections_allowed, :ignore_localhost
13
+
14
+ def http_connections_allowed?
15
+ !!@http_connections_allowed
16
+ end
17
+
18
+ def ignore_localhost?
19
+ !!@ignore_localhost
20
+ end
21
+
22
+ def stub_requests(http_interactions, match_attributes)
23
+ grouped_responses(http_interactions, match_attributes).each do |request_matcher, responses|
24
+ queue = stub_queues[request_matcher]
25
+ responses.each { |res| queue << res }
26
+ end
27
+ end
28
+
29
+ def create_stubs_checkpoint(checkpoint_name)
30
+ checkpoints[checkpoint_name] = stub_queue_dup
31
+ end
32
+
33
+ def restore_stubs_checkpoint(checkpoint_name)
34
+ @stub_queues = checkpoints.delete(checkpoint_name)
35
+ end
36
+
37
+ def stubbed_response_for(request_matcher)
38
+ queue = stub_queues[request_matcher]
39
+ return queue.shift if queue.size > 1
40
+ queue.first
41
+ end
42
+
43
+ def reset!
44
+ instance_variables.each do |ivar|
45
+ remove_instance_variable(ivar)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def version
52
+ ::Faraday::VERSION
53
+ end
54
+
55
+ def checkpoints
56
+ @checkpoints ||= {}
57
+ end
58
+
59
+ def stub_queues
60
+ @stub_queues ||= hash_of_arrays
61
+ end
62
+
63
+ def stub_queue_dup
64
+ dup = hash_of_arrays
65
+
66
+ stub_queues.each do |k, v|
67
+ dup[k] = v.dup
68
+ end
69
+
70
+ dup
71
+ end
72
+
73
+ def hash_of_arrays
74
+ Hash.new { |h, k| h[k] = [] }
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ VCR::HttpStubbingAdapters::Common.add_vcr_info_to_exception_message(VCR::Middleware::Faraday::HttpConnectionNotAllowedError)
@@ -74,7 +74,7 @@ module VCR
74
74
  end
75
75
 
76
76
  Typhoeus::Hydra.after_request_before_on_complete do |request|
77
- unless request.response.mock?
77
+ if VCR::HttpStubbingAdapters::Typhoeus.enabled? && !request.response.mock?
78
78
  http_interaction = VCR::HTTPInteraction.new(
79
79
  VCR::Request.new(
80
80
  request.method,
@@ -73,25 +73,27 @@ module VCR
73
73
  end
74
74
 
75
75
  WebMock.after_request(:real_requests_only => true) do |request, response|
76
- http_interaction = VCR::HTTPInteraction.new(
77
- VCR::Request.new(
78
- request.method,
79
- request.uri.to_s,
80
- request.body,
81
- request.headers
82
- ),
83
- VCR::Response.new(
84
- VCR::ResponseStatus.new(
85
- response.status.first,
86
- response.status.last
76
+ if VCR::HttpStubbingAdapters::WebMock.enabled?
77
+ http_interaction = VCR::HTTPInteraction.new(
78
+ VCR::Request.new(
79
+ request.method,
80
+ request.uri.to_s,
81
+ request.body,
82
+ request.headers
87
83
  ),
88
- response.headers,
89
- response.body,
90
- '1.1'
84
+ VCR::Response.new(
85
+ VCR::ResponseStatus.new(
86
+ response.status.first,
87
+ response.status.last
88
+ ),
89
+ response.headers,
90
+ response.body,
91
+ '1.1'
92
+ )
91
93
  )
92
- )
93
94
 
94
- VCR.record_http_interaction(http_interaction)
95
+ VCR.record_http_interaction(http_interaction)
96
+ end
95
97
  end
96
98
 
97
99
  WebMock::NetConnectNotAllowedError.class_eval do
@@ -0,0 +1,18 @@
1
+ module VCR
2
+ module Middleware
3
+ class CassetteArguments
4
+ def initialize
5
+ @options = {}
6
+ end
7
+
8
+ def name(name = nil)
9
+ @name = name if name
10
+ @name
11
+ end
12
+
13
+ def options(options = {})
14
+ @options.merge!(options)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module VCR
2
+ module Middleware
3
+ module Common
4
+ def initialize(app, &block)
5
+ raise ArgumentError.new("You must provide a block to set the cassette options") unless block
6
+ @app, @cassette_arguments_block = app, block
7
+ end
8
+
9
+ private
10
+
11
+ def cassette_arguments(env)
12
+ arguments = CassetteArguments.new
13
+
14
+ block_args = [arguments]
15
+ block_args << env unless @cassette_arguments_block.arity == 1
16
+
17
+ @cassette_arguments_block.call(*block_args)
18
+ [arguments.name, arguments.options]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,79 @@
1
+ require 'faraday'
2
+
3
+ module VCR
4
+ module Middleware
5
+ class Faraday < ::Faraday::Middleware
6
+ include Common
7
+
8
+ class HttpConnectionNotAllowedError < StandardError; end
9
+
10
+ def call(env)
11
+ VCR::HttpStubbingAdapters::Faraday.exclusively_enabled do
12
+ VCR.use_cassette(*cassette_arguments(env)) do |cassette|
13
+ request = request_for(env)
14
+ request_matcher = request.matcher(cassette.match_requests_on)
15
+
16
+ if VCR::HttpStubbingAdapters::Faraday.ignore_localhost? && VCR::LOCALHOST_ALIASES.include?(URI.parse(request.uri).host)
17
+ @app.call(env)
18
+ elsif response = VCR::HttpStubbingAdapters::Faraday.stubbed_response_for(request_matcher)
19
+ env.update(
20
+ :status => response.status.code,
21
+ :response_headers => correctly_cased_headers(response.headers),
22
+ :body => response.body
23
+ )
24
+
25
+ env[:response].finish(env)
26
+ elsif VCR::HttpStubbingAdapters::Faraday.http_connections_allowed?
27
+ response = @app.call(env)
28
+
29
+ # Checking #enabled? isn't strictly needed, but conforms
30
+ # the Faraday adapter to the behavior of the other adapters
31
+ if VCR::HttpStubbingAdapters::Faraday.enabled?
32
+ VCR.record_http_interaction(VCR::HTTPInteraction.new(request, response_for(env)))
33
+ end
34
+
35
+ response
36
+ else
37
+ raise HttpConnectionNotAllowedError.new(
38
+ "Real HTTP connections are disabled. Request: #{request.method.inspect} #{request.uri}"
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def request_for(env)
48
+ VCR::Request.new(
49
+ env[:method],
50
+ env[:url].to_s,
51
+ env[:body],
52
+ env[:request_headers]
53
+ )
54
+ end
55
+
56
+ def response_for(env)
57
+ response = env[:response]
58
+
59
+ VCR::Response.new(
60
+ VCR::ResponseStatus.new(response.status, nil),
61
+ response.headers,
62
+ response.body,
63
+ '1.1'
64
+ )
65
+ end
66
+
67
+ def correctly_cased_headers(headers)
68
+ correctly_cased_hash = {}
69
+
70
+ headers.each do |key, value|
71
+ key = key.to_s.split('-').map { |segment| segment.capitalize }.join("-")
72
+ correctly_cased_hash[key] = value
73
+ end
74
+
75
+ correctly_cased_hash
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,13 @@
1
+ module VCR
2
+ module Middleware
3
+ class Rack
4
+ include Common
5
+
6
+ def call(env)
7
+ VCR.use_cassette(*cassette_arguments(env)) do
8
+ @app.call(env)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -69,7 +69,22 @@ module VCR
69
69
  # on Ruby 1.8.6, identical sets have different hash values,
70
70
  # but identical arrays have the same hash values,
71
71
  # so we convert match_attributes to an array here.
72
- [match_attributes.to_a, method, uri, headers, body].hash
72
+ [match_attributes.to_a, method, uri, sorted_header_array, body].hash
73
73
  end
74
+
75
+ private
76
+
77
+ def sorted_header_array
78
+ header_hash = headers
79
+ return header_hash unless header_hash.is_a?(Hash)
80
+
81
+ array = []
82
+ header_hash.each do |k, v|
83
+ v = v.sort if v.respond_to?(:sort)
84
+ array << [k, v]
85
+ end
86
+
87
+ array.sort! { |a1, a2| a1.first <=> a2.first }
88
+ end
74
89
  end
75
90
  end
data/lib/vcr/version.rb CHANGED
@@ -3,7 +3,7 @@ module VCR
3
3
 
4
4
  def version
5
5
  @version ||= begin
6
- string = '1.3.3'
6
+ string = '1.4.0'
7
7
 
8
8
  def string.parts; VCR.version.split('.').map { |p| p.to_i }; end
9
9
  def string.major; parts[0]; end
data/spec/config_spec.rb CHANGED
@@ -1,6 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe VCR::Config do
4
+ def stub_no_http_stubbing_adapter
5
+ VCR.stub(:http_stubbing_adapter).and_raise(ArgumentError)
6
+ VCR::Config.stub(:http_stubbing_libraries).and_return([])
7
+ end
8
+
4
9
  describe '#cassette_library_dir=' do
5
10
  temp_dir(File.expand_path(File.dirname(__FILE__) + '/fixtures/config_spec'))
6
11
 
@@ -52,12 +57,32 @@ describe VCR::Config do
52
57
  end
53
58
  end
54
59
 
55
- it 'stores the value even when VCR.http_stubbing_adapter is nil' do
56
- VCR.stub!(:http_stubbing_adapter).and_return(nil)
60
+ it 'stores the value even when VCR.http_stubbing_adapter is not set' do
61
+ stub_no_http_stubbing_adapter
57
62
  [true, false].each do |val|
58
63
  VCR::Config.ignore_localhost = val
59
64
  VCR::Config.ignore_localhost?.should == val
60
65
  end
61
66
  end
62
67
  end
68
+
69
+ describe '#allow_http_connections_when_no_cassette=' do
70
+ [true, false].each do |val|
71
+ it "sets the allow_http_connections_when_no_cassette to #{val} when set to #{val}" do
72
+ VCR::Config.allow_http_connections_when_no_cassette = val
73
+ VCR::Config.allow_http_connections_when_no_cassette?.should == val
74
+ end
75
+ end
76
+
77
+ it 'sets http_connnections_allowed to the default' do
78
+ VCR.http_stubbing_adapter.should respond_to(:set_http_connections_allowed_to_default)
79
+ VCR.http_stubbing_adapter.should_receive(:set_http_connections_allowed_to_default)
80
+ VCR::Config.allow_http_connections_when_no_cassette = true
81
+ end
82
+
83
+ it "works when the adapter hasn't been set yet" do
84
+ stub_no_http_stubbing_adapter
85
+ VCR::Config.allow_http_connections_when_no_cassette = true
86
+ end
87
+ end
63
88
  end