Monday, April 14, 2008

Using rAtom in Rails

Some mysterious anonymous person asked me for a tutorial on using rAtom within Rails.  So I'll show you all how I do it.

Firstly, the big difference is that I treat it more like ActiveRecord#to_xml instead of like a *.builder view. So I'll start with an example.

Assuming you have a blog application with a Post model class, you can add a #to_atom method to your class like this:



class Post < ActiveRecord::Base  
def to_atom
Atom::Entry.new do |entry|
entry.title = self.title
entry.updated = self.updated_at
entry.published = self.created_at
entry.author = Atom::Person.new(:name => self.author)
entry.links << Atom::Link.new(:rel => 'alternate',
:href => "/posts/#{self.id}")
entry.content = Atom::Content::Html.new(self.content)
end
end
end


The advantage of returning a rAtom object here is that the atom representation is then composable, for example if you have a Blog class that has many posts you can do this:



class Blog < ActiveRecord::Base
has_many :posts

def to_atom
Atom::Feed.new do |feed|
feed.title = self.title
feed.links << Atom::Link.new(:rel => 'alternate',
:href => "/blogs/#{self.id}")

self.posts.each do |post|
feed.entries << post.to_atom
end
end
end
end


To get this atom representation out via HTTP, you would do this in the controller:

class BlogsController << ApplicationController
def show
@blog = Blog.find(params[:id])
respond_to do |format|
format.atom { render :xml => @blog.to_atom.to_xml }
end
end
end


This has been really useful in an application that I am working on because we are using Atom as the main communication format between a bunch of different components, so this composability comes in really handy. For a simple blog it might be overkill, but at least you get the much better performance of libxml-ruby.

You'll probably notice a bit of ugliness in the to_atom methods, specifically this:

      entry.links    << Atom::Link.new(:rel => 'alternate', 
:href => "/posts/#{self.id}")


The URL is hard-coded here because url_for is not available in a model. This is a bit of trade-off between the composability and strict MVC here, if you really wanted to you could pass the URL in as a parameter, or add a block that generates URLs or whatever, it's up to you.

For some applications, like a simple blog, the composability might not be too important, and you might prefer the syntax of atom_feed in Rails, or at least want to be able to render the atom in a view. In this case it would probably be nice to have a rAtom based template format, so you could add a view like "blogs/show.atom.ratom" which allowed you to move the to_atom method into a view and call url_for directly from the view. I'd like to add this at some stage, but it hasn't happened yet, due to time and priorities, but if anyone would like to submit a patch for it I'd gladly accept it.

1 comment:

Becka said...

People should read this.