Digg API with Ruby (and Rails too!)

Note: This site has changed a lot over the last year. I'm not using Rails anymore on the site, so there's no place where I'm currently using the Digg API. However, I'll leave this post intact, as it might help others with something similar.

If any of you readers have come in through my main site lately, you should notice that I added a little something on the sidebar. One of my favorite time-wasting activities of the day is browsing Digg. I tell you, there should be some sort of "Diggers Anonymous" for us simple-minded folk who can't stop from visiting the site for stories many times a day!

Anyway, they recently announced the release of the Digg API, to allow any user to access the stories on the site in any way, shape or form we desire. Now, I must say that I have no real use for this. Of course, being the geek I am, I just wanted to try it out and see how I could use a simple API using Ruby, for use on my Rails site (this one). This is also made in case anyone else wants to do the same. One note, however: I'm only a Ruby / Rails beginner. I'm sure there are much better and efficient ways to do this. Since I haven't dabbled in this too much, and "it works on my machine", I'll leave it as it is for now.

Just for the record, the testing and development for this code was made on my computer running Ubuntu 7.04, along with Ruby 1.8.6 (compiled by myself, not downloaded from the Ubuntu repos), RubyGems 0.9.2 and Rails 1.2.3. It should work correctly with any fairly recent version of the software mentioned above.

The Digg API is rather simplistic right now. All you need is a simple request using a URL like this:

http://services.digg.com/stories?appkey=http%3A%2F%2Fdennmart.com&type=xml&count=5

In the example above, the API is called to the 'http://services.digg.com' server. The parameters afterwards (in this case, '/stories') is the request. You can add more parameters for a finer-grained search.

Once the request URL is complete, you need some additional actions to add as well. The 'appkey' is required, but can be anything for now. Digg isn't generating application keys (a la eBay or other external API's), but an appkey is required for 'statistical purposes', according to the documentation. The 'type' is how the data is going to be returned to the app, and it can be either XML, Javascript, JSON or PHP. I use good ol' XML for now, until I can actually play around further with the other response types. Finally, the 'count' action is to limit how many stories are returned. I'm only going over these options quickly, as the API documentation has much more information.

Once you figure out which request you want to make (using Mozilla Firefox can help greatly, as the XML response is nicely formatted), it's a matter of getting that data into your Ruby or Rails app. Going back to the ol' trusty Pickaxe book, I found a nice little module integrated in Ruby called open-uri. This module allows the Ruby application to open a URL (either http, https or ftp) and get the returned contents. To use this module, a simple line of code is needed:

require 'open-uri'

That should load your module correctly. Now it's just a matter of having Ruby open the API connection and store its XML response in a variable:

response = open('http://services.digg.com/stories/popular?appkey=http%3A%2F%2Fdennmart.com&type=xml&count=5').read

The 'open' function, well, opens the connection to the API, while the 'read' method gets the data that's returned from the URL. Like I said, rather simple.

However, I was getting timeout errors when calling the 'open' function. Strangely enough, the function worked fine when using any other URL. Upon further reading of the API documentation, I found this little tidbit:

"All API requests must include a User-Agent HTTP Header. A request without this header will receive no response."

So, after that small mistake, I edited the request like so:

response = open('http://services.digg.com/stories/popular?appkey=http%3A%2F%2Fdennmart.com&type=xml&count=5', 'User-Agent' => 'Ruby/1.8.6').read

The 'User-Agent' parameter can be anything. I just decided to use the Ruby version I have. Once I added that, I had my nice XML with the five most recent Digg stories that have been promoted to the front page.

After that, I needed to parse that XML. I searched around the Internet, and found a great little module called 'XmlSimple'. This module reads and writes XML, and formats it according to whatever's needed. In my case, I needed to read the XML response. Just like the 'open-url' module previously, you need to load the 'XmlSimple' module as well, once installed ("gem install xml-simple"):

require 'xmlsimple'

I did run into some minor problems when loading this module. The Ruby interpreter cried out loud, saying it couldn't load the module. How come? I verified that the gem was installed correctly, and it was. Then I realized that since this is a gem, I need to have 'RubyGems' loaded before loading 'xmlsimple':

require 'rubygems'

I believe that Rails already loads the module for you. But if you're testing this out on Ruby and not on Rails, you'll need it.

Okay, once I did that, I was able to load the 'xmlsimple' module. Now I need it to parse the XML response that I stored in the aptly-named 'response' variable. A simple line of code can convert the XML into a hash:

XmlSimple.xml_in(response)

That takes the entire XML string and puts it into a neatly organized hash. I really don't need everything in the hash, just the story title, link and how many diggs the story has. So I just access those particular keys:

digg_hash = XmlSimple.xml_in(response)
story_title = digg_hash['story'][0]['title']
story_link = digg_hash['story'][0]['link']
story_diggs = digg_hash['story'][0]['diggs']

In the example above, the 'story_title' variable has the most recent story title, the 'story_link' is the link that takes you directly to the story, and the 'story_diggs' has the current amount of diggs that story has. The number zero used in the array is the story number. If you want to get more than one, you'll need to loop through the array and get the other stories.

The code I'm using on the main site right now is the following:

<% require 'open-uri' 'rubygems' 'xmlsimple' response="open('http://services.digg.com/stories/popular?appkey=http%3A%2F%2Fdennmart.com&type=xml&count=5'," 'user-agent'> 'Ruby/1.8.6').read
  articles = 0
  while articles < 5
%>

(Dugg <%= xmlsimple.xml_in(response)['story'][articles]['diggs'] %> times)

<% articles +="1" end %>

In all, this was rather simple, and I'm sure there will be a much more creative use for the Digg API soon. But for those who want to learn how to use it, or those who need a very simple approach, this works just fine. I'm interested in listening on how other people have put this to use. Hope this helps someone!