Permalink
Browse files

TEH ÜBER STACK

  • Loading branch information...
0 parents commit 82d3b15c586202a48baff688947695b902c1dd71 @mislav committed Mar 26, 2011
@@ -0,0 +1 @@
+require 'faraday_stack'
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
+require 'test/unit'
+require 'faraday_stack'

0 comments on commit 82d3b15

Please sign in to comment.