Saturday, May 03, 2008

rAtom 0.3.5 released

This is just a maintenance release, nothing too exciting:


== 0.3.5 2008-05-03

* Make sure atom:entries appears last.
* Better examples in the documentation.
* Docs for Feed and Entry list attributes.
* Gave Feeds authors and contributors.
* Fixed a couple warnings.

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.

Tuesday, April 01, 2008

rAtom 0.3.0 Released

I've just release rAtom 0.3.0. This version adds support for simple extension elements and also checks that content is in UTF-8 before serializing to XML.


As defined in the Atom Syndication Format, simple extension elements consist of XML elements from a non-Atom namespace that have no attributes or child elements, i.e. they are empty or only contain text content. These elements are treated as a name value pair where the element namespace and local name make up the key and the content of the element is the value, empty elements will be treated as an empty string.


To access extension elements use the [] method on the Feed or Entry. For example, if we are parsing the follow Atom document with extensions:


<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:ex="http://example.org">
<title>Feed with extensions</title>
<ex:myelement>Something important</ex:myelement>
</feed>

We could then access the extension element on the feed using:


> feed["http://example.org", "myelement"]
=> ["Something important"]

Note that the return value is an array. This is because XML allows multiple instances of the element.


To set an extension element you append to the array:


> feed['http://example.org', 'myelement'] << 'Something less important'
=> ["Something important", "Something less important"]

You can then call to_xml and rAtom will serialize the extension elements into xml.


> puts feed.to_xml
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<myelement xmlns="http://example.org">Something important</myelement>
<myelement xmlns="http://example.org">Something less important</myelement>
</feed>

Notice that the output repeats the xmlns attribute for each of the extensions, this is semantically the same the input XML, just a bit ugly. It seems to be a limitation of the libxml-Ruby API. But if anyone knows a work around I'd gladly accept a patch (or even advice).


You can get rAtom via gem:


> gem install ratom


or from Github.

Saturday, March 08, 2008

rAtom 0.2.1 Gem released

rAtom is a Ruby Gem for working with the Atom Syndication Format and the Atom Publishing Protocol (APP).

rAtom was originally built to support the communication between a number of applications built by Peerworks[http://peerworks.org], via the Atom Publishing protocol. However, it supports, or aims to support, all the Atom Syndication Format and Publication Protocol and can be used to access Atom feeds or to script publishing entries to a blog supporting APP.

Features:
  • Uses libxml-ruby so it is _much_ faster than a REXML based library.
  • Uses the libxml pull parser so it has much lighter memory usage.
  • Supports RFC 5005 (http://www.ietf.org/rfc/rfc5005.txt) for feed pagination.

You can install via gem using:
# sudo gem install ratom

Usage

To fetch and parse an Atom Feed you can simply:
feed = Atom::Feed.load_feed(URI.parse("http://example.com/feed.atom"))

And then iterate over the entries in the feed using:

feed.each_entry do |entry|
# do cool stuff
end

To construct a Feed

feed = Atom::Feed.new do |feed|
feed.title = "My Cool Feed"
feed.id = "http://example.com/my_feed.atom"
feed.updated = Time.now
end

To output a Feed as XML use to_xml

> puts feed.to_xml
<feed xmlns="http://www.w3.org/2005/Atom">
<title>My Cool Feed</title>
<id>http://example.com/my_feed.atom</id>
<updated>2008-03-03T23:19:44+10:30</updated>
</feed>

Publishing

To publish to a remote feed using the Atom Publishing Protocol, first you need to create a collection to publish to:
collection = Atom::Pub::Collection.new(:href => 'http://example.org/myblog')

Then create a new entry:
entry = Atom::Entry.new do |entry|
entry.title = "I have discovered rAtom"
entry.authors << name =""> 'A happy developer')
entry.updated = Time.now
entry.id = "http://example.org/myblog/newpost"
entry.content = Atom::Content::Html.new("<p>rAtom lets me post to my blog using Ruby, how cool!</p>")
end

And publish it to the Collection:
published_entry = collection.publish(entry)

More Information

See http://ratom.rubyforge.org for more information.