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.
Searching...





DUDE, that rocks, big time.
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? 2) Can you put together/post a sample functional test for such a controller?