This is the curriculum for the 2019 Alpha Web Developer Bootcamp.

Layout

Have you noticed how your views actually contain a valid HTML document, but you never write the <body> and <title> and other tags like that yourself? That's because they're in the app/views/layouts/application.html.erb file, which looks something like this:

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
<head>
  <title>Wikipages</title>
  <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  <%= csrf_meta_tags %>
</head>
<body>

  <%= yield %>

</body>
</html>

The <%= yield %> bit of code is where the views with content for each page is inserted. If you ever want to change something on every page - such as adding a logo or nav bar - the layout file is the place to do it.

Passing Content to the Layout

In some cases, you will want to specify content that changes for each page, but that section of the page is actually in the layout. The <title> take is a good example. Remember from our work with HTML pages that the <title> tag defines what is displayed in the tab of the user's browser for our webpage. If we want to make every page have a different <title> we need a way to set it in each view, and then retrieve it in the layout. For this, we use a Rails View Helper. Starting in the view, here's how to set a new content_for tag:

app/views/contacts/show.html.erb

<% content_for(:title, "New contact | Wikipages") %>

<h1>New contact</h1>
...

Notice that we use <% %> tags instead of <%= %> to specify the content_for tag. Without the equal sign, nothing will be displayed on the page, but rather we're providing programatic instructions to the Rails template engine. In this case, we're telling it what content to assign to a specail tag called "title" for use somewhere else on the page.

Then, here's how to retrieve it in the layout.

app/views/layouts/application.html.erb

<title><%= yield(:title) %></title>

We have seen code wrapped in <%= %> before. That means it is Ruby code that we want to be printed to the page.

content_for blocks

When your layouts get more complex, such as with nav bars that change depending on what page you're on, you can write longer content_for 's like this:

<% content_for(:navbar) do %>
  <li><a href="/">Home</a></li>
  <li><a href="something/else">Something else</a></li>
  <li><a href="etc">Etc.</a></li>
<% end %>

Simple Forms from LEARN on Vimeo

Even - Odd Website

Commit new Rails app

rails new even_odd
cd even_odd
git add .
git commit -am 'inital app commit'
: [master (root-commit) 7ce2def] inital app commit
:  80 files changed, 1263 insertions(+)
...
rails generate controller main
:       create  app/controllers/main_controller.rb
:       invoke  erb
:       invoke  test_unit
:       create    test/controllers/main_controller_test.rb
:       invoke  helper
:       create    app/helpers/main_helper.rb
:       invoke    test_unit
:       invoke  assets
:       invoke    coffee
! Running via Spring preloader in process 38398
:       create      app/assets/javascripts/main.coffee
:       invoke    scss
:       create      app/assets/stylesheets/main.scss
:       create    app/views/main
git add .
git commit -m 'generates main controller'
: [master 57445a6] generates main controller
:  5 files changed, 17 insertions(+)
...

Adding a Route

cat config/routes.rb
: Rails.application.routes.draw do
:   get '/answers' => 'main#answers'
: end

Adding the Controller

cat app/controllers/main_controller.rb
: class MainController < ApplicationController
:   def answers
:     if params[:number].to_i.even?
:       @result_string = "Even"
:     else
:       @result_string = "Odd"
:     end
:
:     # Can you predict the name and path to the view file that this controller action will use?
:   end
: end

If we start up the Rails server, and load that route now, we get an error.

MainController#answers is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.

No View yet!

Adding the View

Do you remeber where Rails expects to find the view for our new controller route?


This is purly by convention.  We can actually render a route from anywhere we want but in almost all cases, its best to stick with the convention.  Not only do we have to write less code, but any developers who look at the code after us will expect the app to be setup that way and anything we can do to lessen their cognitive load, the better.

So, what does our view look like?

```bash
cat app/views/main/answers.html.erb
: Number was: <%= @result_string %>
: <hr/>
: <form action="answers" method="get">
:   <input type="text" name="number"/>
:   <input type="submit" value="Submit Number"/>
: </form>

And now, if we reload our browser, we will see a form where we can check the even/oddness of any number.

This is a great spot for a commit.

git add .
git commit -m 'adds controller and view for even odd calculatoR'
: [master 2e0a5fa] adds controller and view for even odd calculatoR
:  3 files changed, 16 insertions(+), 1 deletion(-)
:  create mode 100644 app/views/main/answers.html.erb

Extra credit

Did you notice that our application reports 'even' the first time we go to the page, even though we haven't entered a number yet? What's going on there? A non-value is neither even nor odd, so how did we decide on 'even'? Let's take another look at the controller and see if we can find the answer.

cat app/controllers/main_controller.rb
: class MainController < ApplicationController
:   def answers
:     if params[:number].to_i.even?
:       @result_string = "Even"
:     else
:       @result_string = "Odd"
:     end
:
:     # Can you predict the name and path to the view file that this controller action will use?
:   end
: end

We want to look closer at this line if params[:number].to_i.even?. What is that saying? It is looking for a number to be passed in as "number" in the parameters, and parameters is a simple Ruby hash, like this for example: {number: 42}. In the case where we come to the page for the very first time, there are no parameters at all, params actually looks like this: {}. So what happens when we access a missing attribute of a Ruby hash? Let's find out:

my_hash = {}
puts my_hash[:missing_key]
:

We get a nil value as a response from Ruby. So, notice in our controller code that we call .to_i.even? on this value, which we now know is nil. "What is the integer value of nil?" you might be asking yourself.

puts "Nil's integer value is #{nil.to_i}"
puts "And does Ruby think that is even? #{nil.to_i.even?}"
: Nil's integer value is 0
: And does Ruby think that is even? true

Making our view a bit more user friendly

So now that we know that a non-answer logically in Ruby is even, we probably want to make our application behave a bit more user friendly, and hide the result section of our view until the user has entered a value.

Group Exercise

What changes should we make to the controller and view so that the results are hidden until the user actually enteres a value and submits the form?

The Flash

You might be wishing you could add a message to the user after something has been checked, letting them know that the check was successfully made. Rails provides a tool for doing this called flash messages. (This has nothing to do with the Adobe Flash, by the way.)

Here's how it works: you set a flash message in the controller. The flash message is stored as a temporary cookie on the user's browser. (A cookie is a place a site can store information in a user's web browser; every time the user makes a request to that site, the browser sends the cookie's data with the request.) When the next page loads, the server sees the flash message, adds it to the page (it's usually included in the layout), and clears the cookie. If the user reloads the page or browses to a new page, the message disappears.

Let's add a flash to the Password Checker:

app/controllers/password_controller.rb

class PasswordController < ApplicationController
  def check_credentials
    if valid(params[:userid], params[:password])
      flash.now[:notice] = "Your credentials are valid"
    else
      flash.now[:alert] = "Your credentails are not valid"
    end
    render('check_credentials.html.erb')
  end
end

The flash acts very much like a hash, and the two keys you're allowed to set are flash[:notice] and flash:alert.

(If we were using redirect_to, we would just need flash[:notice].)

Now, let's add the flash message to the layout, right above the page content:

app/views/layouts/application.html.erb

<body>

  <%= flash[:alert] %>
  <%= flash[:notice] %>

  <%= yield %>

</body>

If we check a password, we can see our flash message. And if we refresh the page, it disappears.

Password Checker in Rails

Challenge - Check User ID and Password on a Website

  • Create a route: /check_password to call a method called check in PasswordController.
  • Create a controller: PasswordController with a method called check.
  • Create a view for the check method in PasswordController called check.html.erb.

In a browser use http://localhost:3000/check_password?userid=joe&password=letmein to check the credentials.

Inside the method:
  • Store the user ID and password in instance variables
  • Do some checks for whether they are valid; if they are, return with a meesage 'Credentials are acceptable', otherwise print 'Try again.'

Test the method with the URL above.

In the view:
  • Show the result String at the top (if there is any)
  • Create a form to which uses method GET to the check_password route, and contains two fields with the names of the parameters, and a submit button.

Test that the webpage works the same as using the URL above.

Rails Hi/Lo with Forms Challenge
  • Create a form which uses the action: GET to the try route, and contains one field with the guess parameters, and a submit button.

Today's Tentative Schedule

9:15am - Stand Up

9:30am - Introduction to Rails: Layout, Simple Forms and Flash

11:00am - Challenge: Password Checker

12:00 noon - Lunch

1:00pm - continue with Password Checker Challenge and Hi/Lo Game with forms

4.30pm - Review

5:00pm - Class Ends