Rails Introduction: Templates and Controllers

Lecture Notes for CS 142
Fall 2010
John Ousterhout

  • Readings for this topic:
    • Chapters 1-9 of Agile Web Development with Rails provide an overview of all of the Rails facilities.
    • Chapters 21-22 and Sections 23.1-23.3 and 23.9 of Agile Web Development with Rails cover the material of this topic in detail.

History of Web Frameworks

  • Initially: static HTML files only.
  • CGI:
    • Certain URLs map to executable programs
    • When URL is referenced, program runs
    • Program's output is returned as the Web page
    • Program exits after Web page complete
    • Introduced the notion of stateless servers: each request independent, no state carried over from previous requests.
    • Perl typically used for writing CGI programs
  • First-generation frameworks (PHP, ASP.net, Java servlets):
    • Incorporate language runtime system directly into Web server
    • Templates: mix code and HTML
    • Web-specific library packages:
      • URL handling
      • HTML generation
      • Sessions
      • Interfacing to databases
  • Second-generation frameworks (Ruby on Rails, Django):
    • Object-relational mapping: simplify the use of databases (make database tables and rows appear as classes and objects)
    • Model-view-controller: stylized decomposition of applications.

Overview

  • Rails: a Web application framework based on the Ruby language. Often referred to as "Ruby on Rails".
  • Rails applications have an MVC (model-view-controller) structure:
    • Model: manages the application data
      • Each class encapsulates the data in a particular database table.
    • View: generates Web page.
    • Controller: glue between the model and view:
      • Responds to incoming HTTP requests
      • Fetches/modifies data in the models
      • Invokes view(s) to render results
    • The MVC pattern has been around since the late 1970's; originally conceived in the Smalltalk project at Xerox PARC.

Templates

  • Templates:
    • The most common way to generate dynamic HTML; available in virtually all Web development frameworks.
    • Simple Rails example:
      <?xml version="1.0" encoding="utf-8"?>
      <!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" xml:lang="en" lang="en">
        <head>
          <title>Hello, User</title>
        </head>
        <body>
          <p>
          This page was fetched at <%= Time.now() %>
          </p>
        </body>
      </html>
      
    • Basic ideas:
      • Write HTML document containing parts of the page that are always the same.
      • Add bits of code that generate the parts that are computed for each page.
      • The template gets expanded by executing code snippets, substituting the results into the document.
    • Benefits of templates:
      • Easy to visualize HTML structure
      • Easy to see how dynamic data fits in
    • Two kinds of annotation in Rails templates:
      • <%= ... %>: substitute value of expression
      • <% ... %>: execute Ruby code, but no substitution of result: use concat in code to generate HTML
    • Can interleave Ruby control structures with HTML:
      <?xml version="1.0" encoding="utf-8"?>
      <!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" xml:lang="en" lang="en">
        <head>
          <title>Rails Parameters</title>
        </head>
        <body>
          <p>
          The <code>params</code> hash contains the following values:
          </p>
          <% params.each do |key, value| %>
            <p><%= key %>: <%= value %></p>
          <% end %>
        </body>
      </html>
      
  • Useful "helper" functions for Rails templates:
    • h: method to escape special HTML characters.
    • link_to: convenient for generating links; see pages 514-518 in the Rails book. Example:
      <%= link_to(@name, {:action => "userInfo", :id => @user_id}) %>
      
    • Including stylesheets or Javascript:
      <%= stylesheet_link_tag "styles" %>
      <%= javascript_include_tag "file1", "file2" %>
      

Controllers

  • Most interesting Web pages require non-trivial amounts of code:
    • Selecting/organizing data
    • Business logic
  • Putting all this in the template would make it illegible.
  • Instead, put most code in controller, all template does is substitute values into HTML.
  • Lifecycle of an HTTP request:
    • Dispatching:
      • Rails receives incoming request
      • Rails invokes a method in a Controller class, based on the URL:
        • Default: URL /rails_intro/hello will be dispatched to method hello in class RailsIntroController. The controller should be defined in the file app/controllers/rails_intro_controller.rb
        • You can define other mappings.
      • The controller method corresponding to a URL is called an action method.
      • Controller can have several action methods; typically a controller manages a related group of URLs.
      • Rails makes URL information available to action methods:
        • params holds query values
        • If the URL had the form /class/method/id, where id is an integer, then that value is available through params[:id]. Commonly used to hold the primary key for a database record to display the page.
    • Controller action method:
      • Collects data needed to generate the page, saves in instance variables of the controller object.
      • Typically does this by calling Model classes.
    • By default, Rails automatically invokes the view once the controller action method returns.
      • For URL /rails_intro/hello, view file is app/views/rails_intro/hello.html.erb
      • Controller can override this by invoking render explicitly.
  • Example: controller to compute primes:
    class RailsIntroController < ApplicationController  
      def showPrimes
        # Query value "count" indicates how many primes to
        # compute (default: 10)
        if (params[:count] != nil) then
            count = params[:count].to_i()
        else
            count = 10
        end
        
        # Fill in @primes array with prime numbers
        @primes = []
        candidate = 2
        while (@primes.length < count)
          isPrime = true
          
          # If any of the known primes divides into the candidate
          # evenly, then the candidate isn't prime.
          @primes.each do |prime|
            if ((candidate % prime) == 0) then
              isPrime = false
              break
            end
          end
          if (isPrime) then
              @primes << candidate
          end
          candidate += 1
        end
      end
    end
    
  • Corresponding view:
    <?xml version="1.0" encoding="utf-8"?>
    <!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" xml:lang="en" lang="en">
      <head>
        <title><%= @primes.length %> Prime Numbers</title>
        <%= stylesheet_link_tag 'main' %>
      </head>
      <body>
        <p>
        The first <%= @primes.length %> prime numbers are:
        </p>
        <table class="oddEven" cellspacing="0">
          <tr class="header"><td>Prime Numbers</td></tr>
          <% @primes.each do |prime| %>
            <tr class="<%= cycle('odd', 'even') %>">
              <td><%= prime %></td>
            </tr>
          <% end %>
        </table>
      </body>
    </html>
    

Directory Structure

  • Rails assumes a particular file structure for applications. Some example directories (see book for more information):
    • app/controllers: Ruby files containing controller classes
    • app/models: model definitions
    • app/views/controller: template files for the controller named controller
    • db/migrate: database migration files
    • public: static files accessible via URLs
    • public/images/iii: image file accessible at the URL /images/iii
    • public/javascripts/jjj: Javascript file accessible at the URL /javascripts/jjj
    • public/stylesheets/sss: CSS file accessible at the URL /stylesheets/sss

Layouts

  • Layout: standard frame used for one or more pages
    • DOCTYPE and other boilerplate
    • Header, footer, sidebar, etc.
  • yield marks where the main template should be inserted.
  • The regular template is expanded before the layout is invoked so it can set variables that personalize the layout for individual pages (e.g., title)
  • Files for layouts:
    • app/views/layouts/xyz.html.erb: default layout for all URLs in XyzController.
    • app/views/layouts/application.html.erb: default layout for all URLs in the application.

Partial-Page Templates

  • Basic idea: encapsulate the rendering of common elements in a reasonable way.
  • Looks like any other template.
  • Invoked with the render method:
    <%= render(:partial => "foo") %>
    
    Will render the template _foo.html.erb in the same directory as the calling template.
  • Can pass arguments to the rendered template (see book for details).