= Remote API

Rubyforge Project: http://rubyforge.org/projects/remote-api/

Installation: <tt>gem install remote_api</tt>

== Overview

+RemoteAPI+ is an abstraction from API classes. I noticed that there was very overlapping
functionality in the various API wrappers I had going. This gem will make it much easier to
write an API wrapper by taking care of the common stuff. All you have to do is write the
parts that are specific to the API you are talking to.

=== Usage and Checklist

To use this gem you should follow this simple checklist of things that you must implement on
your own.

1. Create a class that descends from RemoteAPI: <tt>class Foo < RemoteAPI; end</tt>
2. Define the url to access with the url dsl_accessor <tt>url 'some url here'</tt>
3. Add a +request+ method to your class. This should return the body of your request.
4. Add a +assert_response+ method. This method should raise exceptions if the contents
of <tt>@response</tt> describe a fatal error.
5. Add a +process+ method. This parses the API response. It should pull the relevant
data out of the <tt>@response</tt> object and put it in instance variables.
6. Create <tt>attr_reader</tt>s for the instance variables from the +process+ method that
you want to expose as methods.

=== Basic Walkthrough

I'm going to explain just what happens under the hood of a class that inherits from RemoteAPI.

So lets say that I am writing an API to ship a package. It requires address and weight, each
on their own line in a YAML like format. On success, it returns the tracking number of the
package. On failure it just says "There was an error!".

Here is an API class that can handle that:

require 'remote_api'

class Shipment < RemoteAPI
attr_reader :tracking_number
url 'http://test.com/shipments'

def request
<<-EOF
address: #@address
weight: #@weight
EOF
end

def assert_success
raise ResponseFailure if @response =~ /error/
end

def process
@tracking_number = @response
end

end

All the magic happens on instantiation. The request is built and sent, and the response is
processed. It all starts with:

Shipment.new(:address => '123 Fake St', :weight => 12)

==== Instantiation

The hash passed to +new+ is converted to instance variables. In this case the Shipment instance
now has <tt>@address</tt> and <tt>@weight</tt> instance variables in it.

==== Building the request

The next step is the +request+ method is called. This should return a string, something that can
be converted to a sttring. This string will be sent out as the request body to the remote server.

==== Sending the request

Now that we have built the request, the connection is made to the remote server at the url defined
by the dsl_accessor +url+. The request is sent with the result of the +request+ method as the body.
What the server returns is stored in the <tt>@response</tt> variable. This all happens automatically.

==== Assertion of Success

Most API's have some sort of error reporting. The <tt>assert_success</tt> method is for raising
exceptions if the API reports any errors. The ResponseFailure exception is built into RemoteAPI.
Parse the response to see if it reports errors.

==== Processing the response

You must deinfe a +process+ method. This method will parse the response and stuff any needed data into
instance varibales. In this case, we are simply getting the entire response since that will be the
only tracking number that we are expecting.

==== Using the result

Define a <tt>attr_reader</tt> for each instance variable from the +process+ you want exposed. Then use
your new object as you would any other object

pkg = Shipment.new(:address => '123 Fake St', :weight => 12)
puts "Sucess! Your tracking number is #pkgpkg.tracking_number"

==== Using the result with Exception handling

You probably don't want your whole app to crash if there is an API error. This is why ruby has exception
handling

begin
pkg = Shipment.new(:address => '123 Fake St', :weight => 'foo')
rescue RemoteAPI::ResponseFailure
puts "Unable to ship that package. The API really didn't like it."
end