Basic Rail’s routing and a journey into Views, and Controllers

April 7, 2008

 
No Gravatar

Basic Rail’s routing and a journey into the controller

Thanks for tuning in! Last time we spoke, we set up a basic Rails application called the bookstore. Using the new 2.0 scaffolding, we were able to quickly, and effortlessly move from a high-level thought process to a living, breathing model. Now that you’ve toyed around with the application source, I’m going to explain our previous code and I’ll even throw a little wrench into your gears…basic Rail’s routing.

Open up your bookstore application, if you’ve lost it or found this post randomly…be sure to visit the original post “Ruby on Rails Tutorial, now with more 2.0.2!“. Then, fire up your server, and navigate to your application’s root, likely, http:/localhost:3000. If your code is untouched from the last time we met, you should encounter a “routing error” like so:

02_routing.jpg

That’s kind of annoying, and just a little bit ugy; it’s time we fix this! You may remember play with .htaccess and doing a mod_rewrite here and there, when you use the Rails framework you no longer place the burden of URL rewriting on the webserver, but instead, on Rails. Routes can get really complicated, really fast; so I am going to show you the very basics. Even after I show you how to play around with Rails’ routing, I will leave the application’s default routes alone. I will do this because in teaching, it is much easier explaining to you that:

http://localhost:3000/books/1

is calling on the “books” controller, with the “show” method, and an “id” of 1 verues trying to explain to you that,

http://localhost:3000/library/freakonomics

can do the exact same thing! All in time though, all in time. Anyways, open up theconfig/routes.rb file and you should see something like this:

Routes are listed in order of priority, the higher on the list they are, the higher priority they are. You’ll notice Rails’ default routes on the bottom:

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

Briefly, when a visitor types in the URL:

http://localhost:3000/books/1

Rails takes the HTTP request, looks through the routes listing, and systematically tries to find a match. With the URL above, the HTTP request will be matched to the first request and processed as follows:

http://localhost:3000/:controller/:action/:id

Hopefully this makes sense, I’ll touch more on this down the road. If you’re not entirely lost, recall that the reason we’re poking around the routes file is because we are getting a routing error when we attempt to access “http://localhost:3000″. At the end of the routes.rb file, add the following:

map.root :controller => "books", :action => "index"

“map.root” is the shorthand to name the root path “”; now whenever we navigate to the site’s root path we should see a call to the “books” controller and “index” action. Take note, you do not have to specificy the “index” action, as it is called by default, but I included it for sake of illustration. Navigate to your site’s root, and watch the magic.

Pretty neat, huh? Add a few more books, and then take some time to examine the URL structure of the database items you’ve entered. When you’ve done that, open up the “apps/models/book.rb” file. The file you just opened is our “Book model”, it acts as the gateway between any database queries. Right now it is empty, but it is worth pointing out. Now open the, “apps/controllers/books_controller.rb” file, you will see first entry is the index method.

  # GET /books
  # GET /books.xml
  def index
    @books = Book.find(:all)
 
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @books }
    end
  end

Since this code was generated by Rails, we have a few lines of commenting. What this means is, any GET request to /books will actaully call the “index” method (we’ll talk about the different request types later). When this method is called in, there are two things that happen.

  1. We are talking to our “Book model” and asking it to find all records, then we are saving those results as a Ruby hash in “@books”
  2. Next, depending on the requested format, responding with either an HTML template or XML template (more on this later)

For those readers who have some database expereince, check this out, if this doesn’t make sense, don’t worry.

    # ask the Book model to find :all records
    @books = Book.find(:all)
 
    # the resulting MySQL query
    SELECT * FROM books

Now, scroll down to the next method…it should be named “show”.

  # GET /books/1
  # GET /books/1.xml
  def show
    @book = Book.find(params[:id])
 
    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @book }
    end
  end

This time, instead asking the Book model to find :all records, we’re asking it to find by the “params[:id]“. What does “params[:id]” mean? The “id” parameter from the URL is accessed by “params[:id]” and in this case, the value is “1″. If we were to type the URL, “http://localhost:3000/books/5″, params[:id] would be set to “5″. Let’s look at this line of code:

@book = Book.find(params[:id])

What we’re doing is assigning the results of our database query, in this case, by asking the Book model to find the record with ID matching params[:id]. You’ll notice that all models are titleized and singular: in this case Book, but it could easily be Author or Category.

Now that you’ve seen how we are retrieving our data from the database, let me show you how to format that data for your visitors. Open your “app/views/books/index.html.erb” file. And you should see this:

If you’ve developed websites in the past, the show view should be a little more familar to you. You will notice a few things that look out of place, with PHP we enclosed our code with the <? code ?> tags, with Ruby, anything enclosed between <% code %> , <%= code %>, is called embedded ruby. Here are the differences:

  • <% code %> is evaluted, but not printed
  • <%= code %> is evaulated, and printed

Check this:

1
2
3
4
5
# this will display NOTHING in your HTML
<% "You will not see this" %>
 
# this will print the string
<%= "But you will see this!" %>

You can test the code by pasting the embedded ruby code, just below the “<h1>Listing Books</h1>” tag, and accessing your site’s root, like so:

See how that works? Now, the final piece of the puzzle!

<% for book in @books %>
<tr>
<td><%=h book.title %></td>
<td><%=h book.description %></td>
<td><%= link_to 'Show', book %></td>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Destroy', book, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
 
<% end %>

What we’re doing here, is looping through each item in the @books hash, and for each item we are printing out the book title, “book.title” and book description, “book.description”. The “h” you see preceeding the book title and description is the Rails “HTML escape”. If you type <%= book.title %> instead of <%=h book.title %>, the book title will still dispaly, BUT it is good habit to ALWAYS HTML escape database fields with data that may contain something harmful…read: malicious users entering something other than a book description.

Take a look at the code again, if I were to read it out loud to you it would read as follows:

“For each book in the @books variable, do the following: Print the title; description; and the show, edit, and destroy commands”. To expand on the whole “for” block, here’s another example…if you were feeling rather rebelious, you could do this too:

1
2
3
4
5
6
7
8
9
10
<% for item in @books %>
<tr>
<td><%=h item.title %></td>
<td><%=h item.description %></td>
<td><%= link_to 'Show', item %></td>
<td><%= link_to 'Edit', edit_book_path(item) %></td>
<td><%= link_to 'Destroy', item, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
 
<% end %>

Hopefully that makes some more sense :) As always, if this was this helpful, if you have questions, or if you just want to bullshit…leave me a comment…help me, help you! Feel like donating to my beer fund?

The next part of our journey through Rails covers RESTful design, an integral part of a properly designed and correctly executed Rails web application.

  • Jon Cummings
    This is really excellent. Why can't all those fancy books I bought explain the basics as clearly as you did here? Now I FINALLY understand what that params[:id] code I keep seeing all over the place is. Big thanks!
  • haha, if big fancy books explained everything...who'd keep buying books? :)
  • Dude... thank you. Your writing style made everything really easy to understand. This is by far one of the easiest to follow RoR tutorials I've seen.
  • ben
    great tuto, thanks for sharing.

    the "Select * From books" sql part is a bit confusing because the way you present it could lead to think that it's real code in the book.rb file, without a # for comment.
  • ryan
    hey, quick question about escaping html.

    Let's say you have @book.title, and the title is The Hitch-Hiker's Guide to the Galaxy.

    safe coding would be to do



    But then you would not have valid html because of the apostrophe.

    you could do markdown(@book.title), but would that work here?
  • RubyBeginner
    KEEP UP THE GOOD WORK!!!!

    This has been really useful. Thank you for your time.
  • Nick
    Hey, thanks for this tutorial. It took me a while to find a 2.x tutorial of this kind of Rails. Thanks again, and keep it up!
  • Tom
    Keep up these tutorials, they are helping me delve into RoR and I appreciate the help. I did notice a (nit-picky)error on your instructions above though. Towards the beginning of this section of your tutorials, you say to fire up 'app/config/routes.rb' which may be a little confusing because there is no config subdirectory under bookstore/app. Maybe it would be easier to understand if it said 'config/routes.rb' instead. Just a thought... Thanks again.
  • CarniBird
    Really cool!
    Each step, i'm looking forward to learn more!
    And even if I don't understand everything, i'm confident.
  • Jonathan
    I'll add in the database configuration, sometimes I forget all my local development stuff is root with no password :)
  • Dale
    Great job! I have been spending days piecing this all together and this made it all fit together.
  • MellifluidicPulse
    Awesome stuff (also with the first page)! I know I'm basically echoing those who've come before me, but it is great to have stuff out there for 2.0. If I may make a couple suggestions, you might want to remind people on the previous page that they need to actually create their database and set their password in the database.yml file (If they set one other than default). This had me tripped up for a bit. Again though - awesome job!
  • Jack
    This is a great little introduction. I was able to follow it without knowing anything about ruby or rails. C was the last language I programmed in years ago. I started to learn Javascript this past year but Ruby on Rails seems like the best choice if I'm only planning to learn one. Really, I just want to be a code cobbler, gluing together pieces to make little apps. The RoR community seems enthusiastic so I'm hoping to do as little coding as possible.

    I really appreciate that you took the time to create this tutorial.

    Thanks
  • Queue
    Just another schmo wanting to say thanks. Thanks!
  • Al
    Your effort is appreciated!
  • Al
    Thanks for your contribution here.
  • hayden
    thank you very much, i've looked at many many many tutorials for ruby, and your's had definately been the most kind to a beginner like myself. and the what i've learned thusfar has been invaluble to me. thank you once more, and i will continue your tutorial, and hope to see more amazing work.
    THANKS =D
  • Une
    Great! Many thanks! Can't wait for the next episode.
  • Third paragraph, there is a repeated word:

    "I will leave leave the application’s"

    Mind you, I only point this out because I find this to be one of the clearest-written, easy-to-understands Rails 2.x guides I've seen yet, and I want its popularity to grow ;)
  • Mike
    Great Tutorial. A small thing (perhaps) Books is not a Hash, it’s an Array. Edit the controller and add the line “puts @books.class” (without the quotes), then look at the server console!!!
    Likewise @books[0].class is an element of the Array, namely a Book.
  • Tim
    Keep up the fantastic work.. I'm eagerly waiting for the next part.

    Cheers,

    Tim
  • Jonathan
    The methods (edit_book_path, destroy using :method => :delete, and new_post_path) do not look standard because they use something called resources. This is a little tricky, and I will be devoting a post to it.

    Nik: I overlooked that, thanks.
    Mitch: Authentication, for what? User login?
  • A nice followup to the first part.

    It might be worth mentioning to the reader that he/she needs to delete public/index.html in order for the index route to work.
  • Brilliant! This tutorial got me going with RoR within a few minutes.

    You have a great talent for explaining things in an easy-to-follow manner.
  • Zain AlAbdin
    Loved your tutorials!
    thanks a lot
    more more more more more more please!
  • Qubess Ali
    Hi,

    Thank you so much for this gr8 Tutorial. I rate this 10 out of 10. I have found your tutorial very helpful.

    With Best Regards
    Qubess Ali
  • Mitch
    Hi, I've checke your site meeta.com, great job,
    but, how did you manage to implement authentication? Which option do you feel it's the best?

    Thx
  • Leave a Reply
    Why do the four commands not look standard? Show just has book, Edit has edit_book_path(book), Destroy uses :method => :delete, and New port uses new_post_path. Where do they go and how are they linked to everything else?
  • depace
    helpful... thanks
  • Marc
    Thank you for the post, enjoyed reading.

    Need some help understanding routes, I am using a webhost and created a subdomain which points to the app/view/"folder of application"
    Navigating to the subdomain, I get a listing of the files in there rather than index.html.erb being used, as index.html would. Also when I have Time.now add as a variable to see the current time on the index page it doesnt get processed. Hope this helps in understanding the problem I am having.

    Thanks in advance

    Marc
  • Barry
    Great timing with this one. Encore!
  • Oscar
    Superb!!!!! Big thanks to you as most of the tutorial on the internet isn't working anymore.
  • Howdy
    This was a very good walkthrough of a simple ruby application.
    There is so much "junk" out there that never gives the most important points.
  • e
    This is great, please do post some more, it's hard to find rails 2.0 tutorials out there!
  • KS
    Good stuff
blog comments powered by Disqus