[uf-rest] RESTful Rails plugin on RubyForge
Dan Kubb
dan.kubb at autopilotmarketing.com
Tue Mar 14 01:45:38 PST 2006
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I'd like to announce the release of my RESTful Rails plugin on
RubyForge.
It was previously discussed on this mailing list a couple of times
before:
http://microformats.org/discuss/mail/microformats-rest/2005-
November/000042.html
http://microformats.org/discuss/mail/microformats-rest/2006-
February/000144.html
My first goal with this plugin is to attempt to try to show how
much HTTP kicks ass and address the framework shortcomings, not
already addressed by rails itself, that were outlined in the
excellent article "On HTTP Abuse":
http://naeblis.cx/rtomayko/2005/04/22/on-http-abuse
This plugin adds some advanced support for HTTP's features to regular
rails controllers, like conditional GET/PUT/POST/DELETE, per-HTTP
method dispatch handing, transparent OPTIONS support and several others.
Installation of the plugin is pretty straight forward:
1. Go to your rails application directory.
2. If your rails application is under version control with Subversion
run the command after "YES" below, otherwise run the command
after "NO":
YES: script/plugin install -x svn://rubyforge.org/var/svn/
restful-rails/trunk
NO: svn checkout svn://rubyforge.org/var/svn/restful-rails/
trunk vendor/plugins/restful-rails
Thats it!
Documentation and Test Cases are forth-coming, but I wanted
to get this out for feedback now rather than wait any longer
while I continue to tweak it.
This is the third iteration after extracting this from a real
world project. The API is still open for discussion and I would
love to see input what's there so far.
Since documentation is currently sparse, here's a sample
controller using per-method dispatching and conditional request
handling:
class BookController < ApplicationController
include RestController::Base
def new
conditions << @book = Book.new
resource.get(:cache_as => :public, :for => 1.hour)
end
def collection
conditions << @books = Book.find(:all)
resource.post do
@book = @books.build(params[:book])
if @book.save
render_post_success :action => 'by_id', :id => @book
else
render :action => 'new', :status => HTTP::Status::BAD_REQUEST
end
end
end
def by_id
conditions << @book = Book.find(params[:id])
resource.put do
@book.attributes = params[:book]
if @book.save
render_put_success
else
render :status => HTTP::Status::BAD_REQUEST
end
end
resource.delete do
if @book.destroy
render_delete_success :id => nil
else
render :status => HTTP::Status::BAD_REQUEST
end
end
end
end
There are only three things you need to do to use this plugin with
a normal controller:
1. Include RestController::Base as I've done at the top of the
example above.
2. Append all the models that are used in the view for GET requests
or modified by PUT/POST/DELETE requests to the conditions object
as shown in the example above.
3. Move the code that should only be executed in response to
specific HTTP methods into individual blocks, and assign them
the corresponding method handler in the resource object.
You can optionally also specify the caching instructions when
a method handler is executed, as shown in the new action's GET
handler. In this case the Cache-Control and Expires headers
are set so that the response should be publicly cached for up
to 1 hour.
While the per-method dispatch is the most obvious new feature it
isn't the best part. IMHO the best feature is the support for
Conditional HTTP requests.
When you append the models to the condition object, it will
construct ETag and Last-Modified headers if the models have
lock_version and updated_at attributes. It will then compare the
If-* Request headers against these, performing a conditional
test to see if the per-method handler should execute and if the
view should be rendered. If it can, the application will return
a 304 Not Modified or a 412 Preconditional Failed response.
Why is this cool? Well, this solves the whole stale update
problem, especially for AJAX apps where we can control what
headers are sent. Each time an AJAX app fetches a resource,
it could make note of the resource's Last-Modified and ETag
headers. Later on when changing the same resource those headers
would be sent by the AJAX app in the If-Unmodified-Since and
If-Match headers respectively, along with the request.
Effectively this is saying to the server "Only perform the
operation if the copy of the server state matches what I
saw last time"...
That means Optimistic Locking for AJAX apps using plain old HTTP.
Another nice part of this is that if the browser is doing a
GET request, and the server state hasn't changed then the server
will simply return a 304 Not Modified response. This skips the
rendering of the view, and almost no data is sent along the
wire to the browser. The browser will just render what it had
cached locally, which as you can imagine is extremely fast all
around.
I would bet that apps that used conditional GET (when possible)
would scale better than those that did not. It would be
interesting to test this though.
The best part is that support for Conditional GET is not just
limited to AJAX apps, the support in modern browsers is excellent.
So anyway, please install it and give it a spin. Feedback is highly
appreciated.
- --
Thanks,
Dan
__________________________________________________________________
Dan Kubb
Autopilot Marketing Inc.
Email: dan.kubb at autopilotmarketing.com
Phone: 1 (604) 820-0212
Web: http://autopilotmarketing.com/
vCard: http://autopilotmarketing.com/~dan.kubb/vcard
__________________________________________________________________
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (Darwin)
iD8DBQFEFpDC4DfZD7OEWk0RArWXAJ0Sejk2pcCjZezyxHurJfITQjjMfwCgmcFx
9rb1jcGohgfKFvzjTQKdnt4=
=G67N
-----END PGP SIGNATURE-----
More information about the microformats-rest
mailing list