Skip to content

Instantly share code, notes, and snippets.

@zenhob
Created April 4, 2014 03:57
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zenhob/9967821 to your computer and use it in GitHub Desktop.
Save zenhob/9967821 to your computer and use it in GitHub Desktop.
Starter Ruby client for your REST API, based on Faraday
#
# This module is a starter Ruby client for your REST API, based on Faraday:
# https://github.com/lostisland/faraday/
#
# It supports HTTP basic authentication, sending and recieving JSON, and simple error reporting.
# I use this as a basis for building API clients in most of my Ruby projects.
#
# Use it like this:
#
# http = GenericApi.connect('http://localhost:3000/', 'myname', 'secret')
# http.get('boxes.json').body.each do |box|
# puts "found a box named #{box[:name]}! Let's put something inside."
# http.post('boxes/#{box[:id]}.json', contents: 'something!')
# end
#
# A few hints for getting started:
#
# This uses Patron as the HTTP client but you can change those constants to use
# another adapter or set to nil to use the built-in default.
#
# The response body is a Hash with Symbol keys (not String!).
# If you're using ActiveSupport, you should use a hash with indifferent access instead.
#
# The default MIME type is appropriate for most purposes, but some APIs use it for
# versioning so bear that in mind and change MIME_TYPE if necessary.
#
# Warmest regards,
#
# Zack Hobson <zack@zackhobson.com>
#
require 'faraday'
module GenericApi
FARADAY_ADAPTER = :patron
REQUIRE_DEPENDENCIES = ['patron']
MIME_TYPE = 'application/json'
# Connect and return a `Faraday::Connection` instance:
# http://rdoc.info/github/lostisland/faraday/Faraday/Connection
#
# If login and password are provided, the connection will use basic auth.
def self.connect url, login=nil, password=nil
Faraday.new(url) do |f|
f.use :generic_api, login, password
f.adapter(FARADAY_ADAPTER || Faraday.default_adapter)
end
end
class Middleware < Faraday::Request::BasicAuthentication
Faraday::Middleware.register_middleware generic_api: ->{ self }
dependency do
require 'yajl'
(REQUIRE_DEPENDENCIES||[]).each {|dep| require dep }
end
def call(env)
# encode with and accept json
env[:request_headers]['Accept'] = MIME_TYPE
env[:request_headers]['Content-Type'] = MIME_TYPE
env[:body] = Yajl::Encoder.encode(env[:body])
# response processing
super(env).on_complete do |env|
begin
env[:body] = Yajl::Parser.parse(env[:body], symbolize_keys:true)
# XXX or, if you're using ActiveSupport:
# env[:body] = Yajl::Parser.parse(env[:body]).with_indifferent_access
rescue Yajl::ParseError
raise ApiError.new("Unable to parse the response:\n#{env[:body]}", env)
end
case env[:status]
when 300..399
raise RedirectError.new(env[:body][:message], env)
when 400..499
raise AuthError.new(env[:body][:message], env)
when 500..599
raise ApiError.new(env[:body][:message], env)
end
end
end
end
class ApiError < Faraday::ClientError
def initialize(message, response)
super("Upstream API failure: #{message||'Internal error.'}", response)
end
end
class AuthError < Faraday::ClientError
def initialize(message, response)
super("Authentication failure: #{message||'No reason given.'}", response)
end
end
class RedirectError < Faraday::ClientError
def initialize(message, response)
super("Redirected: #{message||'No reason given.'}", response)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment