public
Description: Simple and straight forward interfaces to the various shipping web services
Homepage: http://rdoc.info/projects/binarylogic/shippinglogic
Clone URL: git://github.com/binarylogic/shippinglogic.git
Click here to lend your support to: shippinglogic and make a donation at www.pledgie.com !
binarylogic (author)
Sun Aug 16 23:04:29 -0700 2009
commit  7852ea7448e6451c8e22ead7885d32cb0d2944e2
tree    833ea3c27ff940b6cb6f4d28d2793e3e3651fe92
parent  89e275fb6ec00c78cb670a63cf2d5e60f752a898
README.rdoc

Shippinglogic

The goal of this library is to provide simple and straight forward interfaces to the various shipping web services: FedEx, UPS, USPS, etc. (Only FedEx is supported at this time)

Helpful links

Before contacting me directly, please read:

If you find a bug or a problem please post it in the issues section. If you need help with something, please use google groups. I check both regularly and get emails when anything happens, so that is the best place to get help. This also benefits other people in the future with the same questions / problems. Thank you.

Install & use

Install the gem from rubyforge:

  sudo gem install shippinglogic

Or from github:

  sudo gem install binarylogic-shippinglogic

Now just include it in your project and you are ready to go.

You can also install this as a plugin:

  script/plugin install git://github.com/binarylogic/shippinglogic.git

See below for usage examples.

Simple tracking example

What I think is unique about this library is it’s usage / syntax:

  fedex = Shippinglogic::FedEx.new(key, password, account, meter)
  tracking_details = fedex.track(:tracking_number => "XXXXXXXXXXXXXXXXX")
  # => A proxy object that delegates calls to an array of Shippinglogic::FedEx::Track::Event objects

  # this shows that the tracking object is a proxy for the underlying array
  tracking_details.tracking_number
  # => "XXXXXXXXXXXXXXXXX"

  tracking_details.tracking_number = "YYYYYYYYYYYYYYYYYY"
  # => "YYYYYYYYYYYYYYYYYY"

  tracking_details.class
  # => Shippinglogic::FedEx::Track::Details

  tracking_details.status
  # => "Delivered"

  tracking_details.signature_name
  # => "KKING"

  tracking_details.events.first
  # => #<Shippinglogic::FedEx::Track::Event @postal_code="95817", @name="Delivered", @state="CA", @residential=false,
  #    @city="Sacramento", @type="DL", @country="US", @occured_at=Mon Dec 08 10:43:37 -0500 2008>

  tracking_details.first.name
  # => "Delivered"

Calls to the web services are lazy

In our above example, you will notice we are able to access attributes, while at the same time able to treat the object as an array. That’s because the object is not actually an array, it acts as a proxy for the underlying array.

That being said, a request is not sent to FedEx until we need to deal with the underlying array. Meaning it’s lazy, which is more efficient. Most would think the request to FedEx was sent when we initialized the object. This is not the case. The request to FedEx was sent when we executed "tracking.first".

This is similar to how ActiveRecord’s association proxies work. When you call "user.orders" no database activity occurs until you actually use the object (Ex: user.orders.each).

Flexibility

You will notice above we assign the result of the ‘track’ method to a variable called ‘tracking’. That object has more to it:

  # Initializing
  tracking_details = fedex.track(:tracking_number => "XXXXXXXXXXXXX")
  tracking_details.tracking_number
  # => "XXXXXXXXXXXXX"

  # Attribute accessors
  tracking_details.tracking_number = "YYYYYYYYYYYYYYY"
  tracking_details.tracking_number
  # => "YYYYYYYYYYYYYYY"

  # Mass attribute setting
  tracking_details.attributes = {:tracking_number => "ZZZZZZZZZZZZZZZZ"}
  tracking_details.tracking_number
  # => "ZZZZZZZZZZZZZZZZ"

  tracking_details.attributes
  # => {:tracking_number => "ZZZZZZZZZZZZZZZZ"}

Available services and their features

This library is still very new, as a result only FedEx is supported at this time. More will come.

I spent a lot of time on the documentation, for examples of how to use each service please see the docs for their respective classes.

FedEx

  1. Tracking - See Shippinglogic::Fedex::Track
  2. Signature proof of delivery - See Shippinglogic::Fedex::Signature
  3. Getting service rates - See Shippinglogic::Fedex::Rate
  4. Creating shipments w/ labels - See Shippinglogic::Fedex::Ship
  5. Canceling shipments - See Shippinglogic::Fedex::Cancel

Add your own services

Simply fork the project and make your changes. If you want to add support for a new service it is very straight forward. Checkout the code in Shippinglogic::Fedex::Track, it very simple and easy to follow. It’s a great place to start because its the simplest of services.

Interface integration

What’s nice about having an object is that you can pass it around. Let’s say you wanted to add simple FedEx tracking functionality to your app:

  class TrackingController < ApplicationController
    def new
      @tracking_details = fedex.track(params[:tracking])
    end

    def create
      @tracking_details = fedex.track(params[:tracking])
      render :action => :new if !@tracking.successful?
    end

    private
      def fedex
        @fedex ||= Shippinglogic::FedEx.new(key, password, account, meter)
      end
  end

That’s pretty simple. Now check out your form:

  # new.html.haml
  - form_for @tracking_details do |f|
    = f.error_messages
    = f.text_field :tracking_number
    = f.submit "Track"

Then your results:

  # create.html.haml
  .signature_name= @tracking_details.signature_name
  - @tracking_details.events.each do |event|
    .event
      .name= event.name
      .occured_at= event.occured_at.to_s(:long)
      .location== #{event.city}, #{event.state} #{event.postal_code}, #{event.country}
      .residential= event.residential ? "Yes" : "No"

Leave abstraction to your application

Here is what I did in an application of mine and it worked out great. I also have complete control of what I’m doing and the library is not limiting me:

  class Shipment < ActiveRecord::Base
    class Service
      attr_accessor :carrier, :name, "delivered_by, :rate
    end

    def services
      @services ||= fedex_services # + some_other_services
    end

    private
      def fedex_services
        rate_options = {} # replace me with your own options accepted by Shippinglogic::FedEx::Rate
        fedex.rate(rate_options).collect do |rate|
          service = Service.new
          service.carrier = :fedex
          serivce.name = rate.name
          service.rate = rate.rate
          service.delivered_by = rate.delivered_by
          service
        end
      end
  end

Copyright

Copyright © 2009 Ben Johnson of Binary Logic, released under the MIT license