It's an HTTParty and Everyone Is Invited!
July 29th, 2008
So I’ve made a boatload of gems that consume web services (twitter, lastfm, magnolia, delicious, google reader, google analytics). Each time I get a bit better at it and learn something new. I pretty much am only interested in consuming restful api’s and over time I’ve started to see a pattern. The other day I thought, wouldn’t it be nice if that pattern were wrapped up as a present for me (and others) to use? The answer is yes and it is named HTTParty.
The Pattern
Every web service related gem I’ve written makes requests and parses responses into ruby objects. So first let’s start with requests. The request methods that you make the most use of are get and post, with put and delete occasionally sliding in. I don’t know about you but I constantly forget how to use net/http. First, make the http, then the request. Is it get or post? How do I get the response body? Maybe I’m forgetful, but I always needed to have net/http’s rdoc open when working with it. Not anymore, though, cause now I just HTTParty like it’s 1999.
A Princely Example
To find something that I haven’t written a gem for, I hopped over to programmable web’s API directory. I chose Who is my representative’s API because, frankly, I didn’t know it existed and it was really basic. So let’s say we want to get who my rep is:
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
end
puts Representative.get('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
# "<result n='1' ><rep name='Joe Donnelly' state='IN' district='2' phone='(202) 225-3915' office='1218 Longworth' link='http://donnelly.house.gov/' /></result>"
Yep, that is it. Include HTTParty and you are good to go. So, that was easy. Now we can make requests, but our response was just plain old xml. We want ruby objects! Let’s require rexml or install hpricot or libxml-ruby next, right? Wrong! Just set the format.
Automatically Parse XML
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
format :xml
end
puts Representative.get('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
# {"result"=>{"n"=>"1", "rep"=>{"name"=>"Joe Donnelly", "district"=>"2", "office"=>"1218 Longworth", "phone"=>"(202) 225-3915", "link"=>"http://donnelly.house.gov/", "state"=>"IN"}}}
Yep, I’m not joking. That works, but let’s wrap things up a little bit and make a prettier API for the developer that will eventually use our Representative library.
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
format :xml
def self.find_by_zip(zip)
get('http://whoismyrepresentative.com/whoismyrep.php', :query => {:zip => zip})
end
end
puts Representative.find_by_zip(46544).inspect
# {"result"=>{"n"=>"1", "rep"=>{"name"=>"Joe Donnelly", "district"=>"2", "office"=>"1218 Longworth", "phone"=>"(202) 225-3915", "link"=>"http://donnelly.house.gov/", "state"=>"IN"}}}
There, that is a little better. One simple module include (HTTParty) and we can make requests and automatically get our xml responses parsed. Not to mention it’s so easy my mom could do it. What? Oh, you want to see JSON. Sure, no problem.
Automatically Parse JSON
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
format :json
def self.find_by_zip(zip)
get('http://whoismyrepresentative.com/whoismyrep.php', :query => {:zip => zip, :output => 'json'})
end
end
puts Representative.find_by_zip(46544).inspect
# {"results"=>[{"name"=>"Joe Donnelly", "district"=>"2", "office"=>"1218 Longworth", "phone"=>"(202) 225-3915", "link"=>"http://donnelly.house.gov/", "state"=>"IN"}]}
Holla! You thought you had me but you didn’t. Let’s make our example a little bit more complicated and add another method to get all the reps by name.
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
format :json
def self.find_by_zip(zip)
get('http://whoismyrepresentative.com/whoismyrep.php', :query => {:zip => zip, :output => 'json'})
end
def self.get_all_by_name(last_name)
get('http://whoismyrepresentative.com/getall_reps_byname.php', :query => {:lastname => last_name, :output => 'json'})
end
end
puts Representative.get_all_by_name('Donnelly').inspect
# {"results"=>[{"district"=>"2", "last"=>"Donnelly", "first"=>"Joe", "state"=>"IN", "party"=>"D"}]}
Notice any problems with that? I do. I’m repeating the domain and the output format in each request. Let’s fix that.
Helpers To DRY Things Up
require 'rubygems'
require 'httparty'
class Representative
include HTTParty
base_uri 'whoismyrepresentative.com'
default_params :output => 'json'
format :json
def self.find_by_zip(zip)
get('/whoismyrep.php', :query => {:zip => zip})
end
def self.get_all_by_name(last_name)
get('/getall_reps_byname.php', :query => {:lastname => last_name})
end
end
puts Representative.get_all_by_name('Donnelly').inspect
# {"results"=>[{"district"=>"2", "last"=>"Donnelly", "first"=>"Joe", "state"=>"IN", "party"=>"D"}]}
I used base_uri to remove the duplication of the domain and default_params to automatically append :output => ‘json’ to each request. The previous examples give you a really good example of what HTTParty can do, but there is one last example I’ll show.
HTTP Authentication
The only thing we haven’t covered is authentication. API keys are simple, just add them to default_params I showed in the last example, but what about http authentication? Twitter uses http authentication, so our next example will use them.
require 'rubygems'
require 'httparty'
class Twitter
include HTTParty
base_uri 'twitter.com'
basic_auth 'username', 'password'
end
puts Twitter.post('/statuses/update.json', :query => {:status => "It's an HTTParty and everyone is invited!"}).inspect
# {"user"=>{"name"=>"Snitch Test", "url"=>nil, "id"=>808074, "description"=>nil, "protected"=>true, "screen_name"=>"snitch_test", "followers_count"=>1, "location"=>"Hollywood, CA", "profile_image_url"=>"http://static.twitter.com/images/default_profile_normal.png"}, "favorited"=>nil, "truncated"=>false, "text"=>"It's an HTTParty and everyone is invited!", "id"=>870885871, "in_reply_to_user_id"=>nil, "in_reply_to_status_id"=>nil, "source"=>"web", "created_at"=>"Mon Jul 28 20:07:52 +0000 2008"}
Cool. Wait, HTTP Authentication has to go in the class? No silly, Trix are for kids! The basic_auth method is just a class method so you can use it wherever class methods are acceptable. Try this on for size:
require 'rubygems'
require 'httparty'
class Twitter
include HTTParty
base_uri 'twitter.com'
def initialize(user, pass)
self.class.basic_auth user, pass
end
def post(text)
self.class.post('/statuses/update.json', :query => {:status => text})
end
end
puts Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!").inspect
# {"user"=>{"name"=>"Snitch Test", "url"=>nil, "id"=>808074, "description"=>nil, "protected"=>true, "screen_name"=>"snitch_test", "followers_count"=>1, "location"=>"Hollywood, CA", "profile_image_url"=>"http://static.twitter.com/images/default_profile_normal.png"}, "favorited"=>nil, "truncated"=>false, "text"=>"It's an HTTParty and everyone is invited!", "id"=>870885871, "in_reply_to_user_id"=>nil, "in_reply_to_status_id"=>nil, "source"=>"web", "created_at"=>"Mon Jul 28 20:07:52 +0000 2008"}
Miscellaneous
- Install:
sudo gem install httparty - Rubyforge: http://httparty.rubyforge.org
- Github: http://github.com/jnunemaker/httparty
Conclusion
Ok, so that was a really long introduction, but hopefully it was helpful. I’ve also included examples in the gem for those who want to venture more (twitter, delicious, amazon associates web services, most basic usage). Also, don’t be afraid of the code as it doesn’t have much (< 140 lines at the moment).

Leave a Reply