This gem provides a simple client for Easyship, offering accessing to Easyship's shipping, tracking, and logistics services directly from Ruby applications.
Before you begin, ensure you have met the following requirements:
- Ruby version 3.2.0 or newer. You can check your Ruby version by running
ruby -v. - Bundler installed. You can install Bundler with
gem install bundler.
Install the gem and add to the application's Gemfile by executing:
$ bundle add easyship
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install easyship
To use the easyship gem in your Ruby application, you need to configure it with your API key and then you can start making requests to Easyship's services.
First, configure the gem with your Easyship API key:
If you have to use in a ruby file:
require 'easyship'
Easyship.configure do |c|
c.url = 'api_url'
c.api_key = 'your_easyship_api_key'
endIf you have to use in Rails:
- Add to
Gemfile
gem 'easyship'-
Run
bundle i -
Create a new file in
config/initializersdirectory
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
endConfiguration supports the next keys: url, api_key, per_page, requests_per_second, requests_per_minute, headers, logger.
The gem includes secure, professional logging capabilities that are quiet by default. Logging is designed to be abstract and never exposes sensitive data like API keys, headers, or request/response bodies.
Default Behavior (Quiet & Secure):
# By default, logging is disabled (NullLogger)
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
# No logging output - completely silent
endUsing Rails Logger:
# In config/initializers/easyship.rb
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
config.logger = Rails.logger
endUsing Custom Logger:
require 'logger'
custom_logger = Logger.new(STDOUT)
custom_logger.level = Logger::INFO
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
config.logger = custom_logger
endLog File Output:
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
config.logger = Logger.new('log/easyship.log')
endExample Log Output:
[Easyship] GET /2023-01/account
[Easyship] Response 200 (0.245s)
[Easyship] POST /2023-01/shipments
[Easyship] Response 201 (0.432s)
[Easyship] Error: InvalidTokenError - Invalid API token | Context: {:method=>:get, :path=>"/2023-01/account"}
Extensibility: You can extend logger module for custom logging needs:
log_info(logger, message)- General info logginglog_debug(logger, message)- Debug logginglog_warn(logger, message)- Warning logginglog_error(logger, error, context)- Error with contextlog_request(logger, method, path)- Request logginglog_response(logger, status, duration)- Response logging
Easyship::Client supports the next methods: get, post, put, patch, delete.
Easyship::Client.instance.get('/2023-01/account')To make post request:
payload = {
origin_country_alpha2: "SG",
destination_country_alpha2: "US",
tax_paid_by: "Recipient",
is_insured: false,
items: [
{
description: "Silk dress",
sku: "1002541",
actual_weight: 1.2,
height: 10,
width: 15,
length: 20,
category: "fashion",
declared_currency: "SGD",
declared_customs_value: 100
}
]
}
Easyship::Client.instance.post('/2023-01/shipment', payload)When using the easyship gem in a Rails application, it's important to handle potential errors that may arise during API calls. Here's how you can handle errors gracefully:
- Wrap your API calls in a
begin-rescueblock. - Catch specific errors from the
easyshipgem to handle them accordingly.
For example:
begin
Easyship::Client.instance.post('/2023-01/shipment', payload)
rescue Easyship::Errors::RateLimitError => e
Rails.logger.error("Easyship Error: #{e.message}")
endEach error instance provides these methods:
message: Returns the error message text.body_error: Returns an error details (Deprecated).error: Returns a hash containing original detailed error object from Easyship.response_body: Returns the full response body from the API.response_headers: Returns the HTTP headers from the API response.
The get method in the Easyship::Client class is designed to support pagination seamlessly when interacting with the Easyship API by passing block of code. This method abstracts the complexity of managing pagination logic, allowing you to retrieve all items across multiple pages with a single method call.
Suppose you want to retrieve a list of shipments that may span multiple pages. Here's how you can use the get method:
shipments = []
Easyship::Client.instance.get('/2023-01/shipments') do |page|
shipments.concat(page[:shipments])
end
shipments # Returns all shipments from all pagesTo setup items per page, use the key per_page in your configuration.
For Example:
# Global defaults
Easyship.configure do |config|
config.per_page = 100
end
# Per-call overrides (any of these are supported)
shipments = []
Easyship::Client.instance.get('/2023-01/shipments', {
per_page: 50,
}) do |page|
shipments.concat(page[:shipments])
endRate limiting during pagination:
- The cursor has no default rate limiting; it uses nil to indicate that rate limiting is disabled.
- Use Rate limiting documentation for more details which values to set.
- You can override the limits per call, by passing
requests_per_secondandrequests_per_minute.
Examples:
# Global defaults
Easyship.configure do |config|
config.requests_per_second = 10
config.requests_per_minute = 60
end
# Per-call overrides (any of these are supported)
shipments = []
Easyship::Client.instance.get('/2023-01/shipments', {
requests_per_second: 5,
requests_per_minute: 40,
}) do |page|
shipments.concat(page[:shipments])
endYou can pass custom headers both globally (via configuration) and per-request.
Global Headers (via Configuration):
Easyship.configure do |config|
config.url = 'api_url'
config.api_key = 'your_easyship_api_key'
config.headers = {
'X-Custom-Header' => 'custom-value'
}
endPer-Request Headers:
# Override or add headers for a specific request
Easyship::Client.instance.get('/2023-01/account', {}, headers: {
'X-Request-ID' => 'unique-request-id'
})
# POST with custom headers
Easyship::Client.instance.post('/2023-01/shipment', payload, headers: {
'X-Idempotency-Key' => 'unique-key'
})
# PUT with custom headers
Easyship::Client.instance.put('/2023-01/shipment/123', payload, headers: {
'X-Custom-Header' => 'value'
})
# PATCH with custom headers
Easyship::Client.instance.patch('/2023-01/shipment/123', payload, headers: {
'X-Custom-Header' => 'value'
})
# DELETE with custom headers
Easyship::Client.instance.delete('/2023-01/shipment/123', {}, headers: {
'X-Custom-Header' => 'value'
})Note: Per-request headers will override global headers if the same header key is used.
Idempotency keys are essential for safely retrying requests without accidentally performing the same operation twice. This is particularly important for POST requests that create resources (like shipments, orders, or payments).
Example - Creating a Shipment with Idempotency:
# Generate a unique key (e.g., UUID, database record ID, or any unique identifier)
idempotency_key = SecureRandom.uuid
payload = {
origin_country_alpha2: "SG",
destination_country_alpha2: "US",
tax_paid_by: "Recipient",
is_insured: false,
items: [
{
description: "Product",
actual_weight: 1.2,
height: 10,
width: 15,
length: 20,
declared_currency: "USD",
declared_customs_value: 50
}
]
}
begin
response = Easyship::Client.instance.post(
'/2023-01/shipments',
payload,
headers: { 'X-Idempotency-Key' => idempotency_key }
)
# If this request fails due to network issues and you retry with the same key,
# the API will return the same shipment without creating a duplicate
rescue Easyship::Errors::EasyshipError => e
# Safe to retry with the same idempotency_key
retry
endBest Practices:
- Use unique, random keys (UUIDs are recommended)
- Store the idempotency key with your order/shipment record in your database
- Reuse the same key when retrying a failed request
- Don't reuse keys across different operations or resources
- Keys typically expire after 24 hours (check API documentation)
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. Then, eun rake rubocop to run the rubocop. 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 the created tag, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/mmarusyk/easyship. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct. You can find a list of contributors in the CONTRIBUTORS.md file.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Easyship project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.