Merb gets RESTfull routes

Posted by ezmobius Thu, 25 Jan 2007 23:06:00 GMT

I’m pretty excited about a feature just added to merb last night, namely restful routes and crud controllers. You can still freely intermix your normal routes and the resource routes. No nested resource are supported yet, but will be soon.

Here is a router.rb file with a restful resource and some non restfull routes:

Merb::RouteMatcher.prepare do |r|
  r.resource :posts
  r.add '/:controller/:action/:id'
  r.add '/', :controller => 'files', :action => 'index'
end

The above route def will compile into a lambda that matches the incoming request, and looks like this:

lambda{|path| 
  case path
  when Regexp.new('/posts/([^/,?]+)[;]edit')
    @sections[:id] = $1
    return {:allowed=>{:get=>"edit"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts/new[;]([^/,?]+)')
    @sections[:action] = $1
    return {:allowed=>{:post=>"new", :get=>"new", :delete=>"new", :put=>"new"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts/new')
    return {:allowed=>{:get=>"new"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts/([^/,?]+)\.([^/,?]+)')
    @sections[:id] = $1
    @sections[:format] = $2
    return {:allowed=>{:get=>"show", :delete=>"destroy", :put=>"update"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts\.([^/,?]+)')
    @sections[:format] = $1
    return {:allowed=>{:post=>"create", :get=>"index"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts/([^/,?]+)')
    @sections[:id] = $1
    return {:allowed=>{:get=>"show", :delete=>"destroy", :put=>"update"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when Regexp.new('/posts/?')
    return {:allowed=>{:post=>"create", :get=>"index"}, :rest=>true, :controller=>"posts"}.update(@sections)
  when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/
      @sections[:controller] = $1
      @sections[:action] = $2 || 'index'
      @sections[:id] = $3 if $3
      return @sections
  when Regexp.new('/')
    return {:controller=>"files", :action=>"index"}.update(@sections)
  else
    return {:controller=>'Noroutefound', :action=>'noroute'}
  end
}

And now when you declare a resource :posts, we can have a nice generic crud controller. Here is an example that I was using to test the restfull dispatcher:

class Posts < Merb::Controller
  # GET /posts
  # GET /posts.xml
  def index
    "Post index"+params.inspect
  end

  # GET /posts/1
  # GET /posts/1.xml
  def show
    "Post show"+params.inspect
  end

  # GET /posts/new
  def new
    "Posts new"+params.inspect
  end

  # GET /posts/1;edit
  def edit
    "Posts edit"+params.inspect
  end

  # POST /posts
  # POST /posts.xml
  def create
    "Posts create"+params.inspect
  end

  # PUT /posts/1
  # PUT /posts/1.xml
  def update
    "Posts update"+params.inspect
  end

  # DELETE /posts/1
  # DELETE /posts/1.xml
  def destroy
    "Posts destroy"+params.inspect
  end
end

Fun stuff. One goal of merb’s rest support is to be able to be a lightweight fast ActiveResource server. Since merb is thread safe and only locks for a tiny period of time when you may call the database, it can handle many concurrent connections at once.
RESTfull routes are not in a released gem yet. So you will have to grab the trunk from svn and build a gem locally.

$ svn co http://svn.devjavu.com/merb/trunk merb 
$ cd merb
$ rake install

Merb uses the same hidden _method form field hack that rails does to support PUT and DELETE. When you run merb in development mode you can tack ?_method=PUT on the query string to test different verbs. In production mode _method in the query string will not be picked up if the REQUEST_METHOD is POST. This makes it easy to test your different HTTP verbs in dev mode, but be safe from query string injection in production mode.

Tags , ,  | 2 comments

Comments

  1. atmos said about 16 hours later:
    DUDE, that rocks, big time.
  2. Michael said 20 days later:

    I've been implementing some web services using the RESTfull routes, and I have two questions:

    1) Don't you need to add the route with "resources" for your example to yield the controller you have outlined?
    r.resources :posts
    
    2) Can you put together/post a sample functional test for such a controller?

(leave url/email »)

   Preview comment