Chapter 3: Controllers and Views
This week we will use the Ruby on Rails package to start our FakeBook application. We will create the application and learn how to use Model - View - Controller architecture that is essential for all Rails applications. A simple Controller class will be built along with its View. We will explore how to use layout to change the look and feel of the website. We will create the Model next week.
MVC is an architectural pattern widely used in software development which isolates business logic from the user interface. This makes the applications easier to develop and maintain. Its three fundamental components are as follows:
- Model
- is typically written entirely in ruby and is responsible for communicating with the back-end database.
- View
- defines what the user sees. It uses a mix of HTML and ruby code.
- Controller
- deals with the user input, for example chooses which page to open depending on what the user did.
Prerequisites
You will need Ruby on Rails to be installed on your computer in order to follow this Tutorial.
If you are working in the lab, on one of the Kingston University computers, a Ruby on Rails installation called BitNami RubyStack is already preinstalled. Simply, use the Start menu, choose Web Development and then Open Ruby Console Window.
If you use a computer at home, or you brang your own laptop, it is possible to install your own copy of Ruby on Rails. Check our Frequently Asked Questions to find out how.
Create a Rails Application
First, open Ruby Console Window (check the previous section to see how to do it). This is a typical console window, but all the Ruby and Rails development tools are prepared for you to use.
To create an empty Rails application, type and run the following command:
rails fake_bookNote that:
- We have just followed one of the naming conventions in rails: application names are never written with uppercase and words are separated with underscore character (_).
- When you used the rails program it has created the entire directory structure for the application. Browse now through this structure.
- Most of our development work will be creating and editing files in the rails_apps/fake_book/app directory. Notice that three of its four subirectories reflect the names of components in the Model - View - Controller pattern.
From now on, all the file paths given in this Tutorial will be relative to rails_apps/fake_book.
Testing the Empty Web Application
Change to the fake_book directory:
cd fake_bookNow start the webserver with the server Ruby script (all useful scripts are located in the script directory):
start ruby script/serverNote the word 'start' - it makes the script to be executed in a new window. You could omit this word and type something like this instead:
ruby script/serverThe problem is that this will occupy your console window, and you would have to open another one in order to continue with your programming.
Now open your browser and go to http://localhost:3000. You should see the Welcome page.
your application directoryYour application is created in a separate file directory, named exactly the same as your application. Each time you open a new Ruby Console Window, you will need to change your directory to your application directory, like the following:
cd fake_bookMost of the commands you would like to run in Ruby Console won't work unless you change directory.
ruby scriptsYour rails application comes complete with a number of ready-to-use tools that are scripts written in ruby. You can use them from the command line as follows:
- ruby script/server
- runs Mongrel, a small but quite effective web server for your application. Notice that even though RubyStack installed Apache server, your application will not normally use it: Mongrel is independent and runs on its own.
- ruby script/generate
- is a kind of quite mighty code wizard, which can write very useful pieces of code for you. We will use this script very thoroughly
- ruby script/destroy
- reverses the previous one: deletes the source files generated with script/generate.
- ruby script/console
- opens a console where you can test your ruby objects directly in ruby.
Simple Controller Class
To create a new Controller class, go to your Ruby Console window and run the following command:
ruby script/generate controller SiteThis is an example of using a very powerful code creating tool called generator. It created a number of files. We will now open one of them named site_controller.rb, which is located in app/controllers directory. This is the source code in Ruby that defines a newly created class SiteController. We will make changes to it soon.
What happens if you browse to something that you know does not exist? Try http://localhost:3000/garbage and this should show error message Routing Error.
Now try http://localhost:3000/site You should see a different message: Unknown action - No action responded to index. This tells that the SiteController object has been found but it does not know how to do the action index. Notice that index is the default action looked for if no other action is specified. Now we will add the index method to our SiteController class to ensure it is able to respond to the action:
Can't see the proper error messages?Mongrel Web Server that we are using with FakeBook and other Rails applications in most cases is very fast to reflect any changes in your code. Most cases does not mean always. Sometimes you'll have to kick it forward by pressing F5, wait a couple of seconds or even - fortunately in rather rare cases - to restart your server.
app/controllers/site_controller.rb
class SiteController < ApplicationController def index render :text => "Hello World" end endRefresh your browser, you should now see "Hello World". You will have the same results with http://localhost:3000/site/index.
The word render is a name (precisely: an identifier) of a function which Ruby will be happy to perform for us. This function is a part of Rails framework. To succeed, it requires a param. The param name is :text, and its value is "Hello World". Anything preceded with a colon like :text is called in Ruby a symbol and is commonly used to name things. Anything in quotation marks like "Hello World" is called in Ruby a string and is used to pass text.
Now it's your turn!
EXERCISE 3.1FakeBook, the application we develop in this tutorial, will be a social network site. Exercises like this one propose you to create your own web application, and to develop it in parallel with Fake Book.
To make your task more interesting, and also a bit more challenging, make your application distinct. Don't go too far: let it also be a social network site, but think about making it a bit more specialised. For example, it could be a site for amateur photographers, or for lovers of books or arts, or for car owners... There could be plenty of possible ideas.
Now, please start your own application. But:
- Don't call it fake_book. Find out another name that would be relevant for your application.
- Don't use site as the name for your controller. There is plenty of other good, relevant names, as page, app, main etc.
EXERCISE 3.2Add another method called about to print "Wow, that was easy!" on the browser.
Browse to your new action (webpage) to see if it works.
Rails URL'sYou can by now notice that the URL (address of your web page) used by Rails is made of the name of the controller and the name of its action:
http://localhost:3000/controller/action
If you did the exercise, than your name of the controller is site and your name of the action is about.
Working with the Controller Class
The SiteController class made in the previous section generated two actions (index and about) but their output was not very impressive. In this section we will use some mix of HTML and Ruby to specify the View in detail. The View is the second, after the Controller, component of the MVC architecture.
But first, we have to get rid of the simple rendering methods we've used in the previous section. Edit the app/controllers/site_controller.rb file again. We will keep the two actions we already have, but delete its render function calls. We will also add something new:
app/controllers/site_controller.rb
class SiteController < ApplicationController def index @title = "Welcome to FakeBook" end def about @title = "About Us..." end endThe @title is an identifier of a variable. If the identifier is preceded with '@' this means that we deal with an instance variables. They will be available anywhere within the class, and also in the View definition, which we will use soon. In opposition to this, local variables (without the preceding '@') are only available from within the action where they were defined.
VariablesTo store any value in the computer's memory for later use you just need to give the value a name. In programming, this process is called assignment, the name is called identifier and the whole thing (the value and its name) - a variable.
Identifiers of variables are built of combinations of letters, digits and underscore character _, but cannot start with a digit. Therefore a, my_var and var_1 are legal names but 5 and 23dc are not. Additionally, as explained above, identifiers of instance variables are preceded with @.
Creating a View Template
Let's have a try: browse to http://localhost:3000/site (you may have to modify this address to reflect your own controller name, if different than site) and what you get is the following error message: Template is missing - Missing template site/index.html.erb in view path C:/Rails/rails_apps/fake_book/app/views.
This time both the controller and the action are in place, but what is missing is a special View template file, with a special extension html.erb. They are written in HTML, with something added. Notice that the file below does not exist yet, you will have to create it at app/views/site.
app/views/site/index.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><%= @title %></title> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> </head> <body> <div id="page"> <div id="header">FakeBook</div> <div id="content"> <h1>Index</h1> <p>This is my first site in Ruby on Rails!</p> </div> </div> </body> </html>Try and navigate to your application again. Not much better, but at least now we have all the power of HTML within our grasp!
HTML? Let's have a look at the code above. It looks like a normal strict HTML, prepared for use with CSS. But if you look at the <title> tag you will see the following:
<title><%= @title %></title>
Everything that goes into between <%= and %> is the Ruby code. In this case this is the value of a variable that we have created in the index action in SiteController class. Check if it works properly!
DRY Your Code - Use Layouts!
Keep Your Code DRY!Ruby on Rails developers are particularly pride of not repeating their code. The DRY acronym stands for Don't Repeat Yourself. Somewhat abusing English language, developers often use phrases like "Rails is a DRY framework" or "Now let's DRY our code".
It is time now to provide a template for the about action. Actually, you could simply re-use the template we've created for the index, but this wouldn't keep our code DRY!
A layout is a special piece of html.erb code that incorporates those elements that are common for entire controller or even a whole application. This is typically the beginning and ending portions of the HTML, including any standard titles, headers, menus, footers and other stuff that would normally be rewritten in the same form in every template file.
To begin with, simply copy the existing template file to create a layout. Type in your Ruby Console Window:
copy app\views\site\index.html.erb app\views\layouts\application.html.erbThe created file is the global layout for entire application:
app\views\layouts\application.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><%= @title %></title> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> </head> <body> <div id="page"> <div id="header">FakeBook</div> <div id="content"> <%= yield %> </div> </div> </body> </html>This code will be reused in every page. The yield is a special word reserved in Ruby as a placeholder - for the code specific for a given page, in this case.
Now we can change the template for index cutting out all stuff that went to the application layout. What is left is just like this:
app/views/site/index.html.erb
<h1>Index</h1> <p>This is my first site in Ruby on Rails!</p>Navigate to your application again. Nothing should change in the look of the page. However, now preparing a template for about action should not be difficult. We will leave this for you to do!
EXERCISE 3.3Create a view template file for the about action. Put some contents there that you think is relevant for an About Us page.
Hint: create file app/views/site/about.html.erb, where site should be replaced by the name of your own controller.
Add Some Styling
To add a cascade style sheet (CSS) we need to slightly modify the application layout. Rails provides a special helper function named stylesheet_link_tag that facilitates linking to a stylesheet:
app\views\layouts\application.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><%= @title %></title> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <%= stylesheet_link_tag 'default' %> </head> <body> <div id="page"> <div id="header">FakeBook</div> <div id="content"> <%= yield %> </div> </div> </body> </html>The stylesheet lives under public/stylesheets (attention! that's outside the usual app directory!). Our rather minimalistic stylesheet goes like this:
public/stylesheets/default.css
body { font: normal small Arial, Verdana, Helvetica, sans-serif; font-size: 12pt; background: #fffdda;; margin: 0; padding: 0; text-align: center; } #page { width: 800px; text-align: left; border: solid maroon 1px; margin-left:auto; margin-right:auto; } #header { height: 110px; background: url("http://rubyonrails.org/images/rails.png") maroon no-repeat 16px 16px; color: White; font-size: 64pt; font-weight: 900; padding: 16px 16px 16px 120px; } #content { background: white; padding: 1em; }How is it looking now? Check your application now!
EXERCISE 3.4Apply your favourite stylesheet to the application. Make it beautiful!
Finishing Touches
There is one more important thing missing: visiting any of our pages the user has no links to the other ones.
Using Rails helper functions, it's very easy to provide a navigation bar to our application. A link_to method creates a link without all that messy <a href> stuff:
app\views\layouts\application.html.erb
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><%= @title %></title> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <%= stylesheet_link_tag 'default' %> </head> <body> <div id="page"> <div id="header">FakeBook</div> <div id="nav"> <%= link_to_unless_current "Home", :controller => "site", :action => "index" %> | <%= link_to_unless_current "About", :controller => "site", :action => "about" %> | <%= link_to_unless_current "Help", :controller => "site", :action => "help" %> </div> <div id="content"> <%= yield %> </div> </div> </body> </html>Well, actually we've used a slightly different function link_to_unless_current. Try out both and spot the difference!
Now, add this to the stylesheet:
public/stylesheets/default.css - adding more styles
#nav { background: #ececec; border-bottom: solid maroon 1px; padding: 0.5em; font-weight: bold; } #nav a { color: Maroon; text-decoration:none; }EXERCISE 3.5Create a Help action and page.
Hint: you need to add the action to app/controllers/site_controller.rb and create app/views/site/help.html.rb file for page template. Again, the names mentioned here will be a bit different if your controller holds any other name but site.
All the power behind the controller generatorWhen we generated our SiteController we used a rather minimalistic form of script/generate. Try something like this:
ruby script/generate controller Test index page1 page2This has created a new Controller class named TestController. What's really interesting, three actions (named index, page1 and page2) were also automaticaly generated! This includes action methods within the TestController class and also view templates. Test them: http://localhost:3000/test, http://localhost:3000/test/page1 and http://localhost:3000/test/page2. If you see nothing but routing errors, just restart your Mongrel Web Server.
To revert to the previous state you can use:
ruby script/destroy controller Testhowever you'll still have to remove app/views/test directory manually.
Changing the Default Route
The final refinement proposed in this chapter will change the behaviour of our application when the most basic URL is used. So far, when you tried http://localhost:3000 the only thing you've got was the default Rails "Welcome Aboard" page which is not exactly what you could expect in a real application. To change this default behaviour, just locate a configuration file that sets up the URL routes and add a single line. This file is located in config directory in the application root:
config/routes.rb
map.root :controller => "site"Now you only have to delete the file: public/index.html and - that's all! Test the link http://localhost:3000 again (you may need to refresh the page to see the difference).
Copyright (C) 2009-2011 by Jaroslaw Francik

