{"id":598,"date":"2008-12-14T11:22:46","date_gmt":"2008-12-14T19:22:46","guid":{"rendered":"https:\/\/www.ultrasaurus.com\/sarahblog\/2008\/12\/getting-started-with-rails-2-day-2\/"},"modified":"2008-12-14T11:22:46","modified_gmt":"2008-12-14T19:22:46","slug":"getting-started-with-rails-2-day-2","status":"publish","type":"post","link":"https:\/\/www.ultrasaurus.com\/2008\/12\/getting-started-with-rails-2-day-2\/","title":{"rendered":"getting started with rails 2 – day 2"},"content":{"rendered":"
In Day 1<\/a> with Rails, we built a simple application that let us manage a list of categories using the powerful generate scaffold script. Today, we’ll look under the hood and learn about the code that was created for us, so that we can modify the application to do just what we want (or, rather, what the tutorial Four Days on Rails<\/a> tutorial did, as I continue to update that tutorial for Rails 2.)<\/p>\n You’ll get the most out of Day 2, if you already know Ruby or you first read chapters 0-3 of the humble little Ruby book<\/a> or some other introduction to the language. Or, of course, you can just wing it.<\/p>\n You also should know about the MVC (Model View Controller) design pattern. Although if you don’t know MVC already, you’ll get the hang of it soon, at least from the Rails perspective. The Getting Started Guide has a nice intro<\/a> to MVC and elaborates on how Rails applies this venerated pattern:<\/p>\n 2.1.1. Models<\/strong> A model represents the information (data) of the application and the rules to manipulate that data. In the case of Rails, models are primarily used for managing the rules of interaction with a corresponding database table. In most cases, one table in your database will correspond to one model in your application. The bulk of your application’s business logic will be concentrated in the models.<\/p>\n 2.1.2. Views<\/strong> Views represent the user interface of your application. In Rails, views are often HTML files with embedded Ruby code that performs tasks related solely to the presentation of the data. Views handle the job of providing data to the web browser or other tool that is used to make requests from your application.<\/p>\n 2.1.3. Controllers<\/strong> Controllers provide the “glue” between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.<\/p>\n<\/blockquote>\n In today’s tutorial, we will learn about:<\/p>\n h1 {font-size: 150%} <\/p>\n <\/p>\n h1 {font-size: 150%} <\/p>\n <\/a><\/p>\n The definition of the model is in one of the files generated by scaffold:<\/p>\n This is a Ruby file that declare a class named Category that inherits from class ActiveRecord::Base. To define a class in Ruby, you place the class keyword at the beginning of a line, followed by the class name and a < and the class it inherits from.<\/p>\n Active Record is one of the “gems” installed with Rails. Looking in the Ruby on Rails api documentation<\/a> for ActiveRecord::Base in the Classes section shows all of the methods, attributes, exceptions and other parts available in class ActiveRecord::Base.<\/p>\n An instance of the class Category could be called anything but the Rails convention is to use a variable named category or @category. The :: symbol is the Ruby scope operator. In ActiveRecord::Base it means that you are referring to the Base of ActiveRecord and not some other base. <\/p>\n note: I’ve adapted this nice explanation from Fairleads<\/a> along with language note from the humble little ruby book<\/a>)<\/p>\n We will customize our Model by adding code here.<\/p>\n <\/p>\n <\/a><\/p>\n Let’s say we want to make it so that each category appears once and only once in the category list. If you try that now, you’ll notice that the application allows a duplicate entry. Go ahead and delete the duplicate. We’ll make sure it isn’t allowed in the future, by adding validation to the model. <\/p>\n Rails gives you a lot of error handling for free (almost). To demonstrate this, add some validation rules to the empty category model: <\/p>\n <\/p>\n <\/strong><\/p>\n These entries will give automatic checking that: <\/p>\n Documentation: ActiveRecord::Validations::ClassMethods<\/a><\/p>\n To try this out, now try to insert a duplicate record. This time, Rails will you prevent you from creating a duplicate record (see below). The style is a bit in your face — it’s not the most subtle of user interfaces. However, what do you expect for free? <\/p>\n <\/p>\n <\/a><\/p>\n Let’s look at the code behind the controller. The controller is where the programming logic for the application Some methods of the controller along with the class definition produced by the scaffold script are listed below: <\/p>\n app\/controllers\/categories_controller.rb<\/strong><\/p>\n Note that just like the model, the controller is simply a ruby class file. Each method in the class defines an action. Actions, by default, render a template in the app\/views directory corresponding to the name of the controller and action after executing code in the action. For example, the index action in our Categories controller renders a list of categories; however, note that unlike index, the destroy action will not render a template. After performing its main purpose (calling destroy on the category object that the user selected), it initiates a redirect instead.<\/p>\n Note: there is quite a bit of Ruby magic going on in the generated controller file with exactly how the respond_to block works. Perhaps I’ll dive into that once I’ve got the basics down.<\/p>\n <\/p>\n <\/a><\/p>\n Views are where the user interface is defined. Rails can render the final HTML page presented to the user from three components: <\/p>\n If you look at the view files generated by the scaffold command, you will see that they are .erb files. I’m not sure what ‘erb’ stands for, but they have replaced .rhtml files<\/a> in previous versions of Rails. These files are a mix of html markup with snippets of ruby code in them. <\/p>\n <\/p>\n <\/a><\/p>\n Rails Naming conventions: if there is a template in appviewslayouts with the same name as the current controller then it will be automatically set as that controller’s layout unless explicitly told otherwise.<\/p>\n <\/code><\/p>\n This is mostly HTML, plus a few bits of Ruby code embedded within The Ruby bits in bold are translated into HTML during the Rails rendering process as follows:<\/p>\n <\/p>\n <\/a><\/p>\n Rails naming convention: templates are held in appviews’controller”action’.erb.html. The basic list action is perhaps easiest to understand. This is the default action for the controller and is therefore the ‘index’ template<\/p>\n \n<h1>Listing categories<\/h1><\/p>\n <table> <% for category in @categories<\/strong> %> <br \/><\/p>\n <%= link_to<\/strong> 'New category', new_category_path %>\n<\/p><\/blockquote>\n <\/code><\/p>\n Below are some notes about the more interesting bits of code highlighted (bold) above<\/p>\n The ‘new’ action template created by the scaffold script is given below:<\/p>\n \n<h1>New category<\/h1><\/p>\n <% form_for<\/strong>(@category) do<\/strong> |f| %> <p> <%= link_to 'Back', categories_path %>\n<\/p><\/blockquote>\n form_for<\/strong> is part of the Rails FormHelper class. The 'new' form is defined with the block of Ruby code between do and end. Form helpers are designed to make working with models much easier compared to using just standard HTML elements by providing a set of methods for creating forms based on your models. This helper generates the HTML for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form is submitted, the form inputs will be bundled into the params object and passed back to the controller. The form that is generated can be seen here: <\/p>\n \n<form action="\/categories" class="new_category" id="new_category" method="post"><div style="margin:0;padding:0"> <\/code><\/p>\n Documentation: ActionView::Helpers::FormHelper<\/p>\n <\/p>\n <\/a><\/p>\n The code generated by the Scaffold script is perfectly usable 'out of the box', and is robust once you have added enough validation into your data model. However, if that's all there was to developing Rails applications, then all the Rails apps would be boring. Now that we understand a little bit of what's going on, we can start to tailor the application to do exactly what we want.<\/p>\n <\/p>\n <\/a><\/p>\n In a list view, as we can see on the mail page, I would expect the records to be displayed in alphabetical order. This requires a minor change to \n# GET \/categories respond_to do |format| <\/code><\/p>\n Remember that Category is an ActiveRecord, so you can look up the find method on ActiveRecord::Base and see all the nifty options.<\/p>\n Documentation: ActiveRecord::Base.find<\/p>\n In this application, the show screen is unnecessary - all the fields fit comfortably on a single row on the screen. \n(excerpt) respond_to do |format| # PUT \/categories\/1 respond_to do |format| The flash message will be picked up and displayed on the next screen to be displayed - in this case, the list screen.<\/p>\n <\/p>\n <\/a><\/p>\n Below you can see that I've moved the <h1>...<\/h1> heading text out of the Template into the Layout so that we can keep the formatting consitent across pages. As each template will have a different heading, I need to set the value of the variable @heading in the Template. Rails is quite ok with this - Template variables are available to Layouts at rendering \n<head> <\/code><\/p>\n \n<% @heading = "Categories" %><\/strong> <td><%= link_to 'Edit', edit_category_path(category) %><\/td> <\/code><\/p>\n Since we removed the 'show' action above, I've also removed the link in the categories list view. Then I added created_at and updated_at, which if you recall from Day 1, were auto-generated for us as part of the scaffold.<\/p>\n Instead of the default date format (e.g. Sat Dec 13 23:26:19 UTC 2008), we're using a Ruby method strftime() to format the date and time fields (e.g. 11:26 PM 13-Dec-08)<\/p>\n Ruby Documentation: class Time<\/p>\n <\/p>\n <\/a><\/p>\n You may have noticed that edit and new are almost idential. The scaffold does not assume that will be your design, so it doesn't prematurely optimize; however, in this app the new and edit pages share almost all of the same elements. Rails can allow these templates to share code by using a \"partial\" template. Create a new file called _category (all partial templates begin with an '_' per Rails convention) and insert the shared code:<\/p>\n \n<% form_for(@category) do |f| %> <p> <%= link_to 'Back', categories_path %>\n<\/p><\/blockquote>\n <\/code><\/p>\n This makes the edit and new templates very short and sweet. Note the use of the variable button_name, which you can see in action below. <\/p>\n \n<% @heading = "Edit Category" %><\/strong><\/p>\n <%= render :partial => @category, \n<% @heading = "New Category" %><\/strong><\/p>\n <%= render :partial => @category, <\/code><\/p>\n Now we have a nicely modular webapp following the DRY principle without that unsightly repeated code. We also understand much of the code that defines our little app.<\/p>\n <\/p>\n <\/a><\/p>\n We learned about the following files that were created by the generate scaffold script:<\/p>\n We also learned how to create a partial template: _category.html.erb.<\/p>\n I learned more of the Ruby language and how to navigate the Ruby API docs, and I hope you did too. This tutorial got a bit long and required quite a bit of changes from the old one. Please comment if you find any errors and I'll fix up up for posterity.<\/p>\n Notably absent from this tutorial has been the famous unit testing that I hear all good Ruby developers swear by. Perhaps I'll need to go offscript and read up on that for Day 3.<\/p>\n Stay tuned.<\/p>\n","protected":false},"excerpt":{"rendered":" In Day 1 with Rails, we built a simple application that let us manage a list of categories using the powerful generate scaffold script. Today, we’ll look under the hood and learn about the code that was created for us, so that we can modify the application to do just what we want (or, rather,… Continue reading \n
\n
\n
\n
\n
\nh1,h2 {font-style: bold}
\nimg
\n{
\nborder:2px solid silver;
\nmargin:0px 0px 15px 20px;
\n}
\nblockquote, pre.code {
\nborder: solid 1px #aaa;
\npadding: 6px;
\nbackground-color: #eee;
\ncolor: inherit;
\noverflow:auto;
\nmargin: 10px 0px;
\n}<\/p>\n
\n
\nh1,h2 {font-style: bold}
\nimg
\n{
\nborder:2px solid silver;
\nmargin:0px 0px 15px 20px;
\n}<\/p>\nThe Model<\/h1>\n
app\/models\/category.rb<\/strong><\/pre>\n
\n
\nclass Category < ActiveRecord::Base\nend\n<\/pre>\n<\/blockquote>\n
Validation<\/h2>\n
app\/models\/category.rb<\/pre>\n
\n
\nclass Category 1..20\nvalidates_uniqueness_of :title, :message => \"already exists\"\nend\n<\/pre>\n<\/blockquote>\n
\n
<\/p>\n
The Controller<\/h1>\n
\nlies. It interacts with the user using views, and with the database through models. You should be able to read the
\ncontroller and see how the application hangs together. <\/p>\n\n
\nclass CategoriesController @categories }\n end\nend\n\n# GET \/categories\/1\n# GET \/categories\/1.xml\ndef show\n @category = Category.find(params[:id])\n\n respond_to do |format|\n format.html # show.html.erb\n format.xml { render :xml => @category }\n end\nend\n:\n:\n# DELETE \/categories\/1\n# DELETE \/categories\/1.xml\ndef destroy\n @category = Category.find(params[:id])\n @category.destroy\n\n respond_to do |format|\n format.html { redirect_to(categories_url) }\n format.xml { head :ok }\n end\nend\nend\n<\/pre>\n<\/blockquote>\n
Views<\/h1>\n
\n
\nbrowser.<\/li>\n\n
\n
\n
\n
Layout<\/h2>\n
\napps\/layouts\/categories.html.erb<\/strong><\/p>\n\n
\n<!DOCTYPE html PUBLIC "-\/\/W3C\/\/DTD XHTML 1.0 Transitional\/\/EN"\n"http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-transitional.dtd">
\n
\n<html xmlns="http:\/\/www.w3.org\/1999\/xhtml" xml:lang="en" lang="en">
\n<head>
\n <meta http-equiv="content-type" content="text\/html;charset=UTF-8" \/>
\n <title>Categories: <%= controller.action_name<\/strong> %><\/title>
\n <%= stylesheet_link_tag<\/strong> 'scaffold' %>
\n<\/head>
\n<body>
\n
\n<p style="color: green"><%= flash<\/strong>[:notice] %><\/p>
\n
\n<%= yield<\/strong> %>
\n
\n<\/body>
\n<\/html>
\n<\/pre>\n<\/blockquote>\n<\/code> tags. This layout will be called by
\nthe rendering process regardless of the action being run. It contains the standard HTML tags – the
\n<html><head>...<\/head><body>...<\/body><\/html><\/code> that will appear on every page.<\/p>\n
\n
Documentation: ActionController::Base<\/li>\n<link href="\/stylesheets\/scaffold.css" media="screen" rel="Stylesheet" type="text\/css" \/><\/code>
Documentation: ActionView::Helpers::AssetTagHelper<\/li>\n
Documentation: ActionController::Flash<\/li>\n
\nDocumentation: see Ruby language docs. <\/li>\n<\/ul>\nTemplates<\/h2>\n
\napp\/views\/categories\/index.erb.html<\/strong><\/p>\n
\n
<tr>
\n
<th>Title<\/th>
\n
<\/tr><\/p>\n
\n
<tr>
\n
<td><%=h category.title<\/strong> %><\/td>
\n
<td><%= link_to<\/strong> 'Show', category %><\/td>
\n
<td><%= link_to<\/strong> 'Edit', edit_category_path(category) %><\/td>
\n
<td><%= link_to<\/strong> 'Destroy', category, :confirm<\/strong> => 'Are you sure?', :method => :delete %><\/td>
\n
<\/tr>
\n
<% end<\/strong> %>
\n
<\/table><\/p>\n\n
<\/table><\/code> as a Category. To guard against this, it is good practice to ‘HTML escape’ any data which has been provided by users. This means that e.g.
<\/table><\/code> is rendered as <\/table> which is harmless. Rails makes this really simple – just add an ‘h’ as shown<\/li>\n
Documentation: ActionView::Helpers::UrlHelper<\/li>\n<\/ul>\napp\/views\/categories\/new.erb.html<\/strong><\/p>\n
\n
<%= f.error_messages<\/strong> %><\/p>\n
\n
<%= f.label<\/strong> :title %><br \/>
\n
<%= f.text_field<\/strong> :title %>
\n
<\/p>
\n
<p>
\n
<%= f.submit<\/strong> "Create" %>
\n
<\/p>
\n
<% end %><\/p>\n<\/p>\n
<\/p>\n
\n
<p>
\n
<label for="category_title">Title<\/label><br \/>
\n
<input id="category_title" name="category[title]" size="30" type="text" \/>
\n
<\/p>
\n
<p>
\n
<input id="category_submit" name="commit" type="submit" value="Create" \/>
\n
<\/p>
\n
<\/form>\n<\/p><\/blockquote>\nTailoring the Generated Scaffold Code<\/h1>\n
Modifying the Controller<\/h2>\n
\nthe controller:<\/p>\nappcontrollerscategories_controller.rb (excerpt)<\/strong><\/p>\n
\n
# GET \/categories.xml
\n
def index
\n
@categories = Category.find(:all, :order=>'title'<\/strong>)<\/p>\n
\n
format.html # index.html.erb
\n
format.xml { render :xml => @categories }
\n
end
\n
end\n<\/p><\/blockquote>\n
\nSo, def show can disappear, and let's go straight back to the list screen (categories index) after an 'Edit':<\/p>\n
\n(excerpt)<\/strong><\/p>\n
\n
# POST \/categories
\n
# POST \/categories.xml
\n
def create
\n
@category = Category.new(params[:category])<\/p>\n
\n
if @category.save
\n
flash[:notice] = 'Category was successfully created.'
\n
format.html { redirect_to :action => "index"<\/strong> }
\n
format.xml { render :xml => @category, :status => :created, :location => @category }
\n
else
\n
format.html { render :action => "new" }
\n
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
\n
end
\n
end
\n
end<\/p>\n
\n
# PUT \/categories\/1.xml
\n
def update
\n
@category = Category.find(params[:id])<\/p>\n
\n
if @category.update_attributes(params[:category])
\n
flash[:notice]<\/strong> = 'Category was successfully updated.'
\n
format.html { redirect_to :action => "index"<\/strong> }
\n
format.xml { head :ok }
\n
else
\n
format.html { render :action => "edit" }
\n
format.xml { render :xml => @category.errors, :status => :unprocessable_entity }
\n
end
\n
end
\n
end\n<\/p><\/blockquote>\nSharing Variables between the Template and Layout<\/h2>\n
\ntime.<\/p>\n
\nappviewslayoutscategories.html.erb (exceprt)<\/p>\n
\n
<meta http-equiv="content-type" content="text\/html;charset=UTF-8" \/>
\n
<title>Categories: <%= controller.action_name %><\/title>
\n
<%= stylesheet_link_tag 'scaffold' %>
\n
<\/head>
\n
<body>
\n
<h1><%=@heading %><\/h1><\/strong>
\n
...\n<\/p><\/blockquote>\n
\napp\/views\/categories\/index.html.erb (excerpt)<\/p>\n
\n
\n
<table>
\n
<tr>
\n
<th>Title<\/th>
\n
<\/tr>
\n
\n
<% for category in @categories %>
\n
<tr>
\n
<td><%=h category.title %><\/td>
\n
<td><%= category["created_at"].strftime("%I:%M %p %d-%b-%y") %><\/td>
\n
<td><%= category["updated_at"].strftime("%I:%M %p %d-%b-%y") %><\/td><\/strong><\/p>\n
\n
<td><%= link_to 'Destroy', category, :confirm => 'Are you sure?', :method => :delete %><\/td>
\n
<\/tr>
\n
<% end %>
\n
<\/table>\n<\/p><\/blockquote>\nPartial Templates<\/h2>\n
\napp\/views\/categories\/_category.html.erb<\/strong><\/p>\n
\n
<%= f.error_messages %><\/p>\n
\n
<%= f.label :title %><br \/>
\n
<%= f.text_field :title %>
\n
<\/p>
\n
<p>
\n
<%= f.submit button_name<\/strong> %>
\n
<\/p>
\n
<% end %><\/p>\n
\napp\/views\/categories\/edit.html.erb (that's the whole thing!)<\/strong><\/p>\n
\n
:locals => { :button_name => "Update"<\/strong>} %>\n<\/p><\/blockquote>\n<\/p>\n
\napp\/views\/categories\/new.html.erb<\/strong><\/p>\n
\n
:locals => { :button_name => "Create"<\/strong>} %>\n<\/p><\/blockquote>\nWhat did we learn?<\/h1>\n
\n
\n