---
title: 'Jester 1.5: Universal REST'
teaser:
tags: news,web,open source,javascript,rails,jester
author: Eric Mill
published_on: 2007-10-25
---

It's been quite some time since the last Jester release, but there's actually
been a great deal of work done on it since then.  Jester's object hierarchy code
has been completely rewritten, some major new features added, and some syntax
changes that break backwards compatibility.  This release emphasizes working
with customized server-side <abbr title="Representational State
Transfer">REST</abbr> <abbr title="Application Programming
Interface">API</abbr>s, not just the ones generated by default in Rails through
scaffold_resource.  Jester is also moving beyond being just a JavaScript
ActiveResource clone, with its own ideas that takes advantage of what JavaScript
can do.

Bigger than that, this release coincides with the launch of a real Jester
website, located at [http://jesterjs.org](http://jesterjs.org).  It's a dirt
simple site, with a basic howto, a download link, and a link to the new [Jester
discussion group](http://groups.google.com/group/jester-js).  Maybe someday
it'll have its own blog, or its own Trac, or something, but the small approach
seems to fit Jester snugly for now.

Jester is available from SVN in [trunk
form](http://svn.thoughtbot.com/jester/trunk), or a [1.5 release
form](http://svn.thoughtbot.com/jester/tags/rel-1.5/). You can also download a
[zipped copy of 1.5](http://images.thoughtbot.com/ui/2007-10-25-jester-1.5.zip).
Jester is released under the MIT License.

New features this release:

* Path prefixes can interpolate keys, such as those for scoped resources (e.g.
  /users/:user_id)
* Customization of all the <abbr title="Uniform Resource Locator">URL</abbr>s a
  model uses, such as its show <abbr title="Uniform Resource
  Locator">URL</abbr>, its destroy <abbr title="Uniform Resource
  Locator">URL</abbr>, etc.
* Support for <abbr title="JavaScript Object Notation">JSON</abbr> callbacks
  (JSONP), allowing fetching of remote data if the <abbr title="Application
  Programming Interface">API</abbr> supports it
* Renamed `Base` to `Resource`, and provided a scoped reference to it,
  `Jester.Resource`
* Rewrite of object hierarchy, so objects returned from `User.find` are of class
  `User`
* Models can fetch their skeleton from new.xml or new.json if passed checkNew:
  true on the call to `Resource.model`, instead of on each call to `build`
* <abbr title="JavaScript Object Notation">JSON</abbr> responses will now have
  any date ending in _at or _on transformed into a Date value.
* <abbr title="Extensible Markup Language">XML</abbr> parser is only
  instantiated if a model is instantiated with an <abbr title="Extensible Markup
  Language">XML</abbr> format

If you're scoping your resources, by parent or date or whatever, you can use
Ruby symbol notation to interpolate different keys into the <abbr title="Uniform
Resource Locator">URL</abbr>.  You can pass in values for these keys as part of
the params hash that is the second argument to `find`.  Any non-interpolated
values will be appended to the query string.

    >>> Resource.model("Article", {prefix: "/:section"})
    Article()
    >>> Article.find("first", {section: "humor"})
    GET http://localhost:3000/humor/articles.xml

    >>> Resource.model("Comment", {prefix: "/posts/:post_id"})
    Comment()
    >>> Comment.find("all", {post_id: 1, approved: true})
    GET http://localhost:3000/posts/1/comments.xml

You can also go further, and specify a custom <abbr title="Uniform Resource
Locator">URL</abbr> for each RESTful action for a given model, by providing a
hash of <abbr title="Uniform Resource Locator">URL</abbr>s when you first define
the model.  You need only define the <abbr title="Uniform Resource
Locator">URL</abbr>s which differ from the defaults.  You can specify <abbr
title="Uniform Resource Locator">URL</abbr>s for create, list, destroy, update,
show, and new.

    Resource.model("Article", {
      urls: {
        list: "/:section/articles.xml",
        show: "/all/articles/:id.xml",
        create: "/:section/new_article.xml"
      }
    }

    // will interpolate the section into the URL, and POST the rest as attributes
    >>> article = Article.create(
      {section: "humor",
        title: "Fishing Through the Ages",
        author: "fishing@breathoffire.com"
      }
    )
    POST http://localhost:3000/humor/articles.xml

The coolest new feature is that Jester can now work with remote <abbr
title="Application Programming Interface">API</abbr>s that support <abbr
title="JavaScript Object Notation">JSON</abbr> callbacks.  This works by adding
a script element to the <abbr title="Document Object Model">DOM</abbr> that
loads in remote JavaScript, with a callback method name appended to the query
string.  The loaded JavaScript will call this method with the <abbr
title="JavaScript Object Notation">JSON</abbr> representation of the remote
data.  Some people have gone ahead and [started
calling](http://ecmanaut.blogspot.com/2006/01/jsonp-recipe-for-visitor-innovation.html)
this method [<abbr title="JSON with
Padding">JSONP</abbr>](http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/).

The poster child for this approach is [Twitter](http://www.twitter.com).  Here's
a working Twitter client:

    Resource.model("Twitter", {
      format: "json",
      prefix: "http://twitter.com",
      urls: {
        list: "/statuses/user_timeline/:username.json",
        show: "/statuses/show/:id.json"
      }
    }

There are some caveats here.  Because loading remote data does not use
XmlHttpRequest, no such object is returned from calls to find(). In addition,
all calls to find() must be asynchronous, meaning you must provide a callback
method.  Lastly, since only GET requests are possible, the only operation you
can perform on remote models is find().

    // Loads
    // http://twitter.com/statuses/user_timeline/jesterjs.json?callback=jesterCallback
    // into the DOM
    >>> Twitter.find("all", {username: "jesterjs"}, listTwitters)

    // Loads
    // http://twitter.com/statuses/show/12345678.json?callback=jesterCallback into
    // the DOM
    // Twitter.find(12345678, showTwitter)

Implementing this on the server is dirt easy in Rails, simply append a
`:callback` parameter to your `render :json` call.  This only works with `render
:json`, not `render :text`.  Here's an example index action for a
UsersController:

    def index
      @users = User.find :all
      respond_to do |wants|
        wants.xml {render :xml => @users.to_xml}
        wants.json {render :json => @users.to_json, :callback => params[:callback]}
      end
    end

Jester can take advantage of an <abbr title="Application Programming
Interface">API</abbr> that provides a new template for an object, by setting
checkNew to true as a parameter to `Resource.model`.  Before, this was passed in
on every call to `build`, which was completely unnecessary.  The request to
fetch this template will occur, immediately and asynchronously, after the call
to `Resource.model`, and the template will be cached in the model and given to
each object created with it.

    >>> Resource.model("User", {checkNew: true})
    User()
    >>> eric = User.build()
    GET http://localhost:3000/users/new.xml

    // This only works because the User class knows "email" is an attribute
    >>> eric.email = "emill@thoughtbot.com"
    "emill@thoughtbot.com"
    >>> eric.save()
    POST http://localhost:3000/users.xml
    true

I'm still open to ideas for a better parameter name than checkNew.

I have gotten bug reports, feature requests, and even patch submissions, and
talked with people face-to-face who have heard of Jester and used it.  Still,
Jester began as a proof of concept, and has largely remained so.  I think Jester
has moved past the proof-of-concept stage; this release and [the new
website](http://jesterjs.org) will be as good a test as any.

Visit our [Open Source page](https://thoughtbot.com/open-source) to learn more about our team's contributions.
