Beginners Tutorial: Routing in Rails 2.0 (with REST) – Part 5 of n

Part 6

In part 4 we explored named routes. In this session we move onto Rails’ REST.

Part 5

The Critics

Before we begin you should know that there are many critics of the Rails implementation of REST. There are many reasons for arguments against Rails’ REST e.g. Rails uses server side session state and client side cookies. Some people say that this is not REST compliant. Rails’ REST might not be 100% REST compliant, but you can still benefit from using REST in your Rails applications. In The Rails Way, Obie Fernandez explains that the benefits from using Rails’ REST fall into two categories:

  • Convenience and automatic best practices for you
  • A REST interface to your application for the rest of the world

Therefore it is worth learning and using Rails’ REST.

The Standard Introduction

Representational State Transfer (REST) is an architectural style. It was first introduced by Roy Fielding in his PhD thesis. Roy Fielding is one of the principal authors of HTTP.

The Mindset

When developing a Rails application in the RESTful way it helps to change your mindset. You should not think that you are developing a web site for users accessing the site via a browser. It is better to think of the application as being a service. The service performs actions for clients. These clients may be humans or other machines on the network.

This service will typically be placed on the internet. Therefore we could call it a web service. When I hear the term web service I immediately start thinking about SOAP and all the WS-* specifications. Let’s be clear, we are not talking about ‘those’ web services. We are talking about RESTful web services. These two technologies provide similar functionality, but RESTful web services are simple and sensible.

The Terminology

As previously stated, programming in REST requires a change in the way we think about the system. This naturally leads to a change in the way we talk about the system. The following provides a brief description of some of the key terms used in discussions about REST and HTTP.

A resource is one of the key concepts in REST. Many discussions on REST claim that a resource is anything and everything. This is neither true nor helpful. Let’s examine this concept with the aid of an example.

Let’s assume that the music store has started selling albums. The database may have the following schema:

orders erd

The data in the database is the raw data. There is data which, given in isolation, is of no real value to a client. For instance it is unlikely that a client will want only one row from the LineItems table. This means that a LineItem (on its own) is not a resource within the context of the music store service.

A client would want all the information pertaining to an order. This would include data from one or more tables. Some data would not be included e.g. database keys. Some information may be added e.g. the total cost of the order might be calculated and included in the ‘order information’. This ‘order information’ is an example of a resource. Order resource is the type, there will be one order resource instance for each order made.

Each resource must be given a globally unique identifier. This is done using a URI. This could look like this:

http://www.example.com/orders/65

A client, in an admin role, may want a list of all the orders. This collection of orders would be another resource. Following the REST architecture, the orders resource would be assigned the following URL:

http://www.example.com/orders

It is important to note that these resources are not HTML documents. Roy Fielding describes a resource as a conceptual mapping. As a resource is a conceptual entity it can not be used by a client. The service provides the client with a representation of the resource.

When the client is a browser, the service typically provides an HTML representation of the resource. If the client is another machine then the service may provide an XML representation of the resource. Other representations could include a PDF file, an image, csv etc. A representation is only supported by the service if it is applicable and required.

Resources are often referred to as nouns. This is often done when talking about HTTP to assist learning. The term ‘noun’ is more abstract. For our purposes, resources and ‘nouns’ are synonymous.

HTTP also has the notion of verbs. These are the HTTP operations. We are all familiar with the POST and GET operations. In addition to these two operations, HTTP also supports: DELETE and PUT.

There are a few more terms to cover, but we can do that later. The core terms have been explained.

Joining The Dots…

REST is an architecture style which makes full use of HTTP. Some say that it makes proper use of HTTP.

As previously explained, HTTP supports GET, POST, DELETE and PUT. One of the main ideas behind REST is to use these HTTP operations to determine the correct action to perform. Let’s clarify this with an example.

If the RESTful music store server receives a URL of: /albums/4 sent with a GET, then the server will invoke the show action of the albums controller. The response may be an HTML page showing details of the 4th album.

If the server receives a URL of: /albums/4 sent with a DELETE, then the server will invoke the destroy action of the albums controller. This will result in the 4th album being deleted.

Note that the URL did not contain explicit ‘show’ or ‘delete’ information. The server was able to infer the action by examining the HTTP operation. Therefore show and delete were implicitly called.

The form of that URL is REST compliant. If the music store had to include Artists and Songs, we could expect to see URLs like this:
/artists/2
/songs/8

As you can see a consistent API is forming, this is the REST API. It becomes intuitive once you start getting familiar with it. For instance I am sure you can tell what would happen if we did a DELETE on this URL:
/songs/154

An application typically performs the full set of CRUD operations – Create Read Update and Delete. The following table shows the mapping between database CRUD operations, the HTTP verbs, and REST compliant URLs.

Database HTTP Operation URL Path
Create POST /albums
Read GET /albums/1
Update PUT /albums/1
Delete DELETE /albums/1

When creating a new album, the plural /albums is used. This is because we are adding a one new album to the collection of albums.

In previous posts in this series, creating and editing an album has always involved two controller actions being invoked. When we create an album we:

  1. Call the new action to retrieve a form with empty fields
  2. Call the create action which creates a new album extracting the information from that form

When doing an update we:

  1. Call the edit action to get a form with fields filled in with data from the relevant album
  2. Call the update action which then performs the actual database update

This is the typical behaviour of a website. We need to fit this behaviour in with REST. When doing an edit we can’t obtain the albums details by doing a GET on /album/1 – that is already used for showing an album.

This functionality is achieved with modifiers (also called adjectives). The modifiers we need are ‘new’ and ‘edit’. This results in the following mapping:

Database Controller Action HTTP Operation URL path
Create create POST /albums
Read show GET /albums/1
Update update PUT /albums/1
Delete destroy DELETE /albums/1
- new GET /albums/new
Read edit GET /albums/1/edit
Read index GET /albums

Note the last line which calls the index action.

As you can tell, we are moving towards Rails specific terminology. I guess this means it is time to step into the code…

Experiment 5.0 Preparation

In part 4 we created named routes. The routes.rb file looked like this:

ActionController::Routing::Routes.draw do |map|  

 map.albums 'albums',
 :controller => "albums",
 :action => "index"

 map.album 'album/:id',
 :controller => "albums",
 :action => "show"

 map.edit_album 'albums/:id/edit',
 :controller => "albums",
 :action => "edit"

 map.update_album 'albums/:id/update',
 :controller => "albums",
 :action => "update"  

 map.destroy_album 'albums/:id/destroy',
 :controller => "albums",
 :action => "destroy"     

 map.new_album 'albums/new',
 :controller => "albums",
 :action => "new"  

 map.create_album 'albums/create',
 :controller => "albums",
 :action => "create"  

 #map.connect ':controller/:action/:id'
 #map.connect ':controller/:action/:id.:format'

end

In this session we move from named routes to RESTful routing. Therefore we can delete these named routes. Empty the routes.rb file:

ActionController::Routing::Routes.draw do |map|  

 #map.connect ':controller/:action/:id'
 #map.connect ':controller/:action/:id.:format'

end

Once again we have a broken music store. Let’s fix it and make it REST compliant.

Experiment 5.1 Including REST Routes

We want the music store application to be REST compliant. At the moment we don’t make use of the HTTP operations, instead we explicitly state the intentions within the URL. At the end of part 4 the site worked like this:

Database Controller Action HTTP Operation URL path
Create create POST /albums/create
Read show GET /albums/1
Update Update POST /albums/1/update
Delete destroy GET /albums/1/destroy
- new GET /albums/new
Read edit GET /albums/1/edit
Read index GET /albums

The application was fairly similar to REST. This is because the named routes created in part 4 were based on the REST style. To make the application more RESTful we need to make use of the HTTP verbs. We could update each of our named routes to explicitly state the HTTP operation e.g.

  map.albums 'albums',
    :controller => "albums",
    :action => "index",
    :method => "get"
 

Rails has made this easier for us. Update the routes.rb file to:

ActionController::Routing::Routes.draw do |map|  

  map.resources :albums

  #map.connect ':controller/:action/:id'
  #map.connect ':controller/:action/:id.:format'

end

Thanks to this one line, Rails will create a set of named routes – intelligent named routes. They are intelligent because they examine the request URL as well as the specified HTTP operation. Therefore the same URL can be routed to deferent controller actions.

As you may recall from part 4, when you use named routes Rails dynamically generates routing helper methods. These helper methods are also available on these new ‘intelligent’ named routes.

Experiment 5.2 Showing the Index Page

Let’s have a look at the table:

Database Controller Action HTTP Operation URL path
Create create POST /albums
Read show GET /albums/1
Update update put /albums/1
Delete destroy DELETE /albums/1
- new GET /albums/new
Read edit GET /albums/1/edit
Read index GET /albums

To show a list of albums we need to perform a GET with a URL of /albums. We can perform a get by simply typing the URL into the browser and pressing enter. If you try that you should end up with an error.

undefined method `destroy_album_path’ for #

This is good news as we did not get a route not found error. To get the index page up and running we need to replace the named route helper methods we introduced in part 4 with the helper methods created by ‘map.resources :albums’. The following table shows these ‘new’ helper methods.

Helper Method Controller Action HTTP Operation URL Path
albums_url create POST /albums
album_url(@album) show GET /albums/1
album_url(@album) update put /albums/1
album_url(@album) destroy DELETE /albums/1
new_album_url new GET /albums/new
edit_album_url edit GET /albums/1/edit
albums_url index GET /albums

This table shows methods ending with _url, the _path versions also exist. This was explained in part 4. By making use of these helper methods we can update the index.html.erb.

Thanks to our foresight in part 4, we only need to change the call to delete, from:

link_to ‘Destroy’, destroy_album_path(album.id), :confirm => ‘Are you sure?

To

link_to ‘Destroy’, album_path(album), :confirm => ‘Are you sure?’, :method => :delete

Just to be clear, the index.html.erb should look like this:

<h1>Listing albums</h1>
<table>
<tr>
<th>Title</th>
<th>Review</th>
</tr>
<% for album in @albums %>
<tr>
<td><%=h album.title %></td>
<td><%=h album.review %></td>
<td><%= link_to 'Show', album_path(album)  %></td>
<td><%= link_to 'Edit', edit_album_path(album) %></td>
<td><%= link_to 'Destroy', album_path(album), :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %></table>
<%= link_to 'New album', new_album_path %>

A URL of /albums should show the index page – test it if you like.

Alert!
Link_to obtains the URL by making use of another Rails method called ‘url_for’. The following quote is from the Rails documentation and it refers to the parameters being passed into url_for.

“If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter, you‘ll trigger the named route for that record. The lookup will happen on the name of the class. So passing a Workshop object will attempt to use the workshop_path route”

This means that we can replace
link_to ‘Show’, album_path(album)
with
link_to ‘Show’, album

And
link_to ‘Destroy’, album_path(album), :confirm => ‘Are you sure?’, :method => :delete
with
link_to ‘Destroy’, album, :confirm => ‘Are you sure?’, :method => :delete

I am sure this bit of Rails magic has been the cause of much confusion.
This magic is also used in other methods e.g. redirect_to, form_for etc.

The index.html.erb can now look like this:

<h1>Listing albums</h1>
<table>
<tr>
<th>Title</th>
<th>Review</th>
</tr>
<% for album in @albums %>
<tr>
<td><%=h album.title %></td>
<td><%=h album.review %></td>
<td><%= link_to 'Show', album  %></td>
<td><%= link_to 'Edit', edit_album_path(album) %></td>
<td><%= link_to 'Destroy', album, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %></table>
<%= link_to 'New album', new_album_path %>
 

A URL of /albums should now render the index page.

Experiment 5.3 The RESTful Show

Thanks to the naming convention we used in part 4, we do not need to do anything to make show work. Show.html.erb remains like this:


  <b>Title:</b>
  <%=h @album.title %>

  <b>Review:</b>
  <%=h @album.review %>

<%= link_to 'Edit', edit_album_path(@album) %> |
<%= link_to 'Back', albums_path %>
 

Experiment 5.4 Edit RESTfully

To fix update we need to make use of the following methods in the edit view.

Helper Method Controller Action HTTP Operation URL Path
album_url(@album) update put /albums/1
edit_album_url edit GET /albums/1/edit

The form_for allows us to use more syntactic sugar.

We could use:
form_for (@album), :url => album_path(@album)

Or we could use:
form_for (@album)

This means the edit.html.erb should look like this:

<h1>Editing album</h1>
<%= error_messages_for :album %>

<% form_for (@album) do |f| %>

    <b>Title</b>
    <%= f.text_field :title %>

    <b>Review</b>
    <%= f.text_area :review %>

    <%= f.submit "Update" %>

<% end %>

<%= link_to 'Show', @album %> |
<%= link_to 'Back', albums_path %>

Experiment 5.5 Putting Delete to REST

This was fixed when we modified the index.html.erb.

The line was:

link_to ‘Destroy’, album, :confirm => ‘Are you sure?’, :method => :delete

Experiment 5.6 A RESTful Addition

In order to restore the ‘new’ functionality, make new.html.erb look like this:

<h1>New album</h1>
<%= error_messages_for :album %>

<% form_for (@album) do |f| %>

    <b>Title</b>
    <%= f.text_field :title %>

    <b>Review</b>
    <%= f.text_area :review %>

    <%= f.submit "Create" %>

<% end %>

<%= link_to 'Back', albums_path %>

Experiment 5.7 Updating the Controller to use the Syntactic Sugar

When using a resource the controller must implement these specific methods:
index, show, new, edit, create, update, destroy.
Our controller already meets this condition.

Although the music store is fully operational, we can make some minor modifications in the controller. This is just to leave the controller in the way Rails generated it in part 1. These changes are in the create and update actions e.g. redirect_to(@album)

The controller should look as follows:


class AlbumsController < ApplicationController
  # GET /albums
  # GET /albums.xml
  def index
    @albums = Album.find(:all)
    @cont = "The controller is:" + params[:controller]
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @albums }
    end
  end

  # GET /albums/1
  # GET /albums/1.xml
  def show
    @album = Album.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @album }
    end
  end

  # GET /albums/new
  # GET /albums/new.xml
  def new
    @album = Album.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @album }
    end
  end

  # GET /albums/1/edit
  def edit
    @album = Album.find(params[:id])
  end

  # POST /albums
  # POST /albums.xml
  def create
     @album = Album.new(params[:album])  

     respond_to do |format|
       if @album.save
         flash[:notice] = 'Album was successfully created.'
         format.html { redirect_to(@album) }
         format.xml  { render :xml => @album, :status => :created, :location => @album }
       else
         format.html { render :action => "new" }
         format.xml  { render :xml => @album.errors, :status => :unprocessable_entity }
       end
     end
  end  

  # PUT /albums/1
  # PUT /albums/1.xml
  def update
     @album = Album.find(params[:id])  

     respond_to do |format|
       if @album.update_attributes(params[:album])
         flash[:notice] = 'Album was successfully updated.'
         format.html { redirect_to(@album) }
         format.xml  { head :ok }
       else
         format.html { render :action => "edit" }
         format.xml  { render :xml => @album.errors, :status => :unprocessable_entity }
       end
     end
   end  

  # DELETE /albums/1
  # DELETE /albums/1.xml
  def destroy
    @album = Album.find(params[:id])
    @album.destroy

    respond_to do |format|
      format.html { redirect_to(albums_url) }
      format.xml  { head :ok }
    end
  end
end

And that is that – our music store is exactly how it was when we generated it in part 1.

End of Part 5

To keep things ‘simple’ I have been skipping over certain aspects of routing. I will draft one more post in this series where I will mention some of these topics. Some of the readers have asked questions, I will still answer those questions but they won’t form part of this tutorial.

That concludes part 5. Hopefully you have a better understanding of what is going on under the hood.

Thank you, Daryn

Part 6

About these ads

50 thoughts on “Beginners Tutorial: Routing in Rails 2.0 (with REST) – Part 5 of n

  1. Pingback: Beginners Tutorial: Routing in Rails 2.0 (with REST) - Part 4 of n « YAB

  2. This placed a lot of issues into context, and actually is one of the few entries on how Rails 2.0 works. For those of us wanting to understand how the magic behind scaffolding works, this is invaluable. Thank you.

  3. Awesome tutorial! I felt I was in a classroom with very good teacher.
    Being Rais newbee, and lazy to go through the books, this gave me good idea about routes, as it is very important.
    I will look forward Daryn to address other topics in Rails too.
    Thank you very much Daryn.
    Keep posting.

  4. It has come to my attention that many of the comments are being marked as spam. I will keep a closer eye on the spam box from now on. My apologies to those of you who have had your comments spammed.
    - Daryn

  5. Nice post :)

    I’ve been wondering how the creation process works, since it has to be a two-step process if you do it using a web interface in order to know which fields should be posted during creation. Something that could be useful is to reserve an ID for a new resource when you do a “new”, instead of determining it during the “create”. Then you don’t have to inject the new ID into the posted parameters (i.e. you can do a 1-1 mapping/translation from posted params to a tuple). Alas, I haven’t tried it out (thinking in Erlang now), so it might not work so well….

    P.S. I think there’s a mistake, in that the URL for edit in your tables should be “albums/1/edit”, as opposed to “albums/edit”

  6. Thanks Benjamin! Just when I thought I had finally created a 100% error free post, you come and rain on my parade! That’s it, no more Erlangers allowed :P

    I do not think it is a good idea to reserve an ID for a new resource when you do a “new”. Firstly, There is a chance that the browser\client does not complete the second step e.g. the browser may not call for the create. A client could do a type of ‘syn-flood attack’, leaving multiple reserved IDs that will never be used.

    Secondly, this would break one of the constraints of REST. I did not go into it in this post, but all the GETs should be idempotent. They should be able to be called time and time again, without having multiple effects on the resources. Theoretically speaking this is also true for the DELETE and PUT e.g. it does not matter if you DELETE a specific resource once or a 100 times, the result is the same. Calling delete on something that has already been deleted has no effect.

    Thanks for pointing out the error…thanks a lot! :)

  7. first off, I’m totally new to rails so go easy…

    so i totally get what’s happening here and why, if i was to add a new method to my controller do i just need to make sure that i do a map.connect line BEFORE my map.resources? If i just add a new method and a corresponding .erb file when i try and access it as // in the url it tries to run the show method with id=

    thanks for the great tutorials,

    john.

  8. Hi Johnb,

    I am glad to help – if you are new to Rails you are more than welcome here.

    It seems like you would like to add a non-RESTful action to your controller – I am assuming you have a good reason to do that.

    I think you are on the right track. Have you declared a simple route, as explained in part 2? If you have, you may be using an incorrect URL.

    Could you provide your simple route and the URL you are using?

  9. Thanks for the series of articles demystifying the rails (black box) magic of routing. Not a F1 developer yet so need to do more groundwork.

    When we click on a link with the page returned – does include some magic that we may never fully understand without help.

    An idea for future articles would be to describe the methods and variables uncounted along with there lifecycle as they pass through our project.

    Another associated set of articles would be to describe the common useful sibling method and variables not used in the above example.

    Tony

  10. daryn,
    yep, that’s exactly what I’m trying to do, I have a method called ‘cloud’ in my tags controller, so i’ve added;

    map.connect ‘/tags/cloud’, :controller => ‘tags’, :action => ‘cloud’

    to my routes file, I’ve still got the resources in there too – I think i’m safe to mix them, I’ve tried my non REST route before and after my resources but still get;

    ActiveRecord::RecordNotFound in TagsController#show
    Couldn’t find Tag with ID=cloud

    thanks for your help,

    john.

  11. Johnb,

    I think the order of your routes are incorrect.
    Try changing the order e.g.

    ActionController::Routing::Routes.draw do |map|  
    
      map.connect ‘/tags/cloud’, :controller => ‘tags’, :action => ‘cloud’
      map.resources :tags  
    end
    

    Then use a URL of:
    /tags/cloud

    it will call the cloud action on your tags controller. You should not be using the params[:id] in this action, as you are not passing it through.

    Doing a GET on /tags will call the index action on the tags controller.
    It might be a good idea to use this to retrieve the tags, which is the tag cloud. I think that will be more RESTful, as creating a new tag is a POST on /tags

    Let me know how this works out
    - Daryn

  12. hi daryn, I’d tried that ordering; still get
    ActiveRecord::RecordNotFound in TagsController#show
    Couldn’t find Tag with ID=cloud

    but i think i’m with you in that /tags should show the cloud. But still, I should be able to figure out how to add additional methods to controllers and route accordingly – will persevere

  13. You are correct, we should figure it out.

    I think it is an ordering problem. Are you using the default route? (part 3)
    e.g. map.connect ‘:controller/:action/:id’

    That could be executing instead of the routing rule you want to use.
    If you are using the default route, place it last in the list.

    You may want to empty out the routes.rb, so that your rule is the only one left.

    You may want to play around with route generation and recognition: (part 1)
    e.g:

    rts.recognize_path …
    rts.generate …

    Let me know if this helps you, if not I can email you and we can sort it out via email.

    Please let me know if this has fixed your problem…

  14. Ok, to be sure I sure created a brand new rails proiject.

    I scaffolded a single item with a few properties. I went into my new controller and added an empty method ‘testmethod’ at the top and corresponding testmethod.html.erb file.

    I edited the routes.rb file to contain

    ActionController::Routing::Routes.draw do |map|
    map.connect ‘musics/testmethod’,:controller=>’musics’,:action =>’testmethod’
    map.resources :musics
    end

    then tried to hit http://ip/musics/testmethod and got

    ActiveRecord::RecordNotFound in MusicsController#show
    Couldn’t find Music with ID=testmethod

  15. thanks to some email conversation we’ve fixed this now – my own fault, rushing around – seemed to be some caching going on somewhere to so restarting my dev server fixed that too….cheers Daryn!

  16. Thanks! Great tutorials it really helped a lot. Could you provide the source of where you learned this, or further documentation? I know you said you were done with your tutorials but in case you change your mind an article on nested resources would be great.

    And I found one error (I think). I believe the fourth instance of ‘HTML’ should be ‘HTTP’.

    Thanks again

  17. Hi Orbit,

    I am glad you liked the tutorial. Thanks for spotting the typo, I have fixed it.

    I have been contemplating doing a part 6 of this tutorial, which will cover nested routes. That is missing from the tutorial. I will try to get it done soon.

    With regards to the list of resources – I often refer to books and other on-line within the post. Are you looking for information about Rails or REST?

    - Thanks, Daryn

  18. This is an excellent tutorial, thank you very much!

    One question though: at the beginning of this article, you said that it would be unlikely that anyone would want a row from the LineItems table. What if, however, you wanted to allow a consumer of this web service to add Orders for a particular album, or Albums to a particular Order? Can you do this without exposing the LineItems table?

    Most of the tutorials I’ve seen don’t go beyond either viewing ALL records in the database, or just a particular one. What if you want to only send a subset of all the records (for example, ask the server for all albums under a particular order)? What is the RESTful way of routing a call like that?

    Perhaps some of this is what you’re planning for part 6 ;-)

    Thanks again, I really have enjoyed this series!

  19. Pingback: Beginners Tutorial: Routing in Rails 2.0 (with REST) - Part 6 of n « Project Entropy

  20. Hi mikequinn, thanks for the interesting questions.

    Just to be clear, I did not say that the line items table should be hidden from the client. I said it is unlikely that a client will want only one row from the LineItems table i.e. in isolation. The client would typically want the complete order information e.g. with the total cost, expected delivery dates etc.

    When making an order the client would be expected to supply more than just product and quantity information. The the ‘order creation data’ would need to contain the delivery address, contact information etc.

    Therefore a line item is not necessarily hidden from the user, it’s just not a REST resource on its own. It forms part of the order resource. Remember a resource is a conceptual entity – more commonly referred to as a conceptual mapping.

    With regards to your second question, you were right about it being covered in part 6. Part 6 is now out, please let me know if it has answered your question.

    Thanks, Daryn

  21. Have to stop here for now, but I want you to know this tutorial is saving my butt! I’m trying to learn Rails on the 2.X version, and so many of the books out there are for 1.0 that it’s endlessly confusing. If one codes along to this tutorial it makes clear concepts that were hopelessly muddy before. This is the ideal tutorial for beginners!

  22. Hi Jay, I’m glad the tutorial is helping.
    Good luck with your Rails application development!

  23. Pingback: stlouispersonalinjuryhelp.com

  24. You got a nice post on rs Tutorial: Routing in Rails 2.0 (with REST) – Part 5 of n « Project Entropy. Really very nice to read and useful, thanks for the nice share.

  25. Very nice tutorial! Very easy to grasp and works on fundamentals! Thanks so much

  26. These tutorials are really excellent, and masterfully organized to teach the concepts effectively. The way you took us from the generated RESTful routes, to basic routes, to named routes, and then back to RESTful routes is really effective. You explained not only the mechanics, but also the motivation for the way Rails does routing.

    I could use some more information about one aspect of RESTful routing. I am very puzzled as to how the different HTTP operations get specified. For example, the index.html.erb template has the lines:

    which results in generated HTML such as the following for each album:

    Dark Side of the Moon
    The classic album by Pink Floyd.
    Show
    Edit
    Destroy

    My question is this: Since the link for Show and Destroy have exactly the same href, how does Show invoke the GET HTTP operation, while Delete invokes the DELETE HTTP operation?

    Thanks again for the excellent tutorial.

    Jay

  27. Hi Jay, thanks for the question, although it looks like WordPress has eaten parts of your question.

    The answer is that SHOW and DELETE do not have the exact same href, and neither does PUT. When performing a DELETE or a PUT, Rails actually does a POST with a hidden field. The hidden field is called ‘_method’ and contains a value of either ‘put’ or ‘delete’.

    You can verify this by viewing the html source in a browser. The hidden field is easier to see if you view the source on an edit\update page. The index page uses JavaScript to add the ‘_method’ hidden field. If you disable JavaScript, then clicking on ‘delete’ will perform a show.

    Rails needs to use a hidden field because most browsers only support ‘POST’ and ‘GET’.

    Did this help to clear things up?

    Kind regards, Daryn

  28. Hi
    Thanks for lots of info presented in plain easy to follow english. I have been unable to find anything as comprehensive anywhere.
    At last I have many questions answered.

    Keep up the great walk

    A very newby

  29. hi all. as an experiment i decide to: rake routes , considering my routes.rb file
    has only: map.resources users. well, we all know the output, but based on this output
    the last 3 mappings which are: show(get), update(put) and destroy(delete):

    user GET /users/:id {:action=>”show”, :controller=>”users”}
    PUT /users/:id {:action=>”update”, :controller=>”users”}
    DELETE /users/:id {:action=>”destroy”, :controller=>”users”}

    based on the output above i decided to write the equivalent routes in the routes.rb file in order to mimic what map.resources does. so we will have 7 routes. to simplify let’s just consider here these last 3 routes. first, based on the output above, my conclusion is that i could use 3 routes with the same name: map.user and differentiate then with the :method symbol, being get, put and delete for each one of them … so rails would know which one to use based on the :method

    map.user ‘users/:id’, … , :method => :get
    map.user ‘users/:id’, … , :method => :put
    map.user ‘users/:id’, … , :method => :delete

    well, that doesn’t work … if i click delete in the view then the show action will be triggered. my 2nd conclusion is that since i’m not using map.resources and instead i’m using named_routes, then the routing system will use the top-first rule

    my doubt is, how rails understand map.resources since the output from rake routes doesn’t imply us to do something like:

    map.user_show ‘users/:id’, … , :method => :get
    map.user_update ‘users/:id’, … , :method => :put
    map.user_delete ‘users/:id’, … , :method => :delete

    plus if i named_route like the sample above, i don’t need to specify which method to use, cause the routes are named. right?

    then my 3rd conclusion is that rails should understand:

    map.user ‘users/:id’, … , :method => :get
    map.user ‘users/:id’, … , :method => :put
    map.user ‘users/:id’, … , :method => :delete

    based on the :method differentiation. i also did: :requirements => {:method => :delete} in routes.rb or user_path(user) in my view, but no success.

    obs. my delete link_to code:
    link_to ‘delete’, user, :confirm => ‘Confirm delete?’, :method => :delete

  30. So, I have an issue :(

    everything in your tutorial #5 here when I follow it exactly.

    However Since lesson 1 I have another link called ‘Display’ it is follows the same pattern as ‘Show’ in the controller and the code in “display.html.erb” is the same except the title is DISPLAY

    This worked in lessons 1-4 but now in lesson 5, its link just takes me to to “show.html.erb”

    I am guessing that “Rails will create a set of named routes – intelligent named routes.” isn’t as intelligent as I would like it :(

    (realistically being new at this I expect I don’t understand this enough and am missing something obvious)

    Any help would be greatly appreciated by anyone.

    Thanks

  31. The create and edit views (Experiment 5.4 and 5.6) both use the same form_for parameters. How does rails know to use POST vs. PUT? Assuming POST is the default and that triggers the create action, shouldn’t form_for in the edit view specify, :html => { :method => :put }, as a parameter?

    I haven’t tried the code above but if it does work does rails automatically check if the object passed to form_for is new and unsaved somehow before deciding on POST vs. PUT automagically?

    Sometimes all this syntactic sugar gives me a headache.

  32. Hi Emice,

    Rails does check to see if the object is new or not. All ActiveRecord objects have an is_new? method.

    You are not alone, many people feel that Rails has too much syntactic sugar – too much syntactic sugar causes cancer of the semi-colon :P

  33. I think there is a mistake in last two tables in row

    edit_album_url edit GET /albums/1/edit

    I thing that there should be edit_album_url(album) instead of edit_album_url, as is in all code samples.

    Jakub

    PS: Thanks for great tutorial. Pity, that there is no part 7.

  34. You have a bug in your code in these tutorials.
    Several times you break the :confirm=>’are you sure you want to delete this’ functionality by missing out the hash delimiters {} around the other symbol / value pairs eg:

    ‘Delete’, :id=>@object, :action=>’Foo’, :controller=>’Bar’, :confirm=>’Wibble’
    Instead of
    ‘Delete’, {:id=>@object, :action=>’Foo’, :controller=>’Bar’}, :confirm=>’Wibble’

    Awesome tutorials though, really sorrted my understanding out!
    Cheers

  35. Just finished part 5. Man this stuff is great. I now have a very good understanding of not only routes but ruby on rails. This puts me way ahead of my schedule to develope an internal web site using Ruby on Rails.
    Suggestion. When you show code and say there are some changes to be made it would be great if you highlighted the code that is to be changed.

    Nice work keep it up

  36. Hi Daryn,

    What a great tutorial, congratulations.

    I have a question for you. What if I want to use RESTful routing and different method names (e.g. ‘novo’ instead of ‘new’, which is the portuguese translation)? Is it possible to be done without using named routes?

    Thanks in advance.

  37. It gets confusing!

    The RESTful part is intertwined with the other parts bringing up confusion and making the reading even useless for people looking for this specific information.

    It’s a great job indeed, but RESTful web services should be present entirely apart from the rest of the parts in order to understand them easily.

  38. This series of tutorials is great. I’ve been searching everywhere for the magic behind Rails routing and this helped a lot. Many thanks.

  39. No problem, I am glad I could help. I hope I can find the time to start on the next series of posts.

    - Daryn

  40. Great tutorial. I’ve got a number of books:
    Practical REST on Rails 3 Projects
    Professional Ruby on Rails, etc
    They’re not enough, so your tutorial is valuable to me. But it’s formatted so that ofter the right end of the line is cut off; scrolling won’t help. I’m viewing it in full-screen Firefox 3.6.

    I’ve looked a the document’s source, but that’s too complicated for me to do anything helpful.

    Any ideas?

  41. Hi Richard,

    WordPress has eaten most of the examples. You can try using the copy to clipboard feature, to copy paste into a text editor. Unfortunately it looks like the content has also been changed (note all the smiley business). From now on I will use a different approach, like screen shots…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s