Embedded Ruby Explained — Sinatra Battleships Project Part 2

In the first part of this series on my first Sinatra web project, I explained on a general level the different tools we would be using to create a web application using Ruby. In this post, we’ll take an in-depth look at how embedded Ruby enables you to integrate Ruby code into an HTML template to produce webpage responses for browsers.

It might be helpful to learn that ERB is a class within Standard Ruby. eRuby, however, is a slightly different piece of software — it works the same as ERB but is written in C to be faster, and is called ERB’s ‘big brother’ by Ruby documentation.

So, ERB is a Ruby class, but it is also known as a templating system (for reasons explained below). It also comes in different guises — such as eRuby. When referring specifically to the ERB class and objects of this class, I will call it ‘ERB’, but when just generally referring to embedded Ruby, I will call it ‘erb’.

Sinatra — First Get Route

You can use the ERB Ruby language feature without Sinatra, but seeing as I’ll be making the application using Sinatra, it’s easier to explain ERB in that context. So we’ll first delve just a little into some Sinatra basics.

Sinatra provides you with methods which enable you to deal with HTTP requests and send back responses. Let’s create a Ruby file called ‘app.rb’ and save the following to it:

require 'sinatra'get "/" do
"Hello, world!"
end

As explained in my previous post, Sinatra is a Ruby gem that must be installed and required in files to be used.

The substantive code is basically saying — on receipt of an HTTP GET request from a browser, send back the response “Hello, world!”. The ‘/’ following get signifies that the path of this resource is the root, i.e. the default of the website.

You can run your file by typing ruby app.rb in terminal. Now in your browser, go to the URL: ‘http://localhost:4567’. The text ‘Hello, world!’ should now be displayed. For now, it suffices simply to get this first route up and running using Sinatra so we have a context for . I’ll explain what is going on with this route in my next post.

Generating Webpage Templates

So the get method above tells the server what to do when it receives a request from the browser — i.e. to send back the response ‘Hello, world!’. This is pretty boring… we want to use HTML to create structured, exciting content. HTML is limited, however — it cannot resolve or calculate code, it is simply a semantic mark-up of content.

This is where erb steps in. It is able to take an HTML template, with Ruby code embedded within it, and resolve the code to generate a final plain-text (HTML) document, to send to the browser as the response. This means you can pass in variables and utilise conditional logic (if, else, and so on).

Some webpages are still static — i.e. they are written completely in HTML and therefore never change. Most webpages these days, however, are dynamically-generated on the basis of templates using a system like erb, for example.

ERB

Ruby has a special class ERB, described in Ruby documentation as a ‘templating system’. In basic terms, it is a Ruby class like any other — but it is used to generate as its return value a plain text document, on the basis of an HTML template with Ruby code. Most commonly it is used to generate webpages, but its properties mean it can be used to produce XML documents, RSS feeds and other forms of structured text files.

Let’s amend our code script above to:

get "/" do
ERB.new(template).result
end

As we know from before, the get verb receives a GET request from a browser and sends back the response from the do block as a plain text document. The receiving browser can then render this text (browsers understand HTML).

If we create a new ERB object by ERB.new, we are able to pass it a string. This string will be the basis of the final text document returned, as indicated by my label of ‘template’. This return text is produced by the call to result.

We could define template as a static HTML string:

template = "<html><h1>Hello, world!</h1></html>"

(Note that you should place this text somewhere above our get route in the file so that the template local variable is recognised.)

Executing this file and returning to the ‘localhost:4567’ URL should now display ‘Hello, world!’ in bigger font (as it is an ‘h1’ HTML element now).

Using Variables in ERB

So far, we can pass the ERB object a string, call result on it, and return a string back (to be sent as the response to the browser).

We can pass local variables around within our routes as usual:

get "/" do
word = "hello"
ERB.new("<h1>#{word}</h1>").result
end

Here, the local variable word is interpolated (in the string fed to the ERB object) within the same scope in which it is defined, i.e. all within the scope of the get method. word will therefore be recognised and replaced with its value ‘“hello”’ when the script is executed.

In the following example, word and template also share the same execution context so this can be correctly resolved.

word = "hello"
template = "<h1>#{word}</h1>"
get "/" do
ERB.new(template).result
end

In contrast, an error will be returned by the following code:

template = "<h1>#{word}</h1>"get "/" do
word = "hello"
ERB.new(template).result
end

Why is this? On execution, the call to result on the ERB object will try to resolve the template local variable. There is another local variable called word within that. The template variable does not have access to the higher-level word variable within the get route, so the variable word does not actually have a meaning within template itself and this cannot be resolved.

Binding

Fortunately, there is a way ‘around’ this.

As per the documentation, “objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use.” In other words, the Binding class captures the current scope — the state of the variables, methods and self are all retained.

So, if we add (binding) as a parameter to the call to result in the code snippet above, this essentially captures whatever is within the current scope (i.e. the scope of the get method), which the ERB object can access — and the word variable can access the value “hello” and the code is resolved!

It is rare to see result without the binding argument as it is usually necessary to pass the execution context to the ERB object.

Embedding Ruby Code

Now we know how to call result on an ERB object, passing it a template and a binding, in order to return a plain text string based on the template.

The ‘magic’ that the ERB object can perform, however, is to resolve embedded Ruby code before returning the plain text string.

There are two fundamental erb tags that you need to know.

The first set includes an equals sign and can be called an expression. This code will be resolved and the result returned as a string when the template is created. This would be used for retrieving variables, for example.

Let’s make word into an instance variable:

template = "<h1>#{@word}</h1>"get "/" do
@word = "hello"
ERB.new(template).result(binding)
end

This time, this is valid code and the view will be rendered by the browser — but nothing will appear. There is some kind of error occurring when the ERB object tries to resolve the @word instance variable within the string interpolation.

If, however, we used the correct erb tags as follows:

template = "<h1><%= @word %></h1>"

Then the ERB object will recognise that the expression within the tags is Ruby code and needs to be resolved, returning the result.

The second set of fundamental erb tags are the same but without the equals sign, and is sometimes called a scriplet. You use these where you wish to embed loops or other conditional logic into templates, as this signifies to erb to execute the script without saving the return values.

A simple formulation would be:

template = "<h1><% if true %><%= @word %><% end %></h1>"

You can see that the difference is that the code between the <% %> tags needs only to be executed as it expresses logic, while the code between the <%= %> contains an expression which should be evaluated and the results captured.

Aside — Filenames

In general, the names you use for files and their extensions (the part following the full stop, such as ‘.exe’ or ‘.rb’) does not matter. You can call a file whatever you like as it will be executed on the basis of its content (i.e. its file type or format) rather than its name.

However, file extensions are sometimes useful — Windows in particular uses this information to determine which program to open a file with — and in any case, it’s obviously a good idea to give a file extension which matches the file’s type.

In my next blog post, I will explain how Sinatra makes erb easy to use. In relation to filenames, it enables you to create separate erb files which can be fed in as templates to routes. The convention is to name files which will be fed as ERB templates as ‘.erb’ and in fact in Rails, it is required that files have the extension of the output type followed by ‘.erb’ — such as ‘layout.html.erb’.

--

--