Skip to content
This repository has been archived by the owner on Aug 23, 2022. It is now read-only.

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
rubiii committed Sep 11, 2010
0 parents commit 08d56d4
Show file tree
Hide file tree
Showing 22 changed files with 535 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
nbproject
pkg
doc
coverage
.loadpath
.project
*~
*.gem
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--color
11 changes: 11 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
source :rubygems

group :development, :test do
gem "httpclient", "~> 2.1.5"
gem "curb", "~> 0.7.8"
end

group :test do
gem "rspec", "2.0.0.beta.19"
gem "mocha", "~> 0.9.8"
end
3 changes: 3 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= HTTPI

HTTP interface supporting multiple backends.
13 changes: 13 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
begin
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new do |t|
t.spec_opts = %w(-fd -c)
end
rescue LoadError
task :spec do
abort "Run 'gem install rspec' to be able to run specs"
end
end

task :default => :spec
1 change: 1 addition & 0 deletions autotest/discover.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Autotest.add_discovery { "rspec2" }
1 change: 1 addition & 0 deletions lib/httpi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "httpi/client"
40 changes: 40 additions & 0 deletions lib/httpi/adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "httpi/adapter/httpclient"
require "httpi/adapter/curb"

module HTTPI
module Adapter

# The default adapter.
DEFAULT = :httpclient

# Returns the adapter to use. Defaults to <tt>HTTPI::Adapter::DEFAULT</tt>.
def self.use
@use ||= DEFAULT
end

# Sets the +adapter+ to use. Raises an +ArgumentError+ unless the +adapter+ exists.
def self.use=(adapter)
validate_adapter! adapter
@use = adapter
end

# Returns a memoized +Hash+ of adapters.
def self.adapters
@adapters ||= { :httpclient => HTTPClient, :curb => Curb }
end

# Returns an +adapter+. Raises an +ArgumentError+ unless the +adapter+ exists.
def self.find(adapter)
validate_adapter! adapter
adapters[adapter]
end

private

# Raises an +ArgumentError+ unless the +adapter+ exists.
def self.validate_adapter!(adapter)
raise ArgumentError, "Invalid HTTPI adapter: #{adapter}" unless adapters[adapter]
end

end
end
16 changes: 16 additions & 0 deletions lib/httpi/adapter/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module HTTPI
module Adapter
module Base

# List of methods expected to be implemented by an adapter.
METHODS = %w(setup client headers headers= get post)

METHODS.each do |method|
define_method method do
raise NotImplementedError, "#{Adapter.use} does not implement a #{method} method"
end
end

end
end
end
45 changes: 45 additions & 0 deletions lib/httpi/adapter/curb.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require "httpi/response"
require "httpi/adapter/base"

module HTTPI
module Adapter
module Curb
include Base

def setup
require "curb"
end

def client
@client ||= Curl::Easy.new
end

def headers
client.headers
end

def headers=(headers)
client.headers = headers
end

def get(url)
client.url = url.to_s
client.http_get
respond
end

def post(url, body)
client.url = url.to_s
client.http_post body
respond
end

private

def respond
Response.new client.response_code, client.headers, client.body_str
end

end
end
end
39 changes: 39 additions & 0 deletions lib/httpi/adapter/httpclient.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "httpi/response"
require "httpi/adapter/base"

module HTTPI
module Adapter
module HTTPClient
include Base

def setup
require "httpclient"
end

def client
@client ||= ::HTTPClient.new
end

def headers
@headers ||= {}
end

attr_writer :headers

def get(url)
respond_with client.get(url)
end

def post(url, body)
respond_with client.post(url, body, headers)
end

private

def respond_with(response)
Response.new response.code, Hash[response.header.all], response.content
end

end
end
end
12 changes: 12 additions & 0 deletions lib/httpi/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "httpi/interface"

module HTTPI
class Client
include Interface

def initialize(adapter = nil)
load! adapter
end

end
end
21 changes: 21 additions & 0 deletions lib/httpi/interface.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require "httpi/adapter"

module HTTPI
module Interface

# Loads a given +adapter+. Defaults to load <tt>HTTPI::Adapter::DEFAULT</tt>.
def load!(adapter = nil)
adapter ||= Adapter.use
include_adapter Adapter.find(adapter)
setup
end

private

# Includes a given +adapter+ into the current class.
def include_adapter(adapter)
self.class.send :include, adapter
end

end
end
47 changes: 47 additions & 0 deletions lib/httpi/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require "zlib"
require "stringio"

module HTTPI
class Response

# Range of HTTP response codes considered to be successful.
SuccessfulResponseCodes = 200..299

# Initializer expects an HTTP response +code+, +headers+ and +body+.
def initialize(code, headers, body)
self.code = code
self.headers = headers
self.body = body
end

attr_accessor :code, :headers

# Returns whether the HTTP response is considered successful.
def error?
!SuccessfulResponseCodes.include? code.to_i
end

# Returns the HTTP response body.
def body
gzipped_response? ? decoded_body : @body
end

attr_writer :body

private

# Returns whether the response is gzipped.
def gzipped_response?
headers["Content-Encoding"] == "gzip" || @body[0..1] == "\x1f\x8b"
end

# Returns the gzip decoded response body.
def decoded_body
gzip = Zlib::GzipReader.new StringIO.new(@body)
gzip.read
ensure
gzip.close
end

end
end
70 changes: 70 additions & 0 deletions spec/httpi/adapter/curb_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require "spec_helper"
require "httpi/adapter/curb"

describe HTTPI::Adapter::Curb do
include HelperMethods

before do
@adapter = Class.new { include HTTPI::Adapter::Curb }.new
end

describe "#setup" do
it "should require the Curb gem" do
@adapter.expects(:require).with("curb")
@adapter.setup
end
end

context "after #setup:" do
before { @adapter.setup }

describe "#client" do
it "should return a memoized Curl::Easy instance" do
client = @adapter.client

client.should be_an(Curl::Easy)
client.should equal(@adapter.client)
end
end

describe "#headers" do
it "should default to return an empty Hash" do
@adapter.headers.should == {}
end
end

describe "#headers=" do
it "should set a given Hash of HTTP headers" do
@adapter.headers = some_headers_hash
@adapter.headers.should == some_headers_hash
end
end

describe "#get" do
before do
@adapter.client.expects(:http_get)
@adapter.client.expects(:response_code).returns(200)
@adapter.client.expects(:headers).returns(some_headers_hash)
@adapter.client.expects(:body_str).returns(some_html)
end

it "should return a valid HTTPI::Response" do
@adapter.get(some_url).should be_a_valid_httpi_response
end
end

describe "#post" do
before do
@adapter.client.expects(:http_post)
@adapter.client.expects(:response_code).returns(200)
@adapter.client.expects(:headers).returns(some_headers_hash)
@adapter.client.expects(:body_str).returns(some_html)
end

it "should return a valid HTTPI::Response" do
@adapter.post(some_url, some_html).should be_a_valid_httpi_response
end
end
end

end
Loading

0 comments on commit 08d56d4

Please sign in to comment.