FreightKit
This library interfaces with the web services of various shipping carriers. The goal is to abstract the features that are most frequently used into a pleasant and consistent Ruby API.
FreightKit supports:
- Downloading scanned documents
 - Finding shipping rates
 - Tracking shipments
 
On a technical level it supports:
- Abstracted accessorials
 - Abstracted tracking events
 - Cubic feet and density calculations
 - Freight class calculations (and manual overriding)
 
Definitions
Carrier: Has unique web services pertaining to whatever real-world services they provide.
Platform: Provides web-accessible services for many carriers at once.
Note: Carriers may extend Platforms and override them when their behavior differs from the Platform.
Plug-in System
FreightKit relies on plug-ins (gems) to define how it connects to individual Carriers and Platforms.
Installation
Using bundler, add to the Gemfile:
gem 'freight_kit'
Or standalone:
$ gem install freight_kit
Note: Plug-ins are required to connect to Carriers and Platforms (see above).
Standard Usage
Start off by initializing the Carrier provided by a Carrier plug-in:
require 'freight_kit'
# Typically just one `Credential` is required
credentials = [
  FreightKit::Credential.new(
    type: :api,
    account: 'account_number',
    username: 'username',
    password: 'password',
    tariff: FreightKit::Tariff.new # optional
  ),
  FreightKit::Credential.new(
    type: :oauth2,
    access_token: 'token',
    expires_at: DateTime.current + 1.day, # DateTime
    scope: 'scope'
  ),
  FreightKit::Credential.new(
    type: :website,
    username: 'username',
    password: 'password'
  ),
  FreightKit::Credential.new(
    type: :selenoid,
    base_url: URI.parse('http://domain:4444'),
    browser: :chrome
  )
]
carrier = FreightKit::SCAC.new(credentials)
Documents
carrier.bol(tracking_number) # BOL generated by carrier
carrier.scanned_bol(tracking_number) # BOL scanned by carrier
carrier.pod(tracking_number)
Tracking
tracking = carrier.find_tracking_info(tracking_number)
tracking.delivered?
tracking.status
tracking.shipment_events.each do |event|
  puts "#{event.name} at #{event.location.city}, #{event.location.state} on #{event.time}. #{event.}"
end
Quoting
packages = [
  FreightKit::Package.new(
    371 * 16, # 371 lbs
    {
      length: 40, # inches
      width: 48,
      height: 47
    },
    units: :imperial
  ),
  FreightKit::Package.new(
    371 * 16, # 371 lbs
    {
      length: 40, # inches
      width: 48,
      height: 47
    },
    freight_class: 125, # override calculated freight class
    units: :imperial
  )
]
origin = FreightKit::Location.new(
  country: 'US',
  state: 'CA',
  city: 'Los Angeles',
  zip: '90001'
)
destination = FreightKit::Location.new(
  country: 'US',
  state: 'IL',
  city: 'Chicago',
  zip: '60007'
)
accessorials = %i[
  appointment_delivery
  liftgate_delivery
  residential_delivery
]
response = carrier.find_rates(origin, destination, packages, accessorials: accessorials)
rates = response.rates
rates = response.rates.sort_by(&:price).collect { |rate| [rate.service_name, rate.price] }