vcr 1.3.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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