Objectify the API: A tip for Rubyists on the web.

Imagine if Ruby were “the language of the web” instead of JavaScript. We could all be sending around messages between our Ruby APIs in RON (Ruby Object Notation). Using any web service would be as easy as working with a new gem. Everyone would be happy and no one would ever cry. Of course, this monolingual utopia is just a Rubyist’s pipe dream. Reality is not so kind…

If your app talking to other apps, it’s using JavaScript. The standard response format for a web API call is JavaScript Object Notation, or JSON. Introducing such a response into your application is like being on a space shuttle and opening the door to outer space. Anything that goes in or out must first go through an airlock to make sure it is prepared for it’s new environment.

The airlock here is your API client, a Ruby class whose sole responsibility is to create a seamless connection between your app and the “outer space” of the world wide web. The moment JSON comes in, objectify it. That is, turn it into a Ruby object we know and love. HTTP response objects do not count, as they are just wrappers for JSON.

I recently made an app for a hackathon that uses the BandsInTown API to get information on music artists. I was dealing with responses sort of like this:

{
"id": 4189861,
"title": "Nas @ UB Stadium in Buffalo, NY",
"datetime": "2011-04-29T19:00:00",
"formatted_datetime": "Friday, April 29, 2011 at 7:00pm",
"formatted_location": "Buffalo, NY",
"ticket_url": "http://www.bandsintown.com/event/4189861/buy_tickets?artist=Nas",
"ticket_type": "Tickets",
"ticket_status": "available",
"on_sale_datetime": "2011-03-08T10:00:00",
"facebook_rsvp_url": "http://www.bandsintown.com/event/4189861?artist=Nas",
"description": "2011 Block Party: featuring Kid Cudi, Damian Marley, Nas & Spec. Guest",
"artists": [{
"name": "Nas",
"mbid": "cfbc0924-0035-4d6c-8197-f024653af823",
"image_url": "http://www.bandsintown.com/Nas/photo/medium.jpg",
"thumb_url": "http://www.bandsintown.com/Nas/photo/small.jpg",
"facebook_tour_dates_url": "http://bnds.in/e5CP5L",
"tracker_count": "367714"
}],
"venue": {
"name": "UB Stadium",
"place: "UB Stadium".
"city": "Buffalo",
"region": "NY",
"country": "United States",
"latitude": 43.0004710,
"longitude" : -78.7802170
}
}

In my BandsInTown client class, I defined a get_artist_params method:

class ArtistAgent
# ..
def get_artist_params(artist_name)
uri = "#{URI_START}#{artist_name}#{URI_END}"
uri = URI.encode(uri)
json = get(uri)
json_to_params(json)
end
end

The “get” method returns the body of the HTTP response, by the way. And json_to_params looks something like this:

class ArtistAgent
# ..
  def json_to_params(json)
json = JSON.parse(json)
{ name: json['name'],
image_url: json['image_url'],
thumb_url: json['thumb_url'] }
end
end

Finally, I define a class method on the Artist class itself that handles instantiation.

class Artist

@@agent = ArtistAgent.new
  # ..
  def self.from_name(artist_name)
params = @@agent.get_artist_params(artist_name)
Artist.name(params)
end

Now, if I ever need more parameters out of the JSON, I only need to change the “json_to_params” method, and everything else stays the same. Making a new Artist object based on a user sending in a name is as easy as:

artist = Artist.from_name("Elvis")

What’s important here is that none of my classes know a lick about JSON, except for the client. If your client is set up correctly, you can pretend the JSON isn’t even there. Feels good, doesn’t it?

Like what you read? Give Christian Carey a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.