make_resourceful: Publish Extras

Posted September 11, 2007

I think we’ve established that publish is pretty darn cool. It’s pretty sweet to be able to generate a representation of any model in XML, YAML, or JSON, with any attributes or associations included or excluded. It’s especially sweet to be able to do so with no more effort than declaring the essential information.

But it wouldn’t be make_resourceful if that were it.

No, like everything else in m_r, publish is built upon several layers of support. Each of these layers has plenty of utility outside of its original purpose, and each can be accessed and customized by the programmer.

The layer immediately underlying publish is a method called serialize. This method is defined for ActiveRecord::Base models and for Arrays. It takes the same parameters as publish. But where publish publishes the data as an action, serialize just returns a string.

User.new(:name => "Me", :link => "http://me.me").serialize(:json, :attributes => [:id, :name, :link])
  #=> "{user: {name: \"Me\", id: null, link: \"http://me.gov\"}}"

If you’re not keen on navigating those escapes, that string prints as

{
  user: {
    name: "Me",
    id: null,
    link: "http://me.gov" 
  }
}

serialize for an Array does the same thing that publish does for the index action:

[User.new(:name => "Hampton", :link => "http://hamptoncatlin.com"),
 User.new(:name => "Will", :link => "http://wfarr.wordpress.com")].serialize(:xml, :attributes => [:name, :link])
   #=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<users>\n  <user>\n    <name>Hampton</name>\n    <link>http://hamptoncatlin.com</link>\n  </user>\n  <user>\n    <name>Will</name>\n    <link>http://wfarr.wordpress.com</link>\n  </user>\n</users>\n"

Expanded:

<?xml version="1.0" encoding="UTF-8"?>
<users>
  <user>
    <name>Hampton</name>
    <link>http://hamptoncatlin.com</link>
  </user>
  <user>
    <name>Will</name>
    <link>http://wfarr.wordpress.com</link>
  </user>
</users>

Serialize itself only really does two things: converts the model into a hash (or each of the models, for an Array), and runs to_#{format} on that hash. This has a couple interesting consequences.

Most obviously, this means it’s easy to add new formats. As long as there’s a to_foo method defined for all the basic datatypes1, you can serialize and publish with it. You may have to register the MIME type to make publish work, but that’s very minor.

It also means that any attribute of a model can be serialized, as long as it responds to to_resourceful_hash. This is built in to ActiveRecord::Base and Array, but could be defined for any other object. All it needs to do is take a hash of attributes, and return the hash to be serialized.

All this is available in make_resourceful trunk (svn://hamptoncatlin.com/make_resourceful/trunk) right now. The development is pretty active, so it’s subject to change, but it’s definitely worth checking out. I’ll all be released with version 0.2.0, which should be just around the corner.

1 Off the top of my head, this includes hashes, arrays, strings, dates/times/datetimes, the numeric types, true, false, and nil. If you’re implementing a full-fledged format, though, it’s probably best to see what to_xml and friends are defined for.

Jason Allen said January 12, 2008:

Nathan,

I just discovered m_r – very cool. With respect to publish (and serialize) – i’ve found that with RESTful api’s its very handy to return URI’s of items (instead of more traditional ID’s). For example, when serializing a Post object, I’d likely include an author URI (instead the author’s ID).

However from scanning the info here it’s not obvious how I can intercept or inject a path function (instead of directly serializing a model attribute). Is there a way?

Keep up the good work!

Nathan said January 13, 2008:

Jason: If you define an accessor, resource or something like that, that gives the URI, you can use that as one of the serialized attributes.

Make your comments snazzy with Textile!