Page 1 of 1

Rails: Routes Explained - Part 2 A continued look at one of Ruby on Rails most useful features.

#1 Skaggles  Icon User is offline

  • THE PEN IS MIGHTIER
  • member icon





Reputation: 251
  • View blog
  • Posts: 640
  • Joined: 01-March 09

Posted 23 February 2010 - 05:07 AM

Rails: Routes Explained - Part 2

This second and final part will cover a few more ways to route URLs with Ruby on Rails. I'll be taking what I talked about in Part 1 further in this tutorial, so I suggest you read the first part if you haven't already. This one can get a little more advanced, so if you're completely new to Ruby on Rails, you might get lost. I'll try to make things as simple as I can, but some of these features aren't easy to explain.


Named Routes

Ruby on Rails has the ability to let you create named routes. A named route is just like a regular route, but with a name. Duh? The main benefit from using named routes is that it will make creating links to these actions much easier. An example of a couple named routes are:

map.login '/login', :controller => 'users', :action => 'login'
map.logout '/logout', :controller => 'users', :action =>' logout'
map.register '/register', :controller => 'users', :action => 'register'


You'll see I'm using the same routes for login/logout URLs as I did in the previous tutorial. The one difference that you'll notice is that instead of using map.connect, I'm using map.login.

Let me explain by comparison. Before, with the previous tutorial, your routes looked like:

map.connect '/login', :controller => 'users', :action => 'login'


Which would allow a user to access the login action located in the users controller with a URL like, http://yourdomain.com/login, instead of http://yourdomain/users/login. In order for you to provide a link to this page in your views you would need to insert this code:

<%= link_to 'Login', :controller => 'users', :action => 'login' %>


You can see that, even though you made the route for the user, you're still having to type all this code every time you want the link to be displayed. Well, with named routes you can define the same route like this:

map.login '/login', :controller => 'users', :action => 'login'


This will achieve the same affect for your users being able to access the page through http://yourdomain.com/login, but now you can provide a link to this action with the line:

<%= link_to 'Login', login_path %>


By using named routes, Rails will automatically create a ROUTENAME_path link to your actions so that your life as a developer is that much better. You'll notice that I used login_path, but it also creates login_url, which will return the full URL. Basically, you'll get two named routes--ROUTENAME_path and ROUTENAME_url.

Now creating links to the login, logout, register are much easier:

<%= link_to 'Login', login_path %>
<%= link_to 'Logout', logout_path %>
<%= link_to 'Register', register_path %>



RESTful resources

Ok, now we get into the cool stuff. For this I will make the assumption that you know what REST and CRUD are and how to create your controllers in a RESTful way. If you aren't sure what REST is, then I suggest you look into it a little--it will greatly improve your knowledge for your future web applications, and able you to better understand this section of the tutorial.

This time I'm going to use a Task controller with the following actions:

  • index # lists all tasks
  • new # form for new task
  • create # creates a new task
  • show # shows a specific task
  • edit # edit form for a specific task
  • update # updates a specific task
  • destroy # deletes a specific task


You'll see I've commented on what each method does. To route to these actions you might think you'd use something like these:

map.connect '/index',       :controller => 'tasks', :action => 'index'
map.connect '/new',         :controller => 'tasks', :action => 'new'
map.connect '/create',      :controller => 'tasks', :action => 'create'
map.connect '/show/:id',    :controller => 'tasks', :action => 'show'
map.connect '/edit/:id',    :controller => 'tasks', :action => 'edit'
map.connect '/update',      :controller => 'tasks', :action => 'update'
map.connect '/destroy/:id', :controller => 'tasks', :action => 'destroy'


While, these do work, it's a lot of code and it's completely unnecessary. With HTTP, every URL is called by either a POST or a GET request, but there are also PUT and DELETE (or CRUD - Create, Read, Update, and Delete). Most web browsers only support POST or GET requests. So, to work around this, Rails has it's own ways to handle the PUT and DELETE requests. I'll cover how this works in a bit.

So, without further delay, I give you the above routes done via map.resources:

map.resources :tasks


Wow, huh? I know it's only one line of code, but it's doing so much. This just mapped your entire tasks controller to several routes. Now you can link to the tasks controller actions with calls like:

# link the the 'index' action
<%= link_to 'Tasks', tasks_path %>

#link to the 'new' action
<%= link_to 'New Task', new_tasks_path %>


You will also have these others to work with now:

  • edit - edit_task_path(@task)
  • create - task_path(@task)
  • show - task_path(@task)
  • update - task_path(@task)
  • destroy - task_path(@task)


These all require you to pass the task with the URL. But wait, what are those last four? They are all the same! How can Rails know which action to use? Well that's where HTTP requests comes into play. You see, each one of those four require a different HTTP request. You set what type of request these are with code like the following:

<%= link_to 'Show Task' task_path(@task) %>
<%= link_to 'Delete Task', task_path(@task), :method => :delete %>


Since HTTP requests default to GET, the show task doesn't require a method change and passes the task's id as a GET request, but now Rails knows that you're calling the destroy action when you set the method to DELETE. Awesome!

As for Update and Create, you'll be using forms, which default to POST requests when submitted. So, again, the create action doesn't need a method change, but update will need to be set to PUT:

# create form
<% form_for :task, :url => tasks_path do |f| %>
  # form stuff here
<% end %>

# update form
<% form_for :task, :url => task_path(@task), :html => { :method => :put } do |f| %>
  # form stuff here
<% end %>


Now when you submit, Rails will see that the second is a PUT request and knows that means to use the update action. This can be a lot to take in, especially if you're not too familiar with REST or CRUD. Acronyms, ack! Just don't bang your head into the wall just yet. Take some time to read up on some of these and you'll realize that it's not too hard to get a grasp on and you'll see how they can be helpful.

Since this is a fairly complex subject, I've uploaded the source code to a simple task application done using REST. You can check it out, browse through it, and see how the actions respond to these HTTP requests.

Link to source: restful_tasks


Nested Resources

If you think you got a handle on map.resources, then we'll jump into the last section of this tutorial which is Nested Resources. Nested resources are basically attaching one controller as a child of another. Here's a look at how this routes:

map.resources :tasks do |task|
  task.resources :comment
end


What this does is attaches a comment controller to every task you create. You also have to add these lines of code to the task and comment models:

# place this code in the task.rb model
has_many :comments

# and place this code in the comment.rb model
belongs_to :task


So now that we've told the models about the other, we can use the comments as part of a task object.

@task     = Task.find(task_id)  # get a specified task
@comments = @task.comments.all  # get all comments from the task

# or get a specific comment
@comment  = @task.comments.find(comment_id)


We can also call the comment actions through named routes, but we need to pass a task id so that it knows which task to pull comments from.

# links to comment 'index' action for a given task
<%= link_to 'Comments', task_comments_path(@task.id) %>

# links to comment 'edit' action for a given  task
<%= link_to 'Edit Comment', edit_task_comment_path(@task.id, @comment.id) %>

# a form for creating a new task comment
<% form_for :comment, :url => task_comments_path(@task.id) %>
  # form stuff here
<% end %>

# and lastly, to update comment for a task
<% form_for :comment, :url => task_comment_path(@task.id, @comment.id), :html => { :method => :put } %>
  # form stuff here
<% end %>


Since comments are now part of the tasks that you create, the full URLs pointing to the comment actions will end up looking something like these:



That's all I got. I tried to be as detailed and specific as I can regarding these subjects, but we're starting to cover topics that extend pass the purpose of this tutorial. If you're interested in getting more information regarding routes and resources, then I suggest taking a browse around the DIC forums or search google for 'ruby on rails nested resources'.

Thanks for reading and I hope it was informative to some extent.

Is This A Good Question/Topic? 0
  • +

Page 1 of 1