Best JavaScript library project roadmap I’ve seen

The mootools developers look like they are having fun defining their project roadmap:

mootools 1.3 roadmap


MooTools Plugin to uninstall Internet Explorer from any machine within network range

New on edge Rails: JSON serialization of ActiveRecord objects reaches maturity

The last time I wrote about ActiveRecord#to_json on edge Rails, it was missing some key functionality. For one, you couldn’t include any associations. Another thing was you couldn’t do something like this in your controller:

@authors = Author.find(:all)

render :json => @authors.to_json(:only => :name)

Oh and did I mention it wasn’t emitting valid JSON by default?

In the last few weeks, several patches to the Rails trunk have fixed all these issues (most of these patches are already on the Rails 2.0 Preview Release). JSON serialization of ActiveRecord objects is finally on par with XML serialization, and that old Jsonifier plugin I wrote can finally be sent off to the plugin retirement home (though, I am thinking of porting all the JSON patches into Jsonifier to add Rails 2.0-like JSON serialization to Rails 1.2.4, but who knows, I am lazy).

Anyway, back to the new JSON stuff on edge Rails.

No more invalid JSON from Rails

Yes it’s true. No more changing of module attribute to get Rails to produce valid JSON. Thanks to Choon Keat with his detailed SVN history snooping, we finally managed to get a patch to fix JSON encoding to quote all hash keys into Rails.

:include option can be used to include associations with ActiveRecord::Base#to_json

At DHH’s urging, I submitted a patch that added the :include option to ActiveRecord#to_json. Of course, it was a sloppy first submission and DHH pointed that out - a little bit of DRY love and it was really great to see it get committed (I have a soft spot for JSON serialization from Rails, since I’d been trying to achieve the same thing with Jsonifier).

So yes, now you can do:

json = @david.to_json(:include => :posts)
# or even...
json = @david.to_json(
  :include => {
    :posts => {
      :include => {
        :taggings => {
          :include => {
            :tag => { :only => :name }
          }
        }
      }
    }
})

Enumerable#to_json and Hash#to_json now accept options

One problem with Jsonifier that I often get emails about is how the to_json options don’t work for lists (Enumerables). And why is this important? Well, we often retrieve collections of ActiveRecord objects at a time with AR::B#find so it’s not too much to ask to be able to do this in your controller actions:

@authors = Author.find(:all)

render :json => @authors.to_json(:only => :name)

But of course you couldn’t, so I patched away by changing the to_json methods of Enumerable and Hash (and other types as well) to accept an optional options argument. Enumerables will pass on any options it receives to its elements. Hashes will respect :only and :except (as well as passing on the options to its elements).

Unambiguous (non-US-centric) dates and times

While Rails 1.2.3 (and 1.2.4) never supported encoding of Dates, Times and DateTimes, edge Rails has been happily supporting Date, Time and DateTime conversions to JSON since revision 6673. Unfortunately, the date string format is in MM/DD/YYYY format:

Time.now.to_json
=> "\"09/21/2007 12:15:02 UTC\""

Geoff Buesing suggested that Rails should change this to the more unambiguous YYYY/MM/DD format. Finding the right date format is important not only for readability reasons, it’s also important that the date string is directly parsable by the many browser JavaScript implementations using the JavaScript new Date(dateString) function. There is no formal spec of how dates should be represented in JSON, but it’s most convenient that strings representing dates can be directly converted into JavaScript Date objects.

Testing in several browsers and on different platforms, albeit not extensively, we settled on the %Y/%m/%d %H:%M:%S %z (strftime) format (more details in the comments of the Trac ticket).

Now it is:

Time.now.to_json
=> "\"2007/09/21 12:15:02 +0800\""

Much better.

What’s left?

Documentation, for one thing. ActiveRecord::Base#to_json is still undocumented (docfix patch). The to_json methods for Enumerable and Hash need documentation as well (will patch that soon).

ActiveRecord::Base#to_json also doesn’t deal with binary attributes, which can be easily resolved by Base64-encoding them (patch coming soon, it gets slightly complicated when trying to do a from_json). And of course, optimization improvements are always nice (decoding from JSON especially).

So, if you use JSON at all, please come in and help patch the remaining bits before Rails 2.0 rolls out. If you can’t (or more realistically, don’t have the time to) patch Rails yourself, do help out by verifying patches :).

New in Edge Rails: Serialize ActiveRecord objects to JSON with ActiveRecord#to_json

Converting ActiveRecord instances to their JSON representation has always been a topic that has been dear to me. I’d tried to get a patch that adds to_json to ActiveRecord into Rails (but the quality of my patch was lacking in a few respects). The Jsonifier plugin that I wrote tried to address the lack of built-in JSON serialization by adding a ActiveRecord#to_json method that acts much like ActiveRecord#to_xml.

So it is with mixed feelings when I discovered a couple of days ago when DHH committed a changeset that added a native ActiveRecord#to_json method. Of course, I’m elated that JSON serialization is given first class attention now, since the raison d’ĂȘtre of Jsonifier and my Rails patch was to do just that. A little bit of me is selfishly disappointed since it means that Jsonifier has become quite obsolete!

But it is an excellent change, I look forward to patching it up (if DHH doesn’t beat me to it - there’ve been so much activity on the Rails trunk recently) so that the JSON serializer supports an :include options for including associations much like for ActiveRecord#to_xml and Jsonifier’s mixed in to_json.

Update: I’ve submitted a patch for adding :include to ActiveRecord#to_json. Please test it and give feedback or +1 ;).

jQuery 1.2 goodness

jQuery 1.2 was released yesterday and while it’s unfortunate that it doesn’t work with some of the fantastic jQuery plugins I am already using, there’re some really cool new features. In particular (this is my favorite), built-in cross-domain script loading via the getScript is just too convenient.

This would be awesome for loading heavy scripts later and only when they are “activated”, like Google Maps (which was exactly what I was trying to do earlier before realizing I’d need to setup a reverse proxy to workaround the cross-domain problem).

Oh, and the easy way to turn off browser caching in AJAX calls is a nice convenience as well (no need to append random numbers or timestamps manually).

Yup, I’m in love with jQuery right now. Protoype and YUI have fallen by the wayside like Chinese first and second wives of yore.

Better JSON output from Rails with the Jsonifier plugin

Update: Edge Rails now does whatever Jsonifier does (and more). Check out my blog post on JSON serialization maturity in edge Rails. This means that this plugin is virtually obsolete if you’re using edge Rails.

If you’ve tried to output JSON from your Rails applications before, you’d probably have noticed how inadequate it all seems. Let’s look at the kind of output (pretty-printed for easier reading) you get from calling to_json on your typical User model:

{
  attributes: {
    id: 1,
    name: "Konata",
    awesome: true,
    created_at: "07/01/2007"
  }
}

Some of the common problems developers who write applications that speak JSON have with this are:

  • The attributes part is often unnecessary cruft when consuming ActiveRecord objects as JSON.
  • There’s no obvious way to control which attributes are shown in the JSON (e.g. you may want to hide private or lengthy attributes). The way to workaround this is either to write your own to_json instance method for your ActiveRecord model, or to use a Rails plugin (like acts_as_json) or a gem.
  • Including 2nd (or higher order) associations is tricky (as above, you can achieve this by defining your own to_json instance methods).

Under the hood of the current ActiveRecord#to_json

The to_json method for ActiveRecord objects falls back on using that defined in ActiveSupport’s JSON classes. The way it’s currently done, there are a bunch of JSON encoder classes defined for various types - when you call to_json on an ActiveRecord object it simply uses Object#to_json.

By the way, see that created_at: "07/01/2007" bit in the example above which is the JSON output for a Date attribute? That’s in MM/DD/YYYY format. Yup, 1st July 2007 not 7th January 2007 you “rest of the world you”. Ouch, but at least you can use JavaScript’s Date.parse() on it directly as detailed in this patch. And before you ask, yes, Date.parse() doesn’t understand the YYYY-MM-DD format.

Can has Rails patch?

So about 2 days ago, I wrote a Rails patch to boost ActiveRecord#to_json with an options hash. Yep much like what you can do with ActiveRecord#to_xml.

I hope it gets committed into the core some day. I mean, gosh, isn’t Rails all about Web 2.0 mashups. It even has that fancy render :json thing. I do believe JSON output needs to be treated more seriously in Rails rather than the half-hearted attempt at JSON encoding. No? Well, if the patch doesn’t get accepted I’ll still have my plugin (see below).

Anyway, let’s see some examples:

Some examples, see?

Assuming User and Post models where User has_many Posts:

david = User.find(1)
david.to_json  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007"}

No more attributes cruft!

:only and :except work the same way that as for ActiveRecord#to_xml:

david.to_json(:only => :name)                 # {name: "David"}
david.to_json(:only => [:id, :name])          # {id: 1, name: "David"}
david.to_json(:except => :created_at)         # {id: 1, name: "David", awesome: true}
david.to_json(:except => [:id, :created_at])  # {name: "David", awesome: true}

You can use the :methods options as well to include any methods on the object.

david.to_json(:methods => :permalink)
  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", permalink => "1-David"}
david.to_json(:methods => [:permalink, :interestingness])
  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
  #   permalink => "1-David", :interestingness => 666}

The :include option lets you include associations.

david.to_json(:include => :posts)
  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
  #    posts: [{id: 1, author_id: 1, title: "Welcome to the weblog"}, \
  #            {id: 2, author_id: 1, title: "So I was thinking"}]}

:only, :except, and :methods works on the included associations as well:

david.to_json(:include => { :posts => { :only => :title } })
  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
  #    posts: [{title: "Welcome to the weblog"}, \
  #            {title: "So I was thinking"}]}

Of course, 2nd level (and higher order) associations work too:

david.to_json(:include => { :posts => { \
                              :include => { :comments => { \
                                              :only => :body } }, \
                              :only => :title } })
  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
  #    posts: [{comments: [{body: "1st post!"}, {body: "OMGWTFBBQ!"}], \
  #             title: "Welcome to the weblog"}, \
  #            {comments: [{body: "Don't think too hard"}], \
  #             title: "So I was thinking"}]}

Please do give any feedback at the Rails patch for ActiveRecord#to_json or here. I wanna know what you feel about adding this functionality into the Rails core!

Jsonifier plugin

I’ve rolled the patch into a plugin: Jsonifier. The Jsonifier Trac is at http://trac.codefront.net/jsonifier/.

Subversion repository:

svn://svn.codefront.net/jsonifier/trunk

To install the plugin:

ruby script/plugin install svn://svn.codefront.net/jsonifier/trunk

A note on valid JSON

The JSON Rails spits out by default is not strictly valid JSON since the JSON specifications require keys to be double quoted. To get strictly valid JSON, add

ActiveSupport::JSON.unquote_hash_key_identifiers = false

in your environment.rb (or in the Rails initializers directory if you’re on edge). See my blog post on how to get strictly valid JSON from Rails for more info.