vcr 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +12 -0
  2. data/FullBuildRakeFile +22 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +46 -25
  5. data/Guardfile +9 -0
  6. data/README.md +10 -371
  7. data/Rakefile +6 -2
  8. data/TODO.md +4 -0
  9. data/features/support/env.rb +5 -1
  10. data/full_build +1 -0
  11. data/lib/vcr.rb +17 -7
  12. data/lib/vcr/basic_object.rb +39 -0
  13. data/lib/vcr/config.rb +4 -7
  14. data/lib/vcr/deprecations.rb +14 -0
  15. data/lib/vcr/http_stubbing_adapters/common.rb +44 -13
  16. data/lib/vcr/http_stubbing_adapters/fakeweb.rb +17 -28
  17. data/lib/vcr/http_stubbing_adapters/multi_object_proxy.rb +43 -0
  18. data/lib/vcr/http_stubbing_adapters/typhoeus.rb +101 -0
  19. data/lib/vcr/http_stubbing_adapters/webmock.rb +16 -37
  20. data/lib/vcr/internet_connection.rb +2 -1
  21. data/lib/vcr/structs.rb +32 -0
  22. data/lib/vcr/version.rb +1 -1
  23. data/spec/config_spec.rb +5 -25
  24. data/spec/deprecations_spec.rb +31 -3
  25. data/spec/extensions/net_http_spec.rb +1 -0
  26. data/spec/fixtures/1.9.1/fake_example.com_responses.yml +32 -1
  27. data/spec/fixtures/not_1.9.1/fake_example.com_responses.yml +32 -1
  28. data/spec/http_stubbing_adapters/fakeweb_spec.rb +9 -24
  29. data/spec/http_stubbing_adapters/multi_object_proxy_spec.rb +101 -0
  30. data/spec/http_stubbing_adapters/typhoeus_spec.rb +28 -0
  31. data/spec/http_stubbing_adapters/webmock_spec.rb +8 -26
  32. data/spec/internet_connection_spec.rb +25 -7
  33. data/spec/spec_helper.rb +3 -1
  34. data/spec/structs_spec.rb +30 -6
  35. data/spec/support/http_library_adapters.rb +50 -15
  36. data/spec/support/http_stubbing_adapter.rb +65 -56
  37. data/spec/support/version_checker.rb +29 -0
  38. data/spec/vcr_spec.rb +30 -12
  39. data/vcr.gemspec +5 -4
  40. 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' => %w( net/http ),
27
- 'webmock' => %w( net/http httpclient patron em-http-request curb )
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
@@ -0,0 +1,4 @@
1
+ * use `expect { }.to` rather than `lambda { }.should` in specs.
2
+ * Use fakefs gem instead of using temp_dir in specs.
3
+ * Use localhost server in specs instead of hitting example.com.
4
+ * Fix #method vs .method in specs.
@@ -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
- c.http_stubbing_library = ENV['HTTP_STUBBING_ADAPTER'].to_sym
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 ||= case VCR::Config.http_stubbing_library
58
- when :fakeweb
59
- VCR::HttpStubbingAdapters::FakeWeb
60
- when :webmock
61
- VCR::HttpStubbingAdapters::WebMock
62
- else
63
- raise ArgumentError.new("The http stubbing library is not configured correctly. You should set it to :webmock or :fakeweb.")
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
- attr_writer :http_stubbing_library
20
- def http_stubbing_library
21
- @http_stubbing_library ||= begin
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
@@ -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, 'vcr/http_stubbing_adapters/fakeweb'
4
- autoload :WebMock, 'vcr/http_stubbing_adapters/webmock'
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 #{self::VERSION_REQUIREMENT} or greater."
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} ~> #{self::VERSION_REQUIREMENT}. It may not work with this version."
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 = *version.split('.').map { |v| v.to_i }
23
- req_major, req_minor, req_patch = *self::VERSION_REQUIREMENT.split('.').map { |v| v.to_i }
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 < req_major
26
- return false, true if major > req_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 < req_minor
29
- return false, true if minor > req_minor
44
+ return true, false if minor < min_minor
45
+ return false, true if minor > max_minor
30
46
 
31
- return patch < req_patch, false
47
+ return patch < min_patch, false
32
48
  end
33
49
 
34
- def library_name
35
- @library_name ||= self.to_s.split('::').last
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
- VERSION_REQUIREMENT = '1.3.0'
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 http_connections_allowed=(value)
20
- @http_connections_allowed = value
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
- requests.each do |request_matcher, responses|
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
- ::FakeWeb.registered_uri?(request.method, request.uri)
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
- if defined?(FakeWeb::NetConnectNotAllowedError)
105
- module FakeWeb
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
+