Rails Introduction: Templates and Controllers

Lecture Notes for CS 142
Winter 2014
John Ousterhout

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 (Views)

  • 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 is expanded by executing code snippets, substituting the results into the document.
  • 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
  • Benefits of templates:
    • Easy to visualize HTML structure
    • Easy to see how dynamic data fits in
  • 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>
    
  • Query values available through params.
  • Rails automatically escapes anything substituted with <%= ... %>
  • Use html_safe to indicate that a value should not be escaped

Controllers

  • Most code should be in controller, not template; all template does is substitute values into HTML. Controller also interfaces with models, which manage application data.
  • Lifecycle of an HTTP request:
    • Routing (what method should handle an incoming request?):
      • Rails receives incoming request
      • Rails invokes a method in a Controller class, based on the URL:
        • Rails provides a general-purpose mechanism called routing for this.
        • Simple approach for this class: URL /rails_intro/hello is routed to method hello in class RailsIntroController. The controller should be defined in the file app/controllers/rails_intro_controller.rb
      • 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 show_primes
        # 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
          is_prime = 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
              is_prime = false
              break
            end
          end
          if is_prime 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:
    • app/controllers: Ruby files containing controller classes
    • app/views/controller: template files for the controller named controller
    • app/models: model definitions
    • app/assets/images/photo.jpg: image corresponding to
      <%= image_tag "photo.jpg" %>
      
    • app/assets/javascripts/my_code.js: Javascript file corresponding to
      <%= javascript_include_tag "my_code" %>
      
    • app/assets/stylesheets/my_styles.css: CSS file corresponding to
      <%= stylesheet_link_tag "my_styles" %>
      
    • db/migrate: database migration files

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/application.html.erb: default layout for all URLs in the application.
    • app/views/layouts/xyz.html.erb: default layout for all URLs in XyzController.

Partial-Page Templates

  • Encapsulate the rendering of common elements in a reusable way.
  • Similar structure to other templates.
  • 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.
    <%= render(:partial => "shared/foo",
        :locals => {:x => 24, :y =>36}) %>
    
    • Template is in app/views/shared/_foo.html.erb
    • The template will have local variables x and y.

Helper Functions

  • Rails provides a collection of "helper" functions that are often useful in templates:
  • link_to: convenient for generating links:
    <%= link_to(@name, {:action => :user_info, :id => @user_id}) %>
    
  • Including images, stylesheets, or Javascript (files must be in app/assets/images, app/assets/stylesheets, or app/assets/javascripts):
    <%= image_tag "icon.jpg" %>
    <%= stylesheet_link_tag "main" %>
    <%= javascript_include_tag "file1", "file2" %>