3.3. Creating HTML Dynamically with erb

Do you know PHP (without any additional framework)? Then the content of an erb file will seem very familiar to you. It is a mixture of HTML and Ruby code (erb stands for embedded Ruby). But we cannot simply put such an erb web page into the public directory, as pages stored in this directory are delivered 1:1 without first passing through an erb parser. This is the first time for us to get in touch with the MVC model.[19] We need a controller and a view. We can create it via the command rails generate controller. Let's have a look at the help:
$ rails generate controller
Usage:
  rails generate controller NAME [action action] [options]

Options:
      [--skip-namespace]        # Skip namespace (affects only isolated applications)
      [--old-style-hash]        # Force using old style hash (:foo => 'bar') on Ruby >= 1.9
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit
      [--helper]                # Indicates when to generate helper
                                # Default: true
      [--assets]                # Indicates when to generate assets
                                # Default: true

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -q, [--quiet]    # Suppress status output
  -s, [--skip]     # Skip files that already exist

Description:
    Stubs out a new controller and its views. Pass the controller name, either
    CamelCased or under_scored, and a list of views as arguments.

    To create a controller within a module, specify the controller name as a
    path like 'parent_module/controller_name'.

    This generates a controller class in app/controllers and invokes helper,
    template engine and test framework generators.

Example:
    `rails generate controller CreditCard open debit credit close`

    Credit card controller with URLs like /credit_card/debit.
        Controller:      app/controllers/credit_card_controller.rb
        Functional Test: test/functional/credit_card_controller_test.rb
        Views:           app/views/credit_card/debit.html.erb [...]
        Helper:          app/helpers/credit_card_helper.rb
$
Nice! We are kindly provided with an example further down:
rails generate controller CreditCard open debit credit close
Doesn't really fit the bill for our case but I am feeling brave and suggest that we simply try this:
$ rails generate controller Example test
      create  app/controllers/example_controller.rb
       route  get "example/test"
      invoke  erb
      create    app/views/example
      create    app/views/example/test.html.erb
      invoke  test_unit
      create    test/functional/example_controller_test.rb
      invoke  helper
      create    app/helpers/example_helper.rb
      invoke    test_unit
      create      test/unit/helpers/example_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/example.js.coffee
      invoke    scss
      create      app/assets/stylesheets/example.css.scss
$
Phew... that's a lot of stuff being created. Amongst others, the file app/views/example/test.html.erb. Let's have a closer look at it:
$ cat app/views/example/test.html.erb 
<h1>Example#test</h1>
<p>Find me in app/views/example/test.html.erb</p>
$
It's HTML, but for it to be a valid HTML page, something is missing at the top and bottom (the missing HTML "rest" will be explained in the section called “Layouts”). We launch the web server to test it
$ rails server
and have a look at the web page in the browser at the URL http://0.0.0.0:3000/example/test:
test.html.erb web page
In the log log/development.log we find the following lines:
Started GET "/example/test" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Connecting to database specified by database.yml
Processing by ExampleController#test as HTML
  Rendered example/test.html.erb within layouts/application (2.7ms)
Compiled example.css  (15ms)  (pid 73491)
Compiled application.css  (25ms)  (pid 73491)
Compiled jquery.js  (9ms)  (pid 73491)
Compiled jquery_ujs.js  (0ms)  (pid 73491)
Compiled example.js  (118ms)  (pid 73491)
Compiled application.js  (166ms)  (pid 73491)
Completed 200 OK in 321ms (Views: 320.4ms | ActiveRecord: 0.0ms)


Started GET "/assets/application.css?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /application.css - 200 OK (8ms)


Started GET "/assets/jquery.js?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /jquery.js - 200 OK (4ms)


Started GET "/assets/example.css?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /example.css - 200 OK (2ms)


Started GET "/assets/example.js?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /example.js - 200 OK (2ms)


Started GET "/assets/application.js?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /application.js - 200 OK (33ms)


Started GET "/assets/jquery_ujs.js?body=1" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Served asset /jquery_ujs.js - 200 OK (2ms)
That almost reads like normal English. Let us analyse the first part:
Started GET "/example/test" for 127.0.0.1 at 2012-11-13 19:06:55 +0100
Connecting to database specified by database.yml
Processing by ExampleController#test as HTML
  Rendered example/test.html.erb within layouts/application (2.7ms)
localhost (127.0.0.1) sent in an HTTP GET request for the URI /example/test. That was then apparently rendered as HTML by the controller ExampleController using the method test.
The other lines tell us that a bunch of CSS and JavaScript files are compiled and than delivered.
Now we just need to find the controller. But you are in luck... I know the answer. ;-) All controllers are in the directory app/controllers, and there you go, we indeed find the corresponding file app/controllers/example_controller.rb.
$ ls -l app/controllers/
total 16
-rw-r--r--  1 xyz  staff  80 Nov 13 18:46 application_controller.rb
-rw-r--r--  1 xyz  staff  69 Nov 13 19:05 example_controller.rb
$
Please open the file with your favorite editor:
class ExampleController < ApplicationController
  def test
  end
end
That is very clear. The controller ExampleController is a descendant of the ApplicationController and contains currently just one method with the name test. This method contains currently no program logic.
You will probably ask yourself how Rails knows that for the URL path /example/test it should process the controller ExampleController and the method test. This is not determined by some magical logic, but by a routing configuration. You can find this in the file config/routes.rb in the second line:
MacBook:testproject xyz$ cat config/routes.rb | grep example
  get "example/test"
MacBook:testproject xyz$ 
This line has been automatically inserted by the command rails generate controller. In the routing file, you can also carry out any mapping. But more on this later. Currently, our routes look very simple. With the command rake routes we can get them. The file contains quite a few examples. But we'll dive into that later (Chapter 6, Routes). For now we have a look at the first two lines of that file:
$ cat config/routes.rb | head -n 2
Testproject::Application.routes.draw do
  get "example/test"
$ 

Important

A static file in the directory public always has higher priority than a route in the config/routes.rb! So if we were to save a static file public/example/test that file will be delivered.

Programming in an erb File

Erb pages can contain Ruby code. You can use it to program and give these page dynamic content.
Let's start with something very simple: adding 1 and 1. First we try out the code in irb:
$ irb --simple-prompt
>> 1 + 1
=> 2
>> exit
$
That was easy. We fill the erb file app/views/example/test.html.erb as follows:
<h1>First experiment with erb</h1>
<p>Addition:
<%= 1 + 1 %>
</p>
Then use rails server to launch the web server.
$ rails server
Visit that page with the URL http://0.0.0.0:3000/example/test
Simple addition
If you want to output the result of Ruby code, enclose the code within a <%= ... %>.
You may ask yourself: how can the result of adding two Fixnums be displayed as text? Let's first look up in irb if it really is a Fixnum:
$ irb --simple-prompt
>> 1.class
=> Fixnum
>> (1 + 1).class
=> Fixnum
>> exit
$ 
Yes, both the number 1 and the result of 1 + 1 is a Fixnum. What happened? Rails is so intelligent that it automatically calls all objects in a view (that is the file test.html.erb) that are not already a string via the method .to_s, which always converts the content of the object to a string (the section called “Method to_s for Your Own Classes”). Once more, a brief trip to irb:
$ irb --simple-prompt
>> (1 + 1).to_s
=> "2"
>> (1 + 1).to_s.class
=> String
>> exit
$
We will now have a closer look at the Ruby code. In a .html.erb file, there are two kinds of Ruby code instructions in addition to the HTML elements:
  • <% %>
    Executes the Ruby code it contains, but does not output anything (unless you explicitly use something like print or puts).
  • <%= %>
    Executes the Ruby code it contains and outputs the result as text. Certain characters are automatically escaped. If you do not want to output escaped text in a specific case, you need to realise this with raw(string).
    So provided an object has a method .to_s or the object itself is already a string, you can output it as result in the view, encapsulated within <%= %>.
Let's use an example, to make sure it all makes sense. We edit the app/views/example/test.html.erb as follows:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>
In the browser, it now looks like this:
Simple addition
Let's now have a look at the HTML source code in the browser:
<!DOCTYPE html>
<html>
<head>
  <title>Testproject</title>
  <link href="/assets/application.css?body=1" media="all" rel="stylesheet" type="text/css" />
<link href="/assets/example.css?body=1" media="all" rel="stylesheet" type="text/css" />
  <script src="/assets/jquery.js?body=1" type="text/javascript"></script>
<script src="/assets/jquery_ujs.js?body=1" type="text/javascript"></script>
<script src="/assets/example.js?body=1" type="text/javascript"></script>
<script src="/assets/application.js?body=1" type="text/javascript"></script>
  <meta content="authenticity_token" name="csrf-param" />
<meta content="lLKYshSWHLMrlvQOqxF2i8fk4+2eSOPYpg6scBB0wJM=" name="csrf-token" />
</head>
<body>

<p>Loop from 0 to 5:
0, 
1, 
2, 
3, 
4, 
5, 
</p>


</body>
</html>
Does it all make sense to you? Potentially, there are still two open questions:

Q:

I don't understand anything. I can't cope with the Ruby code. Could you please explain that again?

A:

Is it possible that you have not completely worked your way through Chapter 2, Ruby Basics? Please do take your time with it and have another thorough look. Otherwise, the rest won't make any sense here.

Q:

I can understand the Ruby code and the HTML output. But I don't get why some HTML code was rendered around it if I didn't even write that HTML code. Where does it come from, and can I influence it?

A:

Excellent question! We will get to that next (see the section called “Layouts”).
You are now going to learn the finer points of erb step by step. Don't worry, it's neither magic nor rocket science.

Layouts

The erb file in the directory app/views/example/ only forms the core of the later HTML page. By default, an automatically generated app/views/layouts/application.html.erb is always rendered around it. Let's have a closer look at it:
<!DOCTYPE html>
<html>
<head>
  <title>Testproject</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>
The interesting bit is the line
<%= yield %>
With <%= yield %> the View file is included here. The three lines with the stylesheets, the JavaScript and the csrf_meta_tags can stay as they are for now. They integrate default CSS and JavaScript files. We'll have a look into that in Chapter 12, Asset Pipeline. No need to bother with that right now.
The file app/views/layouts/application.html.erb enables you to determine the basic layout for the entire Rails application. If you want to enter a <hr> for each page and above it a text, then you can do this between the <%= yield %> and the <body> tag:
<!DOCTYPE html>
<html>
<head>
  <title>Testproject</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<p>My Header</p>
<hr>

<%= yield %>

</body>
</html>
You can also create other layouts in the directory app/views/layouts/ and apply these layouts depending on the relevant situation. But let's leave it for now. The important thing is that you understand the basic concept.

Passing Instance Variables from a Controller to a View

One of the cardinal sins in the MVC model[20]is to put too much program logic into the view. That's more or less what used to be done frequently in PHP programming in the past. But one of the aims of MVC is that any HTML designer can create a view without having to worry about the programming. Yeah, yeah, … if only it was always that easy. But let's just play it through in our minds: if I have a value in the controller that I want to display in the view, then I need a mechanism for this. This is referred to as instance variable and always starts with a @. If you are not 100 % sure any more which variable has which scope, then please have another quick look at the section called “Scope of Variables”.
In the following example, we insert an instance variable for the current time in the controller and then insert it in the view. So we are taking programming intelligence from the view to the controller.
The controller file app/controllers/example_controller.rb looks like this:
class ExampleController < ApplicationController
  def test
    @current_time = Time.now
  end
end
In the view file app/views/example/test.html.erb we can then access this instance variable:
<p>
The current time is 
<%= @current_time %>
</p>
With the controller and the view, we now have a clear separation of programming logic and presentation logic. So now we can automatically adjust the time in the controller in accordance with the user's time zone, without the designer of the page having to worry about it. As always, the method to_s is automatically applied in the view.
I am well aware that no-one will now jump up from their chair and shout: Thank you for enlightening me! From now on, I will only program neatly in accordance with MVC. The above example is just the first small step in the right direction and shows how we can easily get values from the controller to the view with instance variables.

Partials

Even with small web projects, there are often elements that appear repeatedly, for example a footer on the page with contact info or a menu. Rails gives us the option of encapsulate this HTML code in form of partials and then integrating it within a view. A partial is also stored in the directory app/views/example/. But the file name must start with an underscore (_).

Note

The term partial indicates that these are parts of something, in other words, smaller chunks of a template, a partial template.
As an example, we now add a mini footer to our page in a separate partial. Copy the following content into the new file app/views/example/_footer.html.erb:
<hr>
<p>
Copyright 2009 - <%= Date.today.year %> the Easter Bunny.
</p>

Note

Yes, we should use an instance variable and put the programming logic into the controller. But to keep it simpler I do it the dirty way which is putting this little Date.today.year programming in the view.
We edit the file app/views/example/test.html.erb as follows and insert the partial via the commandrender:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>

<%= render "footer" %>
So now we have the following files in the directory app/views/example:
$ ls app/views/example/
_footer.html.erb
test.html.erb
$ 
The new web page now looks like this:
Page with footer as partial

Important

The name of a partial in the code is always specified without the preceding underscore (_) and without the file extension .erb and .html. But the actual file must have the underscore at the beginning of the file name and end with the file extension .erb and .html.
Partials can also be integrated from other areas of the subdirectory app/views. For example, you can create a directory app/views/shared for recurring and shared content and create a file _footer.html.erb in this directory. You would then integrate this file into the erb code via the line:
<%= render "shared/footer" %>

Note

In a real-world project, you would - depending on your programming preferences - not normally solve the footer problem by using a partial that is called locally everywhere, but rather take care of it centrally in the app/views/layouts/application.html.erb.

Passing Variables to a Partial

Partials are great in the sense of the DRY (Don't Repeat Yourself) concept. But what makes them really useful is the option of passing variables. Let's stick with the copyright example. If we want to pass the start year as value, we can integrate this by adding the following in the file app/views/example/_footer.html.erb:
<hr>
<p>
Copyright <%= start_year %> - <%= Date.today.year %> the Easter Bunny.
</p>
So let's change the file app/views/example/test.html.erb as follows:
<p>Loop from 0 to 5:
<% (0..5).each do |i| %>
<%= "#{i}, " %>
<% end %>
</p>

<%= render "footer", :start_year => '2000' %>
If we now go to the URL http://0.0.0.0:3000/example/test, we see the 2000:
Partial with local start_year
Sometimes you need a partial that partially uses a local variable and somewhere else you may need the same partial, but without the local variable. We can take care of this in the partial itself with an if statement:
<hr>
<p>
Copyright 
<%= "#{start_year} - " if defined? start_year %>
<%= Date.today.year %> 
the Easter Bunny.
</p>

Note

defined? can be used to check if an expression has been defined.
You can call this partial with <%= render 'footer', :start_year => '2000' %> and with <%= render 'footer' %>.

Alternative Notation

In the section called “Passing Variables to a Partial” we only use the short form for rendering partials. Often, you will also see this long version:
<%= render :partial => "footer", :locals => { :start_year => '2000' } %>

Further Documentation on Partials

We have really only barely scratched the surface here. Partials are very powerful tools. You can find the official Ruby on Rails documentation on partials at http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials.

Updates about this book will be published on my Twitter feed.