Skip to content
This repository has been archived by the owner on Oct 20, 2020. It is now read-only.

Commit

Permalink
TEH ÜBER STACK
Browse files Browse the repository at this point in the history
  • Loading branch information
mislav committed Mar 26, 2011
0 parents commit 82d3b15
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/faraday-stack.rb
@@ -0,0 +1 @@
require 'faraday_stack'
35 changes: 35 additions & 0 deletions lib/faraday_stack.rb
@@ -0,0 +1,35 @@
# encoding: utf-8
require 'faraday'
require 'forwardable'

module FaradayStack
extend Faraday::AutoloadHelper

autoload_all 'faraday_stack',
:ResponseMiddleware => 'response_middleware',
:ResponseJSON => 'response_json',
:ResponseXML => 'response_xml'

# THE ÜBER STACK
def self.default_connection
@default_connection ||= self.build
end

class << self
extend Forwardable
attr_writer :default_connection
def_delegators :default_connection, :get, :post, :put, :head, :delete
end

def self.build(url = nil, options = {})
Faraday::Connection.new(url, options) do |builder|
builder.request :url_encoded
builder.request :json
builder.use ResponseJSON, :content_type => 'application/json'
builder.use ResponseXML, :content_type => /[+\/]xml$/
builder.response :raise_error
yield builder if block_given?
builder.adapter Faraday.default_adapter
end
end
end
32 changes: 32 additions & 0 deletions lib/faraday_stack/response_json.rb
@@ -0,0 +1,32 @@
module FaradayStack
class ResponseJSON < ResponseMiddleware
adapter_name = nil

# loads the JSON decoder either from yajl-ruby or activesupport
dependency do
require 'yajl'
adapter_name = Yajl::Parser.name
end

dependency do
require 'active_support/json/decoding'
adapter_name = ActiveSupport::JSON.name
end unless loaded?

# defines a parser block depending on which adapter has loaded
case adapter_name
when 'Yajl::Parser'
define_parser do |body|
Yajl::Parser.parse(body)
end
when 'ActiveSupport::JSON'
define_parser do |body|
unless body.nil? or body.empty?
result = ActiveSupport::JSON.decode(body)
raise ActiveSupport::JSON.backend::ParseError if String === result
result
end
end
end
end
end
66 changes: 66 additions & 0 deletions lib/faraday_stack/response_middleware.rb
@@ -0,0 +1,66 @@
module FaradayStack
# A base class for middleware that parses responses
class ResponseMiddleware < Faraday::Response::Middleware
CONTENT_TYPE = 'Content-Type'.freeze

# Executes a block which should try to require and reference dependent libraries
def self.dependency
yield
rescue LoadError, NameError => error
self.load_error = error
end

class << self
attr_accessor :parser
end

# Stores a block that receives the body and should return a parsed result
def self.define_parser(&block)
@parser = block
end

def self.inherited(subclass)
super
subclass.load_error = self.load_error
subclass.parser = self.parser
end

def initialize(app = nil, options = {})
super(app)
@content_types = Array(options[:content_type])
end

# Override this to modify the environment after the response has finished.
def on_complete(env)
if process_response_type?(response_type(env))
env[:body] = parse(env[:body])
end
end

# Parses the response body and returns the result.
# Instead of overriding this method, consider using `define_parser`
def parse(body)
if self.class.parser
begin
self.class.parser.call(body)
rescue
raise Faraday::Error::ParsingError, $!
end
else
body
end
end

def response_type(env)
type = env[:response_headers][CONTENT_TYPE].to_s
type = type.split(';', 2).first if type.index(';')
type
end

def process_response_type?(type)
@content_types.empty? or @content_types.any? { |pattern|
Regexp === pattern ? type =~ pattern : type == pattern
}
end
end
end
12 changes: 12 additions & 0 deletions lib/faraday_stack/response_xml.rb
@@ -0,0 +1,12 @@
module FaradayStack
class ResponseXML < ResponseMiddleware
dependency do
require 'nokogiri'
Nokogiri::XML
end

define_parser do |body|
Nokogiri::XML body
end
end
end
72 changes: 72 additions & 0 deletions test/response_middleware_test.rb
@@ -0,0 +1,72 @@
require 'test_helper'

class ResponseMiddlewareTest < Test::Unit::TestCase
def setup
@json_handler = FaradayStack::ResponseJSON
@conn = Faraday.new do |b|
b.use @json_handler
b.adapter :test do |stub|
stub.get('json') { [200, {'Content-Type' => 'application/json; charset=utf-8'}, "[1,2,3]"] }
stub.get('blank') { [200, {'Content-Type' => 'application/json'}, ''] }
stub.get('nil') { [200, {'Content-Type' => 'application/json'}, nil] }
stub.get('bad_json') { [200, {'Content-Type' => 'application/json'}, '<body></body>']}
stub.get('non_json') { [200, {'Content-Type' => 'text/html'}, '<body></body>']}
end
end
end

def process_only(*types)
@conn.builder.swap @json_handler, @json_handler, :content_type => types
end

def test_uses_json_to_parse_json_content
response = @conn.get('json')
assert response.success?
assert_equal [1,2,3], response.body
end

def test_uses_json_to_parse_json_content_conditional
process_only('application/json')
response = @conn.get('json')
assert response.success?
assert_equal [1,2,3], response.body
end

def test_uses_json_to_parse_json_content_conditional_with_regexp
process_only(%r{/(x-)?json$})
response = @conn.get('json')
assert response.success?
assert_equal [1,2,3], response.body
end

def test_uses_json_to_skip_blank_content
response = @conn.get('blank')
assert response.success?
assert_nil response.body
end

def test_uses_json_to_skip_nil_content
response = @conn.get('nil')
assert response.success?
assert_nil response.body
end

def test_uses_json_to_raise_Faraday_Error_Parsing_with_no_json_content
assert_raises Faraday::Error::ParsingError do
@conn.get('bad_json')
end
end

def test_non_json_response
assert_raises Faraday::Error::ParsingError do
@conn.get('non_json')
end
end

def test_non_json_response_conditional
process_only('application/json')
response = @conn.get('non_json')
assert_equal 'text/html', response.headers['Content-Type']
assert_equal '<body></body>', response.body
end
end
2 changes: 2 additions & 0 deletions test/test_helper.rb
@@ -0,0 +1,2 @@
require 'test/unit'
require 'faraday_stack'

0 comments on commit 82d3b15

Please sign in to comment.