Software Development Magazine - Project Management, Programming, Software Testing |
Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban |
Click here to view the complete list of archived articles
This article was originally published in the Fall 2006 issue of Methods & Tools
An Introduction to Web Development Using the Ruby on Rails Framework - Part 3
Nico Mommaerts
The Controller
The classes that make up the Controller layer are bundled together in the ActionController module. The Controller layer in a Web application is like a policeman regulating traffic, but instead of regulating car traffic, it controls incoming and outgoing Web/database traffic. In Rails, you typically have a whole bunch of these policemen (or ActionController classes). Each one has its own area to control and it is entirely up to the developer to specify these areas. You could have one that is in charge of the contracts and another one that is in charge of your employees:
class ContractsController < ApplicationController
end
class EmployeesController < ApplicationController
end
As its name implies, a Controller class controls something, so it has to be able to 'do something' with the contracts or employees. The Controller classes in the previous example don't seem very capable of doing anything like storing a new contract, or firing an employee. They are powerless policemen. In Rails terms, they still need 'actions'. Actions define what a Controller is able to control, like a policeman can 'give a ticket', or 'stop a car'. Without actions there would not be much happening in your application. You specify actions for a Controller simply by defining methods in a Controller class:
lass EmployeesController < ApplicationController
def show
@employee = Employee.find(params[:id])
end
end
This is not much code, but a lot of stuff is done for you behind the scenes, a testimony of the ease with which you write Rails Web applications. Most other popular Web application frameworks would require a lot more code and configuration to achieve the same result. Let us lift the curtains and peek at what is happening in the EmployeeController. The play begins with a user clicking on a link 'http://www.myrailsapp.com/employees/show/1'.
Now what happens first is that Rails starts dissecting the url to see which action Controller needs to perform. We notice here another convention: the url needs to comply with a specific format. In this case the format is 'http://www.myrailsapp.com/:controller/:action/:id'. Rails will simply look at the strings separated by a forward slash and will confer meaning to them based upon their order. The first part is 'employees', which maps to the Controller name. The second part is 'show', which maps to the name of the action the Controller needs to execute. The third and final part is the id of a record in our database. This very simple url convention lets you write clean and logically constructed urls. As a developer it is really easy to figure out what is going to happen when looking at the url without having to trace through various mapping files as is the case in many other Web frameworks. Just by looking at the url Rails knows to go to the EmployeesController and calls the public method 'show', without writing any code yet! Once inside the method 'show' we see one line of code:
@employee = Employee.find(params[:id])
Employee is the class we saw in the previous chapter about the Model layer. It class method 'find' takes various parameters, in this case params[:id]. The parameter 'params' is a map that contains all incoming parameters, be it from an url, a GET or a POST. Rails collects these on each incoming request, another annoyance the developer doesn't have to worry about anymore! The right part of the assignment can now be explained as: find the employee whose id is in the value of params[:id], which is 1 as specified in the example link.
On the left part we have '@employee'. The '@' in front of the variable means that we want to make it a class variable. In Ruby we don't have to declare the variable first, it exists as soon as we put something in it. When you put something in a class variable, it means you want to make the variable available to the view layer. We want to show the data of an employee on a Web page (the view) so the result of the Employee.find method is put in the class variable @employee.
It looks like we're done now because there is no code left, but there is still one thing left to do: show to the user the information we loaded from the database. If we don't state explicitly in the action what has to happen after executing the action, Rails assumes, correctly in this case, that we want to load a new Web page in the application. Rails needs an html page (well it's a bit more as plain html but we'll see that in the next part) to render the Web page. This is already been taken care of by yet another naming convention. All Web pages reside in a directory that Rails knows about, just like the directory that contains the models, the configuration, etc. In this directory, Rails looks for a subdirectory with the same name as the controller, 'employees' in this case, and then looks for a file with the same name as the action, 'show'. This is an overview of the file layout (I omitted the irrelevant folders):
/app
/controllers
employees_controller.rb-> the controller + .rb extension (Ruby source)
/models
employee.rb -> the model + .rb extension (Ruby source)
/views
/employees -> the name of the controller
show.rhtml -> the name of the action + .rhtml extension (Rails html file)
These directories are generated by Rails at the time you setup the project.
Another example to illustrate these principles:
class ContractsController < ApplicationController
def destroy
contract = Contract.find_by_number(params[:number]
contract.destroy()
# removes the row that contains the contract
redirect_to 'show'
end
show
@contracts = Contract.find(:all)
end
end
We have a ContractsController with an action 'destroy'. Obviously this action deletes a certain contract. Lets presume there is a form on a Web page that lets the user delete a contract by pressing a DELETE button. The action 'destroy' starts with looking up the contract to delete using the Contract model: Contract.find_by_number(params[:number]).
The Contract model seems to have a method with the name of the column of interest, 'number' in it. How is it possible? Well, just like Rails adds attributes to the model class based upon the names of the columns of the table it belongs to, it also adds 'find_by_<column_name>' methods for each column name. As with the automatically added attributes, it doesn't matter if you decide to rename a database column later on, since all this code is added by Rails at runtime. You never have to maintain this generated code! See how Rails gives you a powerful framework to work and yet doesn't require you to write much code?
Back to our example, after the contract is retrieved, it is removed from the database by calling the method 'destroy', nothing earth-shocking there. The next line is something we have not seen before: redirect_to 'show'. In the previous example, we assumed we wanted to render the loaded information, but in this case we didn't load any information, quite the opposite even! We want to show the user an overview of all remaining contracts after the user deletes one. We will add a separate action 'show' for that. This method can be used by visiting the url 'http://www.myrailsapp.com/contracts/show'. The 'redirect_to' statement lets Rails execute the 'show' action immediately after executing the 'destroy' action. The 'show' action doesn't state anything explicitly after loading all the contracts so it assumes it needs to render a Web page (located in /views/contracts/show just like in the employee example). We could explicitly say to render a Web page if we wanted to by saying: render 'show'.
Methods & Tools Testmatick.com Software Testing Magazine The Scrum Expert |