Routing
Please note that is document is not comprehensive yet. It will be finished soon, so please keep checking back!
Basic Routing
The router in Merb is very flexible and powerful. To modify routes in Merb, edit the file config/router.rb in your application folder. There are four methods to create routes: default_routes(), match(), resource(), and resources(). Some simple example routes may be found in the comments of your router.rb file. To add new routes, add code in router prepare block:
Merb::Router.prepare do |r|
# ... routes
end
The ‘r’ variable is the base variable for the routes. The most basic form of routes can be generated by simply using default_routes like so:
Merb::Router.prepare do |r|
r.default_routes
end
This will make it so that the route of controller_name/action_name/id will be available. If you are using a lot of REST routes (covered later), you might not want to use this as it makes actions in the REST controllers available without the proper request methods, making accidental calls easier.
If you require a more complex solution, you can use the match() method. There are a few different ways to define routes: a string, a regular expression, a hash, or a combination of a string or an expression with a hash. You then chain a to() method if you want to define a custom variable (such as the controller) that is linked to the route. If you want to add a named route, you then chain a name() controller, like so:
r.match('/pages/:action/:id').to(:controller => 'CustomPage').name(:page)
When you use a name with a route, you can generate the route in your controllers or views by using the url function.
<a href="<%= url(:page) %>”>Some Link Text. If you prefix a part of the string with a colon (:), that signifies a variable name rather than a literal string. In the example above, a URL like ’/pages/list’ passed to Merb will call the ‘list’ action in the CustomPage controller. If you need more flexibility than this provides, you can use regular expressions:
r.match(%r'links/(.+)').to(:controller => 'Link', :action => 'redirect', :uid => '[1]')
The %r creates a regular expression with a delimiter (in this case it’s an apostrophe). You can see the controller and action variables are set explicitly, and the uid variable has this funky looking value of [1]. This tells Merb to check the regular expression for matches. [1] is the match found in the first parentheses, and you can use [2], [3], [4], etc for each set of parentheses after that. Note that if you use a regular expressions that you will not be able to use named routes.
RESTful Routes
If you are familiar with RESTful routes in Ruby on Rails, you will be pleasantly surprised to find that Merb supports similar syntax. To define a RESTful route, you may use either the methods of resources or resource.
r.resources :pages
r.resource :profile
A lot of power is contained in either of those two lines. When resources is called, it generates named routes that correspond with commonly used actions in controllers. In this case, since we defined resources called “pages”, Merb automatically looks for a controller named Page when a matching URL is found. For resources, the following actions are generally defined in the controller:
- index – For listing many of a type of resource (uses the GET method)
- show – For showing only one item of a resource (uses the GET method)
- new – Show the page/form to create a new item (uses the GET method)
- create – The post-to action for the new page (uses the POST method)
- edit – Show the page/form to edit an item (uses the GET method)
- update – The post-to action for the edited item (uses the PUT method)
- delete – Show the page/form to delete an item (uses the GET method)
- destroy – The post-to action for the item to be destroyed (uses the DELETE method)
A major part of REST is the categorization of different actions defined by the HTTP method they use. You are most likely familiar with the HTTP methods of POST and GET – but what’s with the PUT and DELETE methods? They are just like the POST and GET methods, except that most browsers do not support them. To get around that, a _method variable is often sent with forms to instruct Merb what method to use instead of POST. Requests sent with GET are not supposed to change anything, just view data or forms. Requests by the POST method are to be used to create items. PUT modifies items, and you guessed it: DELETE deletes items.
The new, edit and delete actions are really only convenience actions for users accessing your application through a browser. They respond to a GET method with the correct form to allow the user to perform the action they intend. None of these actions actually modify the underlying data.
Custom routes allow you to add actions that can’t be easily defined inside of REST, adding a search method for example. This will create the path ’/profiles/search’ with the action ‘collection’ getting called in the Profiles controller.
r.resources :profiles, :collection => {:search => :get}
If you want to define an action that will be called when an ID is supplied (such as ’/users/1/defenestrate’), you can define it with the :member option.
r.resources :users, :member => {:defenestrate => :get}
You will also note that the action name is used in conjunction with the HTTP request method (:post, :get, :put, or :delete)
To use a custom controller class, simply use the :controller option:
r.resources :users, :controller => 'Admin/Users'
RESTful Routes Helpers
The url method in Merb’s controllers and views are used with RESTful routes the same way that named routes are. To see the list of pages (defined by the “index” action in the Page controller, in this example) you can call: url(:pages). To see an individual page, you call: url(:page, 1) (where 1 is the ID of the page you want to view) or you can pass a Page object as well:
page = Page.find(2) #If you are using ActiveRecord as your ORM
redirect url(:page, page)
To edit a page, call url(:edit_page, page), to create a new page: url(:new_page) and to link/redirect to a confirmation page for the destroy action you can call url(:delete_page, page).
In the new, edit, and delete actions, you need to create a form to post the information back to Merb. To create a new item, you can do the following:
<form action="<%= url(:pages) %>" method="post">
<!-- form stuff goes here -->
</form>
To edit an item, it is handled a bit differently. You use the same url that shows the item, but you use the PUT method instead. Since browsers do not support the PUT method, you have to cheat a little to let Merb know what you want:
<form action="<%= url(:page, @page) %>" method="post">
<input type="hidden" name="_method" value="put"/>
<!-- form stuff goes here -->
</form>
As you can see, a hidden input field named _method is given the value of “put” so that merb will know to direct this to the update action in the controller. To define an HTML form to confirm the deletion of an item, you can make a form like this:
<form action="<%= url(:page, @page) %>" method="post">
<input type="hidden" name="_method" value="delete"/>
Are you sure you want to delete this page? <br/>
<button name="confirm" value="y">Yes</button> <button name="confirm" value="n">No</button>
</form>
Nested routes
r.resources :users do |user|
user.resources :nicknames
end
The above nested routes will net you all of the routes you expect for user, and these for nicknames -
- url(:user_nicknames, :user_id => 1) => /users/1/nicknames
- url(:user_nickname, :user_id => 1, :id => 1) => /users/1/nicknames/1
- url(:new_user_nickname, :user_id => 1) => /users/1/nicknames/new
- url(:edit_user_nickname, :user_id => 1, :id => 1) => /users/1/nicknames/1/edit
- put, post, and delete will route as you expect, using :method => :verb syntax in your action
To see your available routes, use the following snippet:
merb -i merb.show_routes
Namespaces
Similar to nesting routes, it's possible to have a group of routes match the same thing without having to type code over and over:
r.match(:subdomains => 'admin') do |admin|
admin.match('/').to(:controller => 'AdminController', :action => 'show').name(:admin)
admin.resources(:pages, :controller => 'Admin/Pages')
admin.resources(:links, :controller => 'Admin/Links')
admin.resources(:lists, :controller => 'Admin/Lists')
admin.resource(:account, :controller => 'Admin/Account') do |account|
account.resource(:plan, :controller => 'Admin/Plan', :name_prefix => 'account_')
account.resource(:payment, :controller => 'Admin/Payment')
end
admin.resources(:plans, :controller => 'Admin/Plans')
admin.resources(:emails, :controller => 'Admin/Emails')
admin.resources(:aliases, :controller => 'Admin/Aliases')
end
Everything above will be available under the subdomain of “admin”. Please note that Merb currently calls the to_s method on conditions, and so if someone put in a.d.m.i.n.yourdomain.com, it would match as well as admin.yourdomain.com.
More to come
Merb comes with helpers to create the form with the _method defined for you in the ‘merb-more’ gem.