If statements and comparisons

So far we’ve just used ruby to evaluate simple expressions. Coding becomes a lot more interesting when you can use your code for simple logic tasks:

if age >= 18
  "Here's the vodka you wanted."
else
  "Move along please."
end

The code within the if block will only be run if the statement is true. If the statement is false, the code in the else block will be run instead.

Comparisons

There are some basic comparisons that will be useful when using if statments

SymbolMeaning
==Is equal to
>Greater than
<Less than
>=Greater than or equal to
<=Less than or equal to
!=Not equal to

Comparisons evaluate to true or false.

Task:

Working with a partner, try out each of these comparisons in irb:

4 == 5

'five'.length > 5

a = 20
a <= 20

true >= false

'aardvark' < 'anteater'

'we' != 'done yet'

Post requests revisited

Last time we looked at responding to get requests:

get '/' do
  "Hello there!"
end

As you know from the Introduction to Web Programming course, GET is only one of several types of web requests. Another is the POST request, which is commonly used for submitting data from a web form.

Suppose we have the following HTML form:

<form method="post" action='/'>
    <input type='text' name='user_name'>
    <input type='text' name='user_age'>

    <input type='submit'>
</form>

The form will submit via a POST request to the root url, /. We can respond to this using the following sinatra block:

post '/' do
  name = params[:user_name]
  age  = params[:user_age]

  "Hello #{name}. You are #{age} years old."
end

Note that, like the words matched in the url, the value of the user_name field is made available in the params hash. (If you want to have a look at the params hash you could put a raise params.inspect at the beginning of the method.)

Task:
  1. Fork the repository https://github.com/code61/sinatra_c3s2 (using the fork button on the top right of the repository page on github).
  2. Clone down your fork of the repository onto your laptop.
  3. Open the sinatra_c3s2 project in Sublime Text and have a read through app.rb. See if you can predict what the app will do.
  4. Run the app from the command line (ruby app.rb), to see if you were right.
  5. (Optional) Add the line raise params.inspect right at the top of the post block. Restart the app and see what happens. When you've had a look, make sure you remove the line again!

App deployment

We’re now going to look at how to get the Sinatra app online, a process known as deployment. We’re going to deploy the app to Heroku, which offers easy hosting for a variety of web frameworks - all you need to do is push up a git repository, and they take care of everything else!

You should have set up a heroku account and uploaded your ssh keys for homework.

Preparation

There are a few files sinatra_c3s2 that we need if we’re going to deploy the app to heroku:

  • config.ru
  • Gemfile

config.ru contains the lines

require './app.rb'
run Sinatra::Application

These lines are to tell Heroku which file to find your application (app.rb) and how to run it (using Sinatra).

The Gemfile is a way of specifying which gems your project uses. Heroku needs to know this so it knows which libraries it needs to run your app. The Gemfile is also really helpful way of remembering yourself which gems you need and is invaluable when working with others.

# Gemfile

source 'https://rubygems.org'

gem 'sinatra'

Along with rubygems, ruby has a tool called bundler. Bundler helps you manage your ruby gems. If you run bundle install it will look in the Gemfile and install any gems that you don’t already have.

$ bundle install

will also create a Gemfile.lock which specifies the exact version of the gems (e.g. the sinatra library) that you are using. This means that heroku (and anyone else you’re collaborating with using git) now knows exactly which gems you are using and can pick to use the same versions. You need to check this in to your repository:

$ git add --all
$ git commit -m "Added Gemfile.lock"

Deploying to Heroku

Once your app is prepared, the first thing you need to do is create a new empty heroku application. To do this you use the heroku create command:

$ heroku create
Creating arcane-gorge-2129... done, stack is cedar
http://arcane-gorge-2129.herokuapp.com/ | git@heroku.com:arcane-gorge-2129.git
Git remote heroku added

You’ll see that it created an app for you. In my case the app is called arcane-gorge-2129 and can be found at http://arcane-gorge-2129.herokuapp.com. It also added a git remote for you.

$ git remote -v
heroku  git@heroku.com:arcane-gorge-2129.git (fetch)
heroku  git@heroku.com:arcane-gorge-2129.git (push)
origin  git@github.com:code61/sinatra_project_1.git (fetch)
origin  git@github.com:code61/sinatra_project_1.git (push)

Deploying

You deploy your repository by pushing to this remote. Heroku will serve whatever is in the master branch. Here we push the master branch up to the heroku remote:

$ git push heroku master
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (11/11), 1.08 KiB, done.
Total 11 (delta 1), reused 0 (delta 0)

-----> Ruby/Rack app detected
-----> Using Ruby version: ruby-1.9.3
-----> Installing dependencies using Bundler version 1.3.2
       Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin --deployment
       Fetching gem metadata from https://rubygems.org/..........
       Fetching gem metadata from https://rubygems.org/..
       Installing rack (1.5.2)
       Installing rack-protection (1.5.0)
       Installing tilt (1.4.1)
       Installing sinatra (1.4.3)
       Using bundler (1.3.2)
       Your bundle is complete! It was installed into ./vendor/bundle
       Cleaning up the bundler cache.
-----> Discovering process types
       Procfile declares types     -> (none)
       Default types for Ruby/Rack -> console, rake, web

-----> Compiled slug size: 25.0MB
-----> Launching... done, v3
       http://arcane-gorge-2129.herokuapp.com deployed to Heroku

To git@heroku.com:arcane-gorge-2129.git
 * [new branch]      master -> master

After you push, heroku automatically updates the app and launches it for you. You can now see the app by visiting the url (in this case http://arcane-gorge-2129.herokuapp.com).

Task:

Deploy your sinatra_c3s2 app to Heroku:

  1. Install your bundle to get a Gemfile.lock. In your sintara_c3s2 directory run:

     $ bundle install
  2. Add your work (and new Gemfile.lock) to your repository:

     $ git add --all
     $ git commit -m &#39;Added Gemfile.lock&#39;
  3. Create a new heroku app:

     $ heroku create
  4. Push your work to Heroku

     $ git push heroku master

    (for subsequent updates you should be ok with just git push heroku).

  5. Visit the url that heroku provide, to check that your app is running!

HTML templates

So far we’ve just returned text to the browser. It would be better if we could return proper HTML pages. We can do this by using HTML templates.

# in app.rb

require 'sinatra'

get '/' do
  erb :form
end
<!-- in views/form.erb -->

<!DOCTYPE html>
<html>
<head>
  <title>Barman 2.0</title>
</head>
<body>
  <div class='container'>
    <form method="post" action='/'>
      <input type='text' name='user_name' placeholder='name'>
      <input type='text' name='user_age' placeholder='age'>

      <button type='submit'>Submit</button>
    </form>
  </div>
</body>
</html>

The line erb :form tells sinatra to look for a file called form.erb in a folder called views. It then processes that file using the erb (embedded ruby) templating language and returns the result to the user.

The form.erb above isn’t very interesting: it is just a static template and doesn’t have any ruby embedded in it. Let’s look at a slightly better example:

# in app.rb

get '/' do
  erb :form

post '/' do
  @name = params[:user_name]
  @age  = params[:user_age]

  erb :welcome
end
<!-- in views/welcome.erb  -->

<!DOCTYPE html>
<html>
<head>
    <title>hello</title>
</head>
<body>
    <h1>Hello <%= @name %>. You are <%= @age %> years old.</h1>
</body>
</html>

The important bits are:

  • In app.rb we assign params[:name] to a special type of variable @name. The special type of variable is an instance variable which has to begin with a single @.
  • We use the instance variable in the views/greet.erb inside a special erb tag <%= ... %>. The erb templater looks for these tags and interprets the inside as ruby.

Sinatra emphasises convention over configuration: rather than specifying the exact place to find the template, you just give the name and Sinatra ‘knows’ where to look. This means you have to write less code in the long run, but also that you have to know the conventions before you start.

Sinatra template summary

  1. You call a template with the line erb :template_name.
  2. For this to work, you will need a template called template_name.erb in the views folder.
  3. Instance variables (ones that start with @) will be shared with the template.
  4. You use these shared instance variables in the template by putting them inside an erb tag: <%= @my_variable >
Task:
  1. Uncomment the bottom part of sinatra_c3s2/app.rb and comment out the top two blocks.

  2. Restart your server and check you can see the new page h1 sections.

  3. Add twitter bootstrap to your templates, linking to the online hosted version as described on the Bootstrap CDN section of the bootstrap docs.

  4. Add a div class=&#39;container&#39; around the page content, and add some styling to the form (see here).

  5. Add and commit your changes to your local git repo.

  6. Deploy your changes:

     git push heroku

Logical operations

It’s often useful to combine various conditions:

if age < 12 || height < 1.2
	"You are allowed on the ride."
else
	 "Try a different helter-skelter. There's nothing for you here."
end

It the above snippet of code the || means or. If the person is under 12 or is smaller than 1.2m they are allowed on the ride.

Logical operations

Ruby has three ways of combining conditions:

and (&&)

If you use an && the output is true only if both inputs are true. You can write this in terms of a truth table:

false && false #=> false
false && true  #=> false
true  && false #=> false
true  && true  #=> true

or (||)

If you use the || the output is true if at least one of the inputs is true. || means one or the other or both:

false || false #=> false
false || true  #=> true
true  || false #=> true
true  || true  #=> true

not (!)

The ! is not. It is a quick way of reversing the truth of the condition:

!false #=> true
!true  #=> false
Task:
  1. Working with a partner, see if you can guess what the following expressions evaluate to. Test in irb to see if you're right.
1 > 2 && 3 > 4

1==1 || 10 ==9

!(5<6)

(1<=2 && 3>4) || (3 <= 4 && "ham")

Truthy and falsey

In the last exercise you found out something interesting:

true && "ham" #=> "ham"

Which is a bit weird. There are actually two things going on here:

  1. The string ham is considered to be true
  2. The overall statement evaluates to the last expression in it

Truthy and falsey

In ruby there are only two values that are considered false: false and nil. We say that false and nil are falsey.

Everything else is considered to be true. We say that all other objects are truthy.

What we mean by ‘considered to be true’ is that if that value is used as a conditional in an if statement then the if code runs:

if 5
	x = 1
else
	x = 2
end

x #=> 1

In the above block of code, as 5 is truthy, the variable x is set to 1.

Task:

The bar is trialling a new initiative: Tuesdays is ladies night at the pub - no men allowed and all drinks are 2-4-1! They want you to update Bouncer 2.0 appropriately.

  1. Add select boxes for sex and day on the form.
  2. Add new templates to be shown on ladies night to (a) turn away a customer if he is male (b) give them an extra drink if they are female.
  3. Add the required logic to app.rb to make it all work.

Homework

Task:
  1. Finish the final exercise from class. Spend a bit of time trying to make the app look more presentable and 'bouncer-like'.
  2. Do the Codecademy Ruby track Sections 3 and 4.

Extension

Task:

Change your sinatra app into something more interesting:

  • Add more options to the form.
  • Make the logic in the views more complicated, so that you're using a few boolean operations.
  • Change your app into a horoscope provider/career adviser/which-star-wars-character-are-you machine.
  • Make it look nice, deploy it and show your friends.