vcr 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +12 -0
- data/FullBuildRakeFile +22 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +46 -25
- data/Guardfile +9 -0
- data/README.md +10 -371
- data/Rakefile +6 -2
- data/TODO.md +4 -0
- data/features/support/env.rb +5 -1
- data/full_build +1 -0
- data/lib/vcr.rb +17 -7
- data/lib/vcr/basic_object.rb +39 -0
- data/lib/vcr/config.rb +4 -7
- data/lib/vcr/deprecations.rb +14 -0
- data/lib/vcr/http_stubbing_adapters/common.rb +44 -13
- data/lib/vcr/http_stubbing_adapters/fakeweb.rb +17 -28
- data/lib/vcr/http_stubbing_adapters/multi_object_proxy.rb +43 -0
- data/lib/vcr/http_stubbing_adapters/typhoeus.rb +101 -0
- data/lib/vcr/http_stubbing_adapters/webmock.rb +16 -37
- data/lib/vcr/internet_connection.rb +2 -1
- data/lib/vcr/structs.rb +32 -0
- data/lib/vcr/version.rb +1 -1
- data/spec/config_spec.rb +5 -25
- data/spec/deprecations_spec.rb +31 -3
- data/spec/extensions/net_http_spec.rb +1 -0
- data/spec/fixtures/1.9.1/fake_example.com_responses.yml +32 -1
- data/spec/fixtures/not_1.9.1/fake_example.com_responses.yml +32 -1
- data/spec/http_stubbing_adapters/fakeweb_spec.rb +9 -24
- data/spec/http_stubbing_adapters/multi_object_proxy_spec.rb +101 -0
- data/spec/http_stubbing_adapters/typhoeus_spec.rb +28 -0
- data/spec/http_stubbing_adapters/webmock_spec.rb +8 -26
- data/spec/internet_connection_spec.rb +25 -7
- data/spec/spec_helper.rb +3 -1
- data/spec/structs_spec.rb +30 -6
- data/spec/support/http_library_adapters.rb +50 -15
- data/spec/support/http_stubbing_adapter.rb +65 -56
- data/spec/support/version_checker.rb +29 -0
- data/spec/vcr_spec.rb +30 -12
- data/vcr.gemspec +5 -4
- metadata +44 -15
data/Rakefile
CHANGED
@@ -23,10 +23,13 @@ task :cleanup_rcov_files do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
permutations = {
|
26
|
-
'fakeweb'
|
27
|
-
'
|
26
|
+
'fakeweb' => %w( net/http typhoeus ),
|
27
|
+
'typhoeus' => %w( typhoeus ),
|
28
|
+
'webmock' => %w( net/http typhoeus httpclient patron em-http-request curb )
|
28
29
|
}
|
29
30
|
|
31
|
+
permutations.delete('typhoeus') if RUBY_INTERPRETER == :jruby
|
32
|
+
|
30
33
|
require 'cucumber/rake/task'
|
31
34
|
namespace :features do
|
32
35
|
permutations.each do |http_stubbing_adapter, http_libraries|
|
@@ -35,6 +38,7 @@ namespace :features do
|
|
35
38
|
namespace http_stubbing_adapter do
|
36
39
|
http_libraries.each do |http_lib|
|
37
40
|
next if RUBY_INTERPRETER != :mri && %w( patron em-http-request curb ).include?(http_lib)
|
41
|
+
next if RUBY_INTERPRETER == :jruby && http_lib == 'typhoeus'
|
38
42
|
|
39
43
|
sanitized_http_lib = http_lib.gsub('/', '_')
|
40
44
|
features_subtasks << "features:#{http_stubbing_adapter}:#{sanitized_http_lib}"
|
data/TODO.md
ADDED
data/features/support/env.rb
CHANGED
@@ -43,7 +43,11 @@ YAML_SERIALIZATION_VERSION = RUBY_VERSION == '1.9.1' ? '1.9.1' : 'not_1.9.1'
|
|
43
43
|
|
44
44
|
VCR.config do |c|
|
45
45
|
c.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes', YAML_SERIALIZATION_VERSION)
|
46
|
-
|
46
|
+
|
47
|
+
stubs = [ENV['HTTP_STUBBING_ADAPTER'].to_sym, :typhoeus].uniq
|
48
|
+
stubs.delete(:typhoeus) if RUBY_INTERPRETER == :jruby
|
49
|
+
|
50
|
+
c.stub_with *stubs
|
47
51
|
end
|
48
52
|
|
49
53
|
VCR.module_eval do
|
data/full_build
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.6,1.8.7,1.9.1,1.9.2,ree,jruby,rbx-head rake -f FullBuildRakeFile build | tee tmp/full_build.out
|
data/lib/vcr.rb
CHANGED
@@ -9,6 +9,7 @@ require 'vcr/http_stubbing_adapters/common'
|
|
9
9
|
module VCR
|
10
10
|
extend self
|
11
11
|
|
12
|
+
autoload :BasicObject, 'vcr/basic_object'
|
12
13
|
autoload :CucumberTags, 'vcr/cucumber_tags'
|
13
14
|
autoload :InternetConnection, 'vcr/internet_connection'
|
14
15
|
autoload :RSpec, 'vcr/rspec'
|
@@ -54,13 +55,22 @@ module VCR
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def http_stubbing_adapter
|
57
|
-
@http_stubbing_adapter ||=
|
58
|
-
|
59
|
-
VCR
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
@http_stubbing_adapter ||= begin
|
59
|
+
if [:fakeweb, :webmock].all? { |l| VCR::Config.http_stubbing_libraries.include?(l) }
|
60
|
+
raise ArgumentError.new("You have configured VCR to use both :fakeweb and :webmock. You cannot use both.")
|
61
|
+
end
|
62
|
+
|
63
|
+
adapters = VCR::Config.http_stubbing_libraries.map do |lib|
|
64
|
+
case lib
|
65
|
+
when :fakeweb; HttpStubbingAdapters::FakeWeb
|
66
|
+
when :webmock; HttpStubbingAdapters::WebMock
|
67
|
+
when :typhoeus; HttpStubbingAdapters::Typhoeus
|
68
|
+
else raise ArgumentError.new("#{lib.inspect} is not a supported HTTP stubbing library.")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
raise ArgumentError.new("The http stubbing library is not configured.") if adapters.empty?
|
73
|
+
HttpStubbingAdapters::MultiObjectProxy.for(*adapters)
|
64
74
|
end
|
65
75
|
end
|
66
76
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module VCR
|
2
|
+
# taken directly from backports:
|
3
|
+
# https://github.com/marcandre/backports/blob/v1.18.2/lib/backports/basic_object.rb
|
4
|
+
class BasicObject
|
5
|
+
KEEP = [:instance_eval, :instance_exec, :__send__,
|
6
|
+
"instance_eval", "instance_exec", "__send__"]
|
7
|
+
# undefine almost all instance methods
|
8
|
+
begin
|
9
|
+
old_verbose, $VERBOSE = $VERBOSE, nil # silence the warning for undefining __id__
|
10
|
+
(instance_methods - KEEP).each do |method|
|
11
|
+
undef_method method
|
12
|
+
end
|
13
|
+
ensure
|
14
|
+
$VERBOSE = old_verbose
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def === (cmp)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
# Let's try to keep things clean, in case methods have been added to Object
|
23
|
+
# either directly or through an included module.
|
24
|
+
# We'll do this whenever a class is derived from BasicObject
|
25
|
+
# Ideally, we'd do this by trapping Object.method_added
|
26
|
+
# and M.method_added for any module M included in Object or a submodule
|
27
|
+
# Seems really though to get right, but pull requests welcome ;-)
|
28
|
+
def inherited(sub)
|
29
|
+
BasicObject.class_eval do
|
30
|
+
(instance_methods - KEEP).each do |method|
|
31
|
+
if Object.method_defined?(method) && instance_method(method).owner == Object.instance_method(method).owner
|
32
|
+
undef_method method
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/vcr/config.rb
CHANGED
@@ -16,12 +16,9 @@ module VCR
|
|
16
16
|
@default_cassette_options
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
def
|
21
|
-
@
|
22
|
-
defined_constants = [:FakeWeb, :WebMock].select { |c| Object.const_defined?(c) }
|
23
|
-
defined_constants[0].to_s.downcase.to_sym if defined_constants.size == 1
|
24
|
-
end
|
19
|
+
attr_reader :http_stubbing_libraries
|
20
|
+
def stub_with(*http_stubbing_libraries)
|
21
|
+
@http_stubbing_libraries = http_stubbing_libraries
|
25
22
|
end
|
26
23
|
|
27
24
|
def ignore_localhost=(value)
|
@@ -34,4 +31,4 @@ module VCR
|
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
37
|
-
end
|
34
|
+
end
|
data/lib/vcr/deprecations.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
1
|
module VCR
|
2
|
+
class Config
|
3
|
+
class << self
|
4
|
+
def http_stubbing_library
|
5
|
+
warn "WARNING: `VCR::Config.http_stubbing_library` is deprecated. Use `VCR::Config.http_stubbing_libraries` instead."
|
6
|
+
@http_stubbing_libraries && @http_stubbing_libraries.first
|
7
|
+
end
|
8
|
+
|
9
|
+
def http_stubbing_library=(library)
|
10
|
+
warn "WARNING: `VCR::Config.http_stubbing_library = #{library.inspect}` is deprecated. Use `VCR::Config.stub_with #{library.inspect}` instead."
|
11
|
+
stub_with library
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
class Cassette
|
3
17
|
def allow_real_http_requests_to?(uri)
|
4
18
|
warn "WARNING: VCR::Cassette#allow_real_http_requests_to? is deprecated and should no longer be used."
|
@@ -1,38 +1,69 @@
|
|
1
1
|
module VCR
|
2
2
|
module HttpStubbingAdapters
|
3
|
-
autoload :FakeWeb,
|
4
|
-
autoload :
|
3
|
+
autoload :FakeWeb, 'vcr/http_stubbing_adapters/fakeweb'
|
4
|
+
autoload :MultiObjectProxy, 'vcr/http_stubbing_adapters/multi_object_proxy'
|
5
|
+
autoload :Typhoeus, 'vcr/http_stubbing_adapters/typhoeus'
|
6
|
+
autoload :WebMock, 'vcr/http_stubbing_adapters/webmock'
|
5
7
|
|
6
8
|
class UnsupportedRequestMatchAttributeError < ArgumentError; end
|
7
9
|
|
8
10
|
module Common
|
11
|
+
def self.add_vcr_info_to_exception_message(exception_klass)
|
12
|
+
exception_klass.class_eval do
|
13
|
+
def message
|
14
|
+
super + ". You can use VCR to automatically record this request and replay it later. " +
|
15
|
+
"For more details, visit the VCR wiki at: http://github.com/myronmarston/vcr/wiki"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
9
20
|
def check_version!
|
10
21
|
version_too_low, version_too_high = compare_version
|
11
22
|
|
12
23
|
if version_too_low
|
13
|
-
raise "You are using #{library_name} #{version}. VCR requires version #{
|
24
|
+
raise "You are using #{library_name} #{version}. VCR requires version #{version_requirement}."
|
14
25
|
elsif version_too_high
|
15
|
-
warn "You are using #{library_name} #{version}. VCR is known to work with #{library_name}
|
26
|
+
warn "You are using #{library_name} #{version}. VCR is known to work with #{library_name} #{version_requirement}. It may not work with this version."
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
30
|
+
def library_name
|
31
|
+
@library_name ||= self.to_s.split('::').last
|
32
|
+
end
|
33
|
+
|
19
34
|
private
|
20
35
|
|
21
36
|
def compare_version
|
22
|
-
major, minor, patch =
|
23
|
-
|
37
|
+
major, minor, patch = parse_version(version)
|
38
|
+
min_major, min_minor, min_patch = parse_version(self::MINIMUM_VERSION)
|
39
|
+
max_major, max_minor = parse_version(self::MAXIMUM_VERSION)
|
24
40
|
|
25
|
-
return true, false if major <
|
26
|
-
return false, true if major >
|
41
|
+
return true, false if major < min_major
|
42
|
+
return false, true if major > max_major
|
27
43
|
|
28
|
-
return true, false if minor <
|
29
|
-
return false, true if minor >
|
44
|
+
return true, false if minor < min_minor
|
45
|
+
return false, true if minor > max_minor
|
30
46
|
|
31
|
-
return patch <
|
47
|
+
return patch < min_patch, false
|
32
48
|
end
|
33
49
|
|
34
|
-
def
|
35
|
-
|
50
|
+
def version_requirement
|
51
|
+
max_major, max_minor = parse_version(self::MAXIMUM_VERSION)
|
52
|
+
">= #{self::MINIMUM_VERSION}, < #{max_major}.#{max_minor + 1}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_version(version)
|
56
|
+
version.split('.').map { |v| v.to_i }
|
57
|
+
end
|
58
|
+
|
59
|
+
def grouped_responses(http_interactions, match_attributes)
|
60
|
+
responses = Hash.new { |h,k| h[k] = [] }
|
61
|
+
|
62
|
+
http_interactions.each do |i|
|
63
|
+
responses[i.request.matcher(match_attributes)] << i.response
|
64
|
+
end
|
65
|
+
|
66
|
+
responses
|
36
67
|
end
|
37
68
|
end
|
38
69
|
end
|
@@ -10,26 +10,31 @@ module VCR
|
|
10
10
|
UNSUPPORTED_REQUEST_MATCH_ATTRIBUTES = [:body, :headers].freeze
|
11
11
|
LOCALHOST_REGEX = %r|\Ahttps?://((\w+:)?\w+@)?(#{VCR::LOCALHOST_ALIASES.map { |a| Regexp.escape(a) }.join('|')})(:\d+)?/|i
|
12
12
|
|
13
|
-
|
13
|
+
MINIMUM_VERSION = '1.3.0'
|
14
|
+
MAXIMUM_VERSION = '1.3'
|
15
|
+
|
16
|
+
def http_connections_allowed=(value)
|
17
|
+
@http_connections_allowed = value
|
18
|
+
update_fakeweb_allow_net_connect
|
19
|
+
end
|
14
20
|
|
15
21
|
def http_connections_allowed?
|
16
22
|
!!::FakeWeb.allow_net_connect?("http://some.url/besides/localhost")
|
17
23
|
end
|
18
24
|
|
19
|
-
def
|
20
|
-
@
|
25
|
+
def ignore_localhost=(value)
|
26
|
+
@ignore_localhost = value
|
21
27
|
update_fakeweb_allow_net_connect
|
22
28
|
end
|
23
29
|
|
30
|
+
def ignore_localhost?
|
31
|
+
!!@ignore_localhost
|
32
|
+
end
|
33
|
+
|
24
34
|
def stub_requests(http_interactions, match_attributes)
|
25
35
|
validate_match_attributes(match_attributes)
|
26
|
-
requests = Hash.new { |h,k| h[k] = [] }
|
27
|
-
|
28
|
-
http_interactions.each do |i|
|
29
|
-
requests[i.request.matcher(match_attributes)] << i.response
|
30
|
-
end
|
31
36
|
|
32
|
-
|
37
|
+
grouped_responses(http_interactions, match_attributes).each do |request_matcher, responses|
|
33
38
|
::FakeWeb.register_uri(
|
34
39
|
request_matcher.method || :any,
|
35
40
|
request_matcher.uri,
|
@@ -48,22 +53,13 @@ module VCR
|
|
48
53
|
|
49
54
|
def request_stubbed?(request, match_attributes)
|
50
55
|
validate_match_attributes(match_attributes)
|
51
|
-
|
56
|
+
!!::FakeWeb.registered_uri?(request.method, request.uri)
|
52
57
|
end
|
53
58
|
|
54
59
|
def request_uri(net_http, request)
|
55
60
|
::FakeWeb::Utility.request_uri_as_string(net_http, request)
|
56
61
|
end
|
57
62
|
|
58
|
-
def ignore_localhost=(value)
|
59
|
-
@ignore_localhost = value
|
60
|
-
update_fakeweb_allow_net_connect
|
61
|
-
end
|
62
|
-
|
63
|
-
def ignore_localhost?
|
64
|
-
@ignore_localhost
|
65
|
-
end
|
66
|
-
|
67
63
|
private
|
68
64
|
|
69
65
|
def version
|
@@ -101,12 +97,5 @@ module VCR
|
|
101
97
|
end
|
102
98
|
end
|
103
99
|
|
104
|
-
|
105
|
-
|
106
|
-
class NetConnectNotAllowedError
|
107
|
-
def message
|
108
|
-
super + ". You can use VCR to automatically record this request and replay it later. For more details, see the VCR README at: http://github.com/myronmarston/vcr/tree/v#{VCR.version}"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
100
|
+
VCR::HttpStubbingAdapters::Common.add_vcr_info_to_exception_message(FakeWeb::NetConnectNotAllowedError)
|
101
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module VCR
|
2
|
+
module HttpStubbingAdapters
|
3
|
+
class MultiObjectProxy < defined?(::BasicObject) ? ::BasicObject : VCR::BasicObject
|
4
|
+
|
5
|
+
def self.for(*objects)
|
6
|
+
return objects.first if objects.size == 1
|
7
|
+
new(*objects)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :proxied_objects
|
11
|
+
|
12
|
+
def initialize(*objects)
|
13
|
+
::Kernel.raise ::ArgumentError.new("You must pass at least one object to proxy to") if objects.empty?
|
14
|
+
::Kernel.raise ::ArgumentError.new("Cannot proxy to nil") if objects.any? { |o| o.nil? }
|
15
|
+
|
16
|
+
@proxied_objects = objects
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to?(message)
|
20
|
+
proxied_objects.any? { |o| o.respond_to?(message) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def method_missing(name, *args)
|
26
|
+
responding_proxied_objects = proxied_objects.select { |o| o.respond_to?(name) }
|
27
|
+
return super if responding_proxied_objects.empty?
|
28
|
+
|
29
|
+
uniq_return_vals = responding_proxied_objects.map { |o| o.__send__(name, *args) }.uniq
|
30
|
+
|
31
|
+
return nil unless method_return_val_important?(name)
|
32
|
+
return uniq_return_vals.first if uniq_return_vals.size == 1
|
33
|
+
|
34
|
+
::Kernel.raise "The proxied objects returned different values for calls to #{name}: #{uniq_return_vals.inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_return_val_important?(method_name)
|
38
|
+
method_name == :request_uri || method_name.to_s =~ /\?$/
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
|
3
|
+
module VCR
|
4
|
+
module HttpStubbingAdapters
|
5
|
+
module Typhoeus
|
6
|
+
include VCR::HttpStubbingAdapters::Common
|
7
|
+
extend self
|
8
|
+
|
9
|
+
MINIMUM_VERSION = '0.2.0'
|
10
|
+
MAXIMUM_VERSION = '0.2'
|
11
|
+
|
12
|
+
def http_connections_allowed=(value)
|
13
|
+
::Typhoeus::Hydra.allow_net_connect = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def http_connections_allowed?
|
17
|
+
!!::Typhoeus::Hydra.allow_net_connect?
|
18
|
+
end
|
19
|
+
|
20
|
+
def ignore_localhost=(value)
|
21
|
+
::Typhoeus::Hydra.ignore_localhost = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def ignore_localhost?
|
25
|
+
!!::Typhoeus::Hydra.ignore_localhost?
|
26
|
+
end
|
27
|
+
|
28
|
+
def stub_requests(http_interactions, match_attributes)
|
29
|
+
grouped_responses(http_interactions, match_attributes).each do |request_matcher, responses|
|
30
|
+
::Typhoeus::Hydra.stub(
|
31
|
+
request_matcher.method || :any,
|
32
|
+
request_matcher.uri,
|
33
|
+
request_hash(request_matcher)
|
34
|
+
).and_return(
|
35
|
+
responses.map do |response|
|
36
|
+
::Typhoeus::Response.new(
|
37
|
+
:code => response.status.code,
|
38
|
+
:body => response.body,
|
39
|
+
:headers_hash => response.headers
|
40
|
+
)
|
41
|
+
end
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_stubs_checkpoint(checkpoint_name)
|
47
|
+
checkpoints[checkpoint_name] = ::Typhoeus::Hydra.stubs.dup
|
48
|
+
end
|
49
|
+
|
50
|
+
def restore_stubs_checkpoint(checkpoint_name)
|
51
|
+
::Typhoeus::Hydra.stubs = checkpoints.delete(checkpoint_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def version
|
57
|
+
::Typhoeus::VERSION
|
58
|
+
end
|
59
|
+
|
60
|
+
def checkpoints
|
61
|
+
@checkpoints ||= {}
|
62
|
+
end
|
63
|
+
|
64
|
+
def request_hash(request_matcher)
|
65
|
+
hash = {}
|
66
|
+
|
67
|
+
hash[:body] = request_matcher.body if request_matcher.match_requests_on?(:body)
|
68
|
+
hash[:headers] = request_matcher.headers if request_matcher.match_requests_on?(:headers)
|
69
|
+
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Typhoeus::Hydra.after_request_before_on_complete do |request|
|
77
|
+
unless request.response.mock?
|
78
|
+
http_interaction = VCR::HTTPInteraction.new(
|
79
|
+
VCR::Request.new(
|
80
|
+
request.method,
|
81
|
+
request.url,
|
82
|
+
request.body,
|
83
|
+
request.headers
|
84
|
+
),
|
85
|
+
VCR::Response.new(
|
86
|
+
VCR::ResponseStatus.new(
|
87
|
+
request.response.code,
|
88
|
+
request.response.status_message
|
89
|
+
),
|
90
|
+
request.response.headers_hash,
|
91
|
+
request.response.body,
|
92
|
+
request.response.http_version
|
93
|
+
)
|
94
|
+
)
|
95
|
+
|
96
|
+
VCR.record_http_interaction(http_interaction)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
VCR::HttpStubbingAdapters::Common.add_vcr_info_to_exception_message(Typhoeus::Hydra::NetConnectNotAllowedError)
|
101
|
+
|