---
title: 'Jester 1.2: Flexible REST'
teaser:
tags: news,web,javascript,jester
author: Eric Mill
published_on: 2007-04-30
---

This release is about making Jester more flexible, and better supporting custom
REST <abbr title="Application Programming Interface">API</abbr>s.  The [flurry
of activity] in ActiveResource is a good reminder that REST isn't just several
default controller actions—it's a guiding philosophy to defining your own API.
REST is just about using simple <abbr title="Uniform Resource
Locator">URL</abbr>s and HTTP status codes to carry all the metadata, so that
the bodies of your requests and responses don't have to.

[flurry of activity]: https://thoughtbot.com/blog/activeresource-in-a-flurry

Jester is available from SVN in [trunk form][trunk], or a [1.2 release
form][tag]. You can also download a [zipped copy of 1.2][zip].

[trunk]: http://svn.thoughtbot.com/jester/trunk
[tag]: http://svn.thoughtbot.com/jester/tags/rel-1.2/
[zip]: http://images.thoughtbot.com/ui/2007-4-30-jester-1.2.zip

Jester is released under the MIT License.

New features this release:

* find() will take a hash of query parameters to append to the <abbr
  title="Uniform Resource Locator">URL</abbr>.
* Base.model() takes all parameters after the name as a hash, instead of a list.
* Asynchronous calls can take a hash of callbacks and options, instead of just a
  callback.
* Support for reading an object's schema. (new.xml)
* Dates are parsed into Date objects.  (Thanks to [Nicholas Barthelemy][datejs])
* Objects are reloaded from <abbr title="Extensible Markup Language">XML</abbr>
  if <abbr title="Extensible Markup Language">XML</abbr> is returned after a
  create or update request.
* Included ObjTree.js inline, and replaced prototype.js with an optional lighter
  form (prototype.jester.js).

[datejs]: https://svn.nbarthelemy.com/date-js/

You can pass arbitrary query parameters along with a `find` request, in a hash.
It's the second parameter, bumping the asynchronous callback/options parameter
to third.

**This breaks backwards compatibility.**

```javascript
>>> User.find("all", {admin: true, toys: 5})
GET http://localhost:3000/users.xml?admin=true&toys=5
```

Arguments when defining a model are now taken as a hash instead of a plain
ordered list.

**This breaks backwards compatibility.**

```javascript
>>> Base.model("User", {plural: "people", prefix: "https://thoughtbot.com"})
>>> User._plural_url()
"https://thoughtbot.com/people.xml"
```

You can now supply a hash of options to be fed directly to Prototype's
Ajax.Request method, instead of just a callback.  You can use this to specify
different callbacks for success or failure conditions, or override the HTTP
method used in the request, or anything [specified here][ajax-optinos].  If you
supply only a callback, it will be treated as your "onComplete" callback.  You
can use these options with a synchronous request if you set "asynchronous" to
false.

[ajax-options]: http://www.prototypejs.org/api/ajax/options

```javascript
>>> User.find(1, {}, {onSuccess: success, on404: notFound, method: "post"})
POST http://localhost:3000/users/1.xml

>>> User.find(1, {}, successCallback)
GET http://localhost:3000/users/1.xml

>>> eric = User.find(1, {}, {asynchronous: false})
GET http://localhost:3000/users/1.xml
```

There is a longstanding problem in Jester, which is that when you create or
build a new object, you have to specify all of its properties in the attributes
hash.  If you simply call `eric = User.build()`, and then later, `eric.name =
"Eric"`, the User model has no way of knowing this is a model attribute, and it
won't be included in any `save()` requests.  ActiveResource gets around this by
using Ruby's `method_missing`, which isn't an option for clients written in many
languages.

After talking it over with my coworkers, we realized there was value in giving a
REST client access to a model's schema, much as ActiveRecord has database access
to ascertain a model's schema.  So to I proposed that Rails introduce into their
standard REST controllers the "new.xml" route, and a patch with it, which [was
accepted][rest-patch] this week.

[rest-patch]: http://dev.rubyonrails.org/changeset/6579

Jester supports this, but it is disabled by default, as it will incur an HTTP
request when you may not expect it, and your code may work fine without it.  It
also currently only works synchronously.  You can trigger it by passing an
option to build in a second hash parameter.

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

>>> eric._properties
["active", "email", "name"]

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

>>> eric._properties
["active", "email", "name", "lasers"]
```

Dates are now parsed into actual JavaScript Date objects when a model is loaded
from <abbr title="Extensible Markup Language">XML</abbr>, thanks to code
contributed by Nicholas Barthelemy.
([SVN](https://svn.nbarthelemy.com/date-js/))

```javascript
>>> post = Post.find(1)
GET http://localhost:3000/posts/1.xml
>>> post.created_at
Sat Mar 31 2007 03:01:56 GMT-0500 (Eastern Standard Time)
```

If a create or update request results in an <abbr title="Extensible Markup
Language">XML</abbr> response body, the model will reload itself using this XML.
So, if your app changes an object on create or on update, this will be reflected
in the client, as long as your controller renders the object in <abbr
title="Extensible Markup Language">XML</abbr> after saving it.  Props to
Nicholas Barthelemy for pointing out this was important **before** DHH suddenly
[committed changes](http://dev.rubyonrails.org/changeset/6539) in ActiveResource
to do the exact same thing.

**Client:**

```javascript
>>> eric = User.create({email: "emill@thoughtbot.com", name: "Eric Mill"})
POST http://localhost:3000/users.xml

>>> eric.unique_key
"frederick"
```

**Controller:**

```ruby
def create
  @user = User.new(params[:user])
  @user.unique_key = "frederick"

  respond_to do |format|
    if @user.save
      headers["Location"] = user_url(@user)
      format.xml  { render :xml => @user.to_xml, :status => 201}
  end
end
```

You no longer need to include ObjTree.js in your own HTML.  I took the parts of
ObjTree I used, packed them using [Dean Edwards' packer
script](http://dean.edwards.name/packer/), and appended them to jester.js.  In
the same vein, I removed prototype.js from the repository, and replaced it with
a smaller form of prototype, prototype.jester.js.  Use this if you aren't using
Prototype for anything besides Jester, and want quicker loading times.

There were also some little fixes.  If a resource is found by its ID, the ID
will definitely be set in the object's properties, even if the returned <abbr
title="Extensible Markup Language">XML</abbr> didn't include it.  Also, the ID
is set more correctly when parsed out of a Location header, though I doubt this
issue affected anyone, as it still worked fine in practice in most cases.  There
was also a bug in ObjTree where empty attributes (i.e. `<email></email>`)
weren't even counted.

There's still some significant work left for Jester, and I'm sure even more
great ideas will come out of you guys, and out of the ActiveResource team.  My
targets for the next release, in order of importance:

* Allowing "prefix options", so that you can specify comments at
  `/users/:user_id/comments.xml`, instead of `/comments.xml`.  Options would be
  passed inside the same hash as the query parameters, and separated by Jester.
  An example might look like:

    // find all approved comments by user #1
    >>> Comment.find("all", {user_id: 1, approved: true})
    GET http://localhost:3000/users/1/comments.xml?approved=true

* Calls to new.xml will only happen **once** per model, when the model is first
  declared, not on every call to build.
* Support for "custom methods", as specified in ActiveResource
  [here](http://dev.rubyonrails.org/changeset/6595)
* Support for manually specified <abbr title="Uniform Resource
  Locator">URL</abbr>s, as specified in ActiveResource
  [here](http://dev.rubyonrails.org/changeset/6584)
* Provide an option for a model so that Jester will submit POST and PUT request
  parameters as an <abbr title="Extensible Markup Language">XML</abbr> body,
  instead of as form parameters.

Suggestions and feedback much appreciated as usual!
