Skip to content
/ eezee Public
forked from linqueta/eezee

The easiest HTTP client for Ruby

License

Notifications You must be signed in to change notification settings

petlove/eezee

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Eezee sounds like "Easy"

Gem Version Build Status Maintainability Test Coverage

The easiest HTTP client for Ruby!

With Eezee you can do these things:

  • Define external services in an initializer file and use them through a simple method
  • Take HTTP requests just extending a module and call the HTTP request method in your class/module
  • Set before and after hooks to handle your requests, responses, and errors
  • Handle all requests, responses, and errors in the same way
  • Log the request, response, and errors
  • Set general request timeout and open connection timeout
  • Raise errors in failed requests
  • Spend more time coding your API integrations instead defining and testing HTTP settings and clients

This gem is supported for Ruby 2.6+ applications.

Table of Contents

Getting started

Installation

Add this line to your application's Gemfile:

gem 'eezee'

If you're on Rails you can run this line below to create the initializer:

rails generate eezee:install

Supported HTTP Methods

Eezee supports these HTTP methods:

  • GET
  • POST
  • PATCH
  • PUT
  • DELETE

And here are the corresponding Eezee's HTTP methods:

  • get(request_options)
  • post(request_options)
  • patch(request_options)
  • put(request_options)
  • delete(request_options)

OBS: The param request_options is optional.

How to take a request

To take a request using any of these methods you just have to call the HTTP method, and if you want, you can pass the options.

module RickMorty::Resource::Character
  extend Eezee::Client

  def self.index
    get(url: 'rickandmortyapi.com/api', protocol: :https, path: 'character')
  end

  def self.find(id)
    get(
      url: 'rickandmortyapi.com/api',
      protocol: :https,
      path: 'character/:character_id',
      params: { character_id: id }
    )
  end

  def self.create(payload)
    post(
      url: 'rickandmortyapi.com/api',
      protocol: :https,
      path: 'character',
      payload: payload
    )
  end

  def self.update(id, payload)
    post(
      url: 'rickandmortyapi.com/api',
      protocol: :https,
      path: 'character/:character_id',
      params: { character_id: id }
      payload: payload
    )
  end

  def self.destroy(id)
    delete(
      url: 'rickandmortyapi.com/api',
      protocol: :https,
      path: 'character/:character_id',
      params: { character_id: id }
    )
  end

Request options

Request options are the request settings. They can be used to define services, request options and as a param when you take the HTTP request. For example:

module RickMorty::Resource::Character
  extend Eezee::Client

  eezee_request_options protocol: :https,
                        url: 'rickandmortyapi.com/api'
                        path: 'character/:character_id'

  def self.index
    get
  end

  def self.find(id)
    get(params: { character_id: id })
  end

  def self.update(id, payload)
    put(
      params: { character_id: id },
      payload: payload,
      after: ->(_req, res) { do_something!(res) }
    )
  end
end

The method eezee_request_options can receive all of Available Request options.

When the HTTP methods were called, Eezee has created a Request setting with the options defined in the module and merge with the options passed as a param in the HTTP methods.

Available Request options

Here are the list of available options and about them:

Option Required Default What is it? Example
url Yes nil The request's url "rickandmortyapi.com/api"
protocol No nil The request's protocol :https
path No nil The resource's path "characters\:characted_id\addresses"
headers No {} The request's headers. { Token: "Bearer 1a8then..." }
params No {} The query params. If the url or path has a nested param like :character_id and you pass it in the hash, this value will be replaced. In the opposite, the value will be concatenated in the url like ...?character_id=10&... { character_id: 10 }
payload No {} The request's payload { name: "Linqueta", gender: "male" }
before No nil It's the before hook. You can pass Proc or Lambda to handle the request settings. See more in Hooks. ->(req) { merge_new_headers! }
after No nil It's the after hook. You can pass Proc or Lambda to handle the request settings, response or error after the request. If it returns a valid value (different of false or nil) and the request raises an error, the error won't be raised to your application. See more in Hooks. ->(req, res, err) { do_something! }
timeout No nil If it exceeds this timeout to make whole request Eezee will raise the error Eezee::TimeoutError 5
open_timeout No nil If it exceed this timeout to open a connection Eezee will raise the error Eezee::TimeoutError 2
raise_error No false If you want that Eezee raises an error if the request has wasn't successful. See more in Errors true
logger No false If you want to log the request, response, and error true
url_encoded No false If you want to send request body as form_url_encoded true
preserve_url_params No false The query params will be preserved if the url or path has a nested param like :character_id this value will be not replaced true
ddtrace No nil Support for DataDog apm {}

Services

It's common your app has integrations with many external services and this gem has a feature to organize in one file the settings of these external service integrations and it provides an easy way to get these settings.

For example, I will integrate with Rick and Morty Api using a service:

  • I'll declare it in an initializer file:
Eezee.configure do |config|
  config.add_service :rick_morty_api,
                     protocol: :https,
                     url: 'rickandmortyapi.com/api'
end
  • In my resource, I'll catch the service and pass other settings:
module RickMorty::Resource::Character
  extend Eezee::Client

  eezee_service :rick_morty_api
  eezee_request_options path: 'character/:character_id'

  def self.index
    get
  end

  def self.find(id)
    get(params: { character_id: id })
  end
end

How a service works

When Ruby loads a class/module and it has the method eezee_service declared with a service's name, by default, Eezee will try load the service and create a request base for the class/module, so, when the class/module takes a request, Eezee will create the final request instance based on request base to take the HTTP request. You can turn it lazy setting the option lazy: true, therefore, the final request will be created just in the HTTP request. If the service doesn't exist when Eezee search about it, it will be raised the error Eezee::Client::UnknownServiceError.

About the method add_service, you can pass all of Available Request options. The meaning of this part is to organize in one way the external services integrations.

Request

In Hooks, you always receive the param request and it is an instance of Eezee::Request. Available Request options are the accessors of Eezee::Request, just call for the name, like:

request.protocol
# => :https

request.url
# => "rickandmortyapi.com/api"

Response

In Hooks and the return of the request you have an instance of Eezee::Response. This class can be used for successful and failed requests. Here are all methods you can call from a response:

Name Type What is it?
original Faraday::Response, Faraday::Error, Faraday::TimeoutError, Faraday::ConnectionFailed or Net::ReadTimeout The instance that made the Eezee::Response.
body Hash The body response. It always is an instance of Hash (symbolized). If the response doesn't have a body response, the value will be {}.
success? Boolean (TrueClass or FalseClass) If the request had a timeout error or response has the code 400+ the value will be false, else, the value will be true.
code Integer or NilClass If the request had a timeout error the value will be nil, else, the value will be an integer.
timeout? Boolean (TrueClass or FalseClass) If the request had a timeout error.

Errors

Eezee can raise errors in some situations:

  • When the specified service is unknown
  • When the request got a timeout
  • When the request got a failure response

When the specified service is unknown

  • Eezee::Client::UnknownServiceError

When the request got a timeout

  • Eezee::TimeoutError

Important: This case happens just if the request option raise_error is true.

When the request got a failure response

  • Eezee::RequestError for all errors (ancestor of all below)
  • Eezee::BadRequestError for code equals 400
  • Eezee::UnauthorizedError for code equals 401
  • Eezee::ForbiddenError for code equals 403
  • Eezee::ResourceNotFoundError for code equals 404
  • Eezee::UnprocessableEntityError for code equals 422
  • Eezee::ClientError for code between 400 and 499
  • Eezee::InternalServerError for code equals 500
  • Eezee::ServiceUnavailableError for code equals 503
  • Eezee::ServerError for code between 500 and 599

All of Eezee::RequestError has the accessor @response with an instace of Eezee::Response.

Important: This case happens just if the request option raise_error is true.

Examples

Here are some examples:

Complete integrations

Hooks

The why to use Eezee instead Faraday

So, it's an important part of this README. This gem uses Faraday as the HTTP client and Faraday is an excellent HTTP client, but it brings many difficult, or, in other words, many things could be easier, like:

  • If you work with microservices, you'll create a Faraday Connection setting per ms, or, it's so common to see many Faraday Connection setting in the same project.
  • To raise errors with Faraday you know to set an adapter (it could be easier)
  • When we have a successful response or an error, the way to catch the params (code, body) is completely different
  • Faraday doesn't have any way to set the external services in an initializer, you'll have to create yours
  • At least, it's common in projects the people create files to instantiate Faraday Connection but these people don't test these files

All of these things and others are contemplated by Eezee!

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

License

The gem is available as open source under the terms of the MIT License.

About

The easiest HTTP client for Ruby

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Ruby 99.9%
  • Shell 0.1%