vcr 1.2.0 → 1.3.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 (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
+