APIClientBase
Abstractions to help author API wrappers in Ruby.
Installation
- Add
api_client_base
as a dependency to your gem's gemspec. require 'api_client_base'
inlib/yourgem.rb
Usage
This gem assumes your gem will follow a certain structure.
- Actions that your gem can perform are done through a class, like
APIWrapper::Client
. - Each action has request and response classes.
- Request class takes care of appending the endpoint's path to the host, preparing params, and specifying the right http method to call
- Response class takes care of parsing the response and making the data easily accessible for consumption.
Configuring the gem's base module
Do this:
module MyGem
include APIClientBase::Base.module
with_configuration do
has :host, classes: String
has :username, classes: String
has :password, classes: String
end
end
And you can
- configure (thanks to gem_config) the gem's base module with defaults:
MyGem.configure do |c|
c.host = "https://test.api.com"
end
- instantiate
MyGem::Client
by callingMyGem.new(host: "https://api.com", username: "user", password: "password")
. If you do not specify an option, it will use the gem's default.
Configuring the Client
Default Options
module MyGem
class Client
# specifying a symbol for `default_opts` will look for a method of that name
# and pass that into the request
HOST = "https://production.com"
include APIClientBase::Client.module(default_opts: :default_opts)
private
def default_opts
# At least include `host`. Other things you may need to include: `token`, `secret`
{ host: HOST, secret: MyGem.configuration.secret }
end
end
class Client
HOST = "https://production.com"
include APIClientBase::Client.module(default_opts: {host: HOST})
end
end
Actions
module MyGem
class Client
include APIClientBase::Client.module(default_opts: :all_opts)
api_action :get_user
# `api_action` basically creates a method like:
# def get_user(opts={})
# request = GetUserRequest.new(all_opts.merge(opts))
# raw_response = request.()
# GetUserResponse.new(raw_response: raw_response)
# end
private
def all_opts
{ host: "http://prod.com" }
end
end
end
api_action
accepts the method name, and optional hash of arguments. These may contain:args
: if not defined, the args expected by the method is nothing, or a hash. If given, it must be an array of symbols. For example, ifargs: [:id, :name]
is given, then the method defined is effectively:def my_action(id, name)
but what is passed into the request object is still{id: "id-value", name: "name-value"}
You still need to create MyGem::GetUserRequest
and MyGem::GetUserResponse
. See the "requests" and "responses" section below.
Requests
Requests assume a REST-like structure. This currently does not play well with a SOAP server. You could still use the Base
, Client
, and Response
modules however. For SOAP APIs, write your own Request class that defines #call
. This method needs to return the raw_response
.
module MyGem
class GetUserRequest
# You must install typhoeus if you use the `APIClientBase::Request`. Add it to your gemfile.
include APIClientBase::Request.module(
# you may define the action by `action: :post`. Defaults to `:get`.
# You may also opt to define `#default_action` (see below)
)
private
def path
# all occurrences of `/:\w+/` in the string will have the matches replaced with the
# request object's value of that method. For example, if `request.user_id` is 33
# then you will get "/api/v1/users/33" as the path
"/api/v1/users/:user_id"
# Or you can interpolate it yourself if you want
# "/api/v1/users/#{self.user_id}"
end
# Following methods are optional. Override them if you need to send something specific
def headers
{"Content-Type" => "application/json"}
end
def body
{secret: "my-secret"}.to_json
end
def params
{my: "params"}
end
def default_action
:post # defaults to :get
end
end
end
Responses
module MyGem
class GetUserResponse
include APIClientBase::Response.module
# - has `#status` method that is delegated to `raw_response.status`
# - has `#code` method to get the response's code
# - has `#raw_response` which is a Typhoeus response object
# You're encouraged to use Virtus attributes to extract information cleanly
attribute :id, Integer, lazy: true, default: :default_id
attribute :name, String, lazy: true, default: :default_name
attribute :body, Object, lazy: true, default: :default_body
private
# Optional: define `#default_success` to determine what it means for
# the response to be successful. For example, the code might be 200
# but you consider it a failed call if there are no results
def default_success
code == 200 && !body[:users].empty?
end
def default_body
JSON.parse(raw_response.body).with_indifferent_access
end
def default_id
body[:id]
end
def default_name
body[:name]
end
end
end
You can an example gem at https://github.com/imacchiato/bridge_client-ruby.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/imacchiato/api_client-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.