Two Great Things

If HTML is our chocolate, and Ruby is our peanut butter, it's now time to combine them together to make something even better. (Yes, that's stolen directly from a Reese's commercial).

index.html.erb

That's a weird file extension, isn't it? Normally, files only have one extension like .txt for text files, .docx for Microsoft Word files, .pdf for PDFs, and so on. This file that we've created has two extensions – .html and .erb. That's right – we're combining two things together – HTML and Ruby (ERB stands for Embedded Ruby).

Let's dive right in to see how this works. Edit the index.html.erb and replace it with the following:

<% fillings = ["Carnitas", "Al Pastor", "Steak", "Fish"] %>
<h1>I love tacos</h1>
<p>There are so many kinds of tacos:</p>
<ul>
  <% for filling in fillings %>
    <li><%= filling %></li>
  <% end %>
</ul>
<p>Currently, there are <%= fillings.size %> kinds of tacos.</p>

There's a bit of weird syntax going on here, and it can take some getting used to, but this is a good example of using Rails' ability to combine HTML and Ruby together. Let's break it down:

  • If we view the resulting page source (i.e. View Page Source in Chrome), we can see that the result is HTML – as we've previously learned, the web browser does not understand anything other than HTML.
  • Any time we want to write Ruby inside our HTML file, we use the special <% and %> symbols in our code. That is, anything inside  <% and %> – otherwise known as ERB tags – will be interpreted as Ruby instead of HTML. In this case, instead of having our fillings (carnitas, al pastor, steak, and fish) hard-coded into our HTML li elements, we've moved them into a Ruby array. Now, when we want to add more fillings, we simply modify the array.
  • We can see that there are ERB tags with and without the equals sign – <% and %> and <%= and %>. Any Ruby code inside the version with the equals sign will be written to the resulting HTML.

Let's examine that last point further, because it's that important to understand. We can see that there are times when we simply want to execute Ruby code, for example, setting the value of a variable:

<% fillings = ["Carnitas", "Al Pastor", "Steak", "Fish"] %>

In this case, we want to set the value of the fillings variable to the Array of Strings we've specified. We don't want anything to be displayed to the end-user as a result (i.e. we don't need anything written out to the resulting HTML).

Likewise, our for-loop is just Ruby code we want executed – we don't actually want anything to be written to the resulting HTML for the <% for... in %> and <% end %> lines of code.

What we do want to be written to our resulting HTML is the value of each filling (e.g. "carnitas"), in between a set of start and end <li> tags, i.e.:

<li><%= filling %></li>

The <li> and </li> are HTML, and filling is Ruby. And we want the value of filling to be written out to our HTML, so we can produce a list of taco fillings – so we must use the version of the ERB tags with the equals sign.

Similarly, the last line that prints the total number of fillings, intermingled with HTML:

<p>Currently, there are <%= fillings.size %> kinds of tacos.</p>

Once again, we have the printing, equals sign version of the ERB tags, because we want the value to appear in the resulting HTML.

Roll the Dice

Time to use this newfound knowledge to build a simple dice game! Let's review the steps for adding new functionality to our app:

  • Decide what resource the functionality represents
  • Add the resource to the routes file
  • Create the controller
  • Write the view using HTML and embedded Ruby (ERB)

We'll repeat the steps that we used to create the tacos resource, a little faster this time.

  • What word would be best for describing this resource? How about dice?
  • Add resources "dice" to our routes file at config/routes.rb
  • Generate a controller by doing rails generate controller dice at the Terminal prompt, stopping and starting the server as necessary
  • Create an empty file at app/views/dice/index.html.erb and get ready to build our code

We'll start with some basic HTML to represent the value of our two dice. In app/views/dice/index.html.erb:

<h1>Roll the dice</h1>
<p>
  3
  4
</p>
<p>
  The total is 7
</p>

Making sure our rails server is running, head over to /dice in the browser and see the result. If everything went well, we should see something similar to:

If we hit refresh on the browser a few times, we'll see that, unsurprisingly, the result of the roll of our two dice is 3 and 4, every time. We'd win a lot of money in Vegas!

As exciting as that would be, that's not a very complete game we've built. Instead of 3, 4, and 7, those values should probably be dynamically and randomly generated. Let's write the Ruby code to generate three variables to represent the value of the first die, second die, and total. How would we do this in Ruby? Maybe something like:

die1 = rand(1..6)
die2 = rand(1..6)
total = die1 + die2

(The rand function is built into Ruby – here, we're asking for a random number between 1 and 6). Now, we have Ruby code that creates the three variables that we can use in our HTML, and it's simply a matter of wrapping that code in ERB tags and using the results in the right places:

<%
  die1 = rand(1..6)
  die2 = rand(1..6)
  total = die1 + die2
%>
<h1>Roll the dice</h1>
<p>
  <%= die1 %>
  <%= die2 %>
</p>
<p>
  The total is <%= total %>
</p>
app/views/dice/index.html.erb

Note that we could have written the first three lines using ERB tags on each line, like this:

<% die1 = rand(1..6) %>
<% die2 = rand(1..6) %>
<% total = die1 + die2 %>

But, the way we've done it saves us some typing. Now, if we head back to our finished product in the web browser, and hit refresh a few times... success! We get the values of two random dice displayed, along with a total, with each refresh of the browser.

To make a finer point of this, and to make sure we connect this to what we've learned before, every refresh of the browser initiates a new request to /dice, our application code in the routes file and index.html.erb does its job, which is to return the appropriate response (by crunching the code we've given it in order to generate HTML) to the web browser.

Just Hit Refresh?

It's a perfectly fine development experience, but asking our end-user to hit the Refresh button in the web browser is probably not the best idea for acquiring and retaining end-users, so let's make it better. A simple link that reads Roll Again should do the trick.

We know that we can create a link in HTML by using the <a> or anchor element – but where should it go, i.e. what should the href be? In this case, a link back to the resource we're already on should suffice:

<a href="/dice">Roll Again</a>

Wrap that in a new paragraph, and our code (so far) looks like:

<%
  die1 = rand(1..6)
  die2 = rand(1..6)
  total = die1 + die2
%>
<h1>Roll the dice</h1>
<p>
  <%= die1 %>
  <%= die2 %>
</p>
<p>
  The total is <%= total %>
</p>
<p>
  <a href="/dice">Roll Again</a>
</p>

Great – now the end-user doesn't have to hit refresh and won't be scared away by our app!

Making it Complete

No dice game would be complete without... images of dice. So let's make that happen! The actual game images have already been provided – if we look in the public/images directory, we can see them there.

Note: a file in the public directory can be accessed on the web via its file path, without the public part. For example, the file located at public/images/dice/1.svg is available on the web at /images/dice/1.svg.

Currently, these two lines of code...

<%= die1 %>
<%= die2 %>

... do the job of simply spitting out the two random numbers contained within the die1 and die2 variables, respectively. How do we replace this with images of dice? With the HTML img tag, of course! Let's start by replacing those two lines with hard-coded images of the 3 and 4 dice:

<img src="/images/dice/3.svg">
<img src="/images/dice/4.svg">

Hitting refresh in the browser (or using the Roll Again button) shows us that we're now displaying images of dice, but they're back to being the hard-coded values of 3 and 4, as expected. Of course, we don't want that, so let's replace just the part of the code that needs to replaced with the dynamically-generated values, to get our finished product:

<img src="/images/dice/<%= die1 %>.svg">
<img src="/images/dice/<%= die2 %>.svg">

One more refresh of the page, and we see our finished dice game, in all its glory. Here's the complete code, for reference and comparison, in case you got lost for any reason along the way:

<%
  die1 = rand(1..6)
  die2 = rand(1..6)
  total = die1 + die2
%>
<h1>Roll the dice</h1>
<p>
  <img src="/images/dice/<%= die1 %>.svg">
  <img src="/images/dice/<%= die2 %>.svg">
</p>
<p>
  The total is <%= total %>
</p>
<p>
  <a href="/dice">Roll Again</a>
</p>
app/views/dice/index.html.erb

Lab

Sticking with casino games, let's create a game that deals a five-card hand of poker.

Challenges:

  • (First try) Follow the same pattern as we used for dice – write it this way first, then think about...
  • (Refactor) How do we avoid a duplication of cards?
  • (Refactor again) What if the business requirements changed from 5 cards to 7 cards? How about 100 cards?