Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Added support for Excon #158

Merged
merged 2 commits into from

3 participants

@dim
dim commented

Had to add a few pending specs, as Excon is (currently) simply ignoring userinfo in URLs. Not sure if that's the best approach. D

@bblimke bblimke merged commit e6a9e81 into bblimke:master
@bblimke
Owner

Thank you! That's a big one. Great to have one more library supported by WebMock.

Have you considered using Excon internal stubbing?

@dim
dim commented

I did, but not sure how that would work in combination with WebMock's internals (registry, etc). We are using exconn + net/http in many of our projects, hence I wanted to create a single, consistent way of stubbing our connections.

@bblimke
Owner

Thanks. It will go in Webmock 1.8.0 release within couple of days.

@bblimke
Owner

@myronmarston could you please check if this will cause any conflicts with Excon support in VCR, before I release 1.8.0?

@myronmarston
Collaborator

Yep, thanks for the heads up! I will try to get to it later this weekend.

@myronmarston
Collaborator

All my VCR tests pass, so this is good to go :).

@bblimke
Owner

Excellent! I'm planning to release 1.8.0 tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 3, 2012
  1. @dim

    Added support for Excon

    dim authored
  2. @dim
This page is out of date. Refresh to see the latest.
View
1  lib/webmock.rb
@@ -14,6 +14,7 @@
require 'webmock/http_lib_adapters/curb_adapter'
require 'webmock/http_lib_adapters/em_http_request_adapter'
require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
+require 'webmock/http_lib_adapters/excon_adapter'
require 'webmock/errors'
View
94 lib/webmock/http_lib_adapters/excon_adapter.rb
@@ -0,0 +1,94 @@
+begin
+ require 'excon'
+rescue LoadError
+ # excon not found
+end
+
+if defined?(Excon)
+
+ module WebMock
+ module HttpLibAdapters
+
+ class ExconAdapter < HttpLibAdapter
+ adapter_for :excon
+
+ def self.enable!
+ Excon.send(:remove_const, :Connection)
+ Excon.send(:const_set, :Connection, ExconConnection)
+ end
+
+ def self.disable!
+ Excon.send(:remove_const, :Connection)
+ Excon.send(:const_set, :Connection, ExconConnection.superclass)
+ end
+
+
+ def self.to_query(hash)
+ string = ""
+ for key, values in hash
+ if values.nil?
+ string << key.to_s << '&'
+ else
+ for value in [*values]
+ string << key.to_s << '=' << CGI.escape(value.to_s) << '&'
+ end
+ end
+ end
+ string.chop! # remove trailing '&'
+ end
+
+ def self.build_request(params)
+ params = params.dup
+ method = (params.delete(:method) || :get).to_s.downcase.to_sym
+ params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
+ uri = Addressable::URI.new(params).to_s
+ WebMock::RequestSignature.new method, uri, :body => params[:body], :headers => params[:headers]
+ end
+
+ def self.real_response(mock)
+ raise Excon::Errors::Timeout if mock.should_timeout
+ mock.raise_error_if_any
+ Excon::Response.new \
+ :body => mock.body,
+ :status => mock.status[0].to_i,
+ :headers => mock.headers
+ end
+
+ def self.mock_response(real)
+ mock = WebMock::Response.new
+ mock.status = real.status
+ mock.headers = real.headers
+ mock.body = real.body
+ mock
+ end
+
+ def self.perform_callbacks(request, response, options = {})
+ return unless WebMock::CallbackRegistry.any_callbacks?
+ WebMock::CallbackRegistry.invoke_callbacks(options.merge(:lib => :excon), request, response)
+ end
+
+ end
+
+ class ExconConnection < ::Excon::Connection
+
+ def request_kernel(params, &block)
+ mock_request = ExconAdapter.build_request params.dup
+ WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
+
+ if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
+ ExconAdapter.perform_callbacks(mock_request, mock_response, :real_request => false)
+ ExconAdapter.real_response(mock_response)
+ elsif WebMock.net_connect_allowed?(mock_request.uri)
+ real_response = super
+ ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), :real_request => true)
+ real_response
+ else
+ raise WebMock::NetConnectNotAllowedError.new(mock_request)
+ end
+ end
+
+ end
+ end
+ end
+
+end
View
15 spec/acceptance/excon/excon_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+require 'acceptance/webmock_shared'
+require 'acceptance/excon/excon_spec_helper'
+
+describe "Excon" do
+ include ExconSpecHelper
+ include_context "with WebMock"
+
+ it 'should allow Excon requests to use query hash paramters' do
+ stub_request(:get, "http://example.com/resource/?a=1&b=2").to_return(:body => "abc")
+ Excon.get('http://example.com', :path => "resource/", :query => {:a => 1, :b => 2}).body.should == "abc"
+ end
+
+end
+
View
37 spec/acceptance/excon/excon_spec_helper.rb
@@ -0,0 +1,37 @@
+require 'ostruct'
+
+module ExconSpecHelper
+
+ def http_request(method, uri, options = {}, &block)
+ uri = Addressable::URI.heuristic_parse(uri)
+ uri = uri.omit(:userinfo).to_s.gsub(' ', '+')
+
+ options = options.merge(:method => method) # Dup and merge
+ response = Excon.new(uri).request(options, &block)
+
+ headers = WebMock::Util::Headers.normalize_headers(response.headers)
+ headers = headers.inject({}) do |res, (name, value)|
+ res[name] = value.is_a?(Array) ? value.flatten.join(', ') : value
+ res
+ end
+
+ OpenStruct.new \
+ :body => response.body,
+ :headers => headers,
+ :status => response.status.to_s,
+ :message => ""
+ end
+
+ def client_timeout_exception_class
+ Excon::Errors::Timeout
+ end
+
+ def connection_refused_exception_class
+ Excon::Errors::SocketError
+ end
+
+ def http_library
+ :excon
+ end
+
+end
View
3  spec/acceptance/shared/callbacks.rb
@@ -87,7 +87,8 @@
it "should pass real response to callback with status and message" do
# not supported by em-http-request, it always returns "unknown" for http_reason
- unless http_library == :em_http_request
+ # not supported by excon, it only returns a status code
+ unless [:em_http_request, :excon].include?(http_library)
@response.status[0].should == 302
@response.status[1].should == "Found"
end
View
1  spec/acceptance/shared/request_expectations.rb
@@ -412,6 +412,7 @@
describe "with authentication" do
before(:each) do
+ pending "Excon does not accept basic auth user-info in URLs" if http_library == :excon
stub_request(:any, "http://user:pass@www.example.com")
stub_request(:any, "http://user:pazz@www.example.com")
end
View
12 spec/acceptance/shared/returning_declared_responses.rb
@@ -76,7 +76,8 @@ class MyException < StandardError; end;
stub_request(:get, "www.example.com").to_return(:status => [500, "Internal Server Error"])
response = http_request(:get, "http://www.example.com/")
# not supported by em-http-request, it always returns "unknown" for http_reason
- unless http_library == :em_http_request
+ # not supported by excon, it only returns a status code
+ unless [:em_http_request, :excon].include?(http_library)
response.message.should == "Internal Server Error"
end
end
@@ -90,7 +91,8 @@ class MyException < StandardError; end;
stub_request(:get, "www.example.com")
response = http_request(:get, "http://www.example.com/")
# not supported by em-http-request, it always returns "unknown" for http_reason
- unless http_library == :em_http_request
+ # not supported by excon, it only returns a status code
+ unless [:em_http_request, :excon].include?(http_library)
response.message.should == ""
end
end
@@ -188,7 +190,8 @@ def call(request)
it "should return recorded status message" do
# not supported by em-http-request, it always returns "unknown" for http_reason
- unless http_library == :em_http_request
+ # not supported by excon, it only returns a status code
+ unless [:em_http_request, :excon].include?(http_library)
@response.message.should == "OK"
end
end
@@ -225,7 +228,8 @@ def call(request)
it "should return recorded status message" do
# not supported by em-http-request, it always returns "unknown" for http_reason
- unless http_library == :em_http_request
+ # not supported by excon, it only returns a status code
+ unless [:em_http_request, :excon].include?(http_library)
@response.message.should == "OK"
end
end
View
4 spec/acceptance/shared/stubbing_requests.rb
@@ -260,6 +260,10 @@
end
describe "when stubbing request with basic authentication" do
+ before do
+ pending "Excon does not accept basic auth user-info in URLs" if http_library == :excon
+ end
+
it "should match if credentials are the same" do
stub_request(:get, "user:pass@www.example.com")
http_request(:get, "http://user:pass@www.example.com/").status.should == "200"
View
1  webmock.gemspec
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'em-synchrony', '>= 1.0.0' if RUBY_VERSION >= "1.9"
s.add_development_dependency 'curb', '>= 0.8.0'
s.add_development_dependency 'typhoeus', '>= 0.3.0' unless RUBY_PLATFORM =~ /java/
+ s.add_development_dependency 'excon', '>= 0.9.5'
s.add_development_dependency 'minitest', '>= 2.2.2'
s.add_development_dependency 'rdoc', ((RUBY_VERSION == '1.8.6') ? '<= 3.5.0' : '>3.5.0')
Something went wrong with that request. Please try again.