Exercise 50: Your First Website

These final three exercises will be very hard and you should take your time with them. In this first one you'll build a simple web version of one of your games. Before you attempt this exercise you must have completed Exercise 46 successfully and have a working gem installed such that you can install packages and know how to make a skeleton project directory. If you don't remember how to do this, go back to Exercise 46 and do it all over again.

Installing sinatra

Before creating your first web application, you'll first need to install the "web framework" called Sinatra. The term "framework" generally means "some package that makes it easier for me to do something." In the world of web applications, people create "web frameworks" to compensate for the difficult problems they've encountered when making their own sites. They share these common solutions in the form of a package you can download to bootstrap your own projects.

In our case, we'll be using the Sinatra framework, but there are many, many, many others you can choose from. For now, learn Sinatra, then branch out to another one when you're ready (or just keep using Sinatra since it's good enough).

Using gem, install Sinatra:

$ sudo gem install sinatra
Password:
Fetching: rack-1.5.2.gem (100%)
Successfully installed rack-1.5.2
Fetching: tilt-1.4.1.gem (100%)
Successfully installed tilt-1.4.1
Fetching: rack-protection-1.5.3.gem (100%)
Successfully installed rack-protection-1.5.3
Fetching: sinatra-1.4.5.gem (100%)
Successfully installed sinatra-1.4.5
Parsing documentation for rack-1.5.2
Installing ri documentation for rack-1.5.2
Parsing documentation for rack-protection-1.5.3
Installing ri documentation for rack-protection-1.5.3
Parsing documentation for sinatra-1.4.5
Installing ri documentation for sinatra-1.4.5
Parsing documentation for tilt-1.4.1
Installing ri documentation for tilt-1.4.1
Done installing documentation for rack, rack-protection, sinatra, tilt after 303 seconds
4 gems installed
$

This will work on Linux and Mac OSX computers, but on Windows just drop the sudo part of the gem install command and it should work. If not, go back to Exercise 46 and make sure you can do it reliably. You may also see different versions and names of packages installed but should not see any errors.

Make a Simple "Hello World" Project

Now you're going to make an initial very simple "Hello World" web application and project directory using Sinatra. First, make your project directory based on the skeleton you have:

$ cd projects
$ cp -r skeleton gothonweb
$ cd gothonweb
$ mv bin/NAME bin/app.rb
$ mv lib/NAME lib/gothonweb
$ mv lib/NAME.rb lib/gothonweb.rb
$ mv NAME.gemspec gothonweb.gemspec
$ mv tests/test_NAME.rb tests/test_gothonweb.rb
$ rm tests/NAME_tests.rb
$ mkdir views
$ mkdir static

You'll be taking the game from Exercise 43 and making it into a web application, so that's why you're calling it gothonweb. Before you do that, we need to create the most basic Sinatra application possible. Put the following code into bin/app.rb:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
require 'sinatra'

set :port, 8080
set :static, true
set :public_folder, "static"
set :views, "views"

get '/' do
    return 'Hello world'
end

get '/hello/' do
    greeting = params[:greeting] || "Hi There"
    erb :index, :locals => {'greeting' => greeting}
end

Then run the application like this:

$ ruby bin/app.rb
[2014-07-03 16:02:44] INFO  WEBrick 1.3.1
[2014-07-03 16:02:44] INFO  ruby 2.1.2 (2014-05-08) [x86_64-darwin11.0]
== Sinatra/1.4.5 has taken the stage on 8080 for development with backup from WEBrick
[2014-07-03 16:02:44] INFO  WEBrick::HTTPServer#start: pid=20305 port=8080

The output you see is Sinatra running your little "hello world" application. However, if you did this:

$ cd bin/   # WRONG! WRONG! WRONG!
$ ruby bin/app.rb # WRONG! WRONG! WRONG!

Then you are doing it wrong. In all Ruby projects you do not cd into a lower directory to run things. You stay at the top and run everything from there so that all of the system can access all the modules and files. Go reread Exercise 46 to understand a project layout and how to use it if you did this.

Finally, use your web browser and go to http://localhost:8080/ and you should see two things. First, in your browser you'll see Hello, world!. Second, you'll see your Terminal with new output like this:

::1 - - [03/Jul/2014 16:07:10] "GET / HTTP/1.1" 200 11 0.0136
localhost - - [03/Jul/2014:16:07:10 PDT] "GET / HTTP/1.1" 200 11
- -> /

Those are log messages that Sinatra prints out so you can see that the server is working, and what the browser is doing behind the scenes. The log messages help you debug and figure out when you have problems.

I haven't explained the way any of this web stuff works yet, because I want to get you setup and ready to roll so that I can explain it better in the next two exercises. To accomplish this, I'll have you break your sinatra application in various ways and then restructure it so that you know how it's setup.

What's Going On?

Here's what's happening when your browser hits your application:

  1. Your browser makes a network connection to your own computer, which is called localhost and is a standard way of saying "whatever my own computer is called on the network." It also uses port 8080.
  2. Once it connects, it makes an HTTP request to the bin/app.rb application and asks for the / URL, which is commonly the first URL on any website.
  3. Inside bin/app.rb you've got a list of URLs and what classes they match. The only one we have is the '/', 'index' mapping. This means that whenever someone goes to / with a browser, Sinatra will find the class index and load it to handle the request.
  4. Now that Sinatra has found class index it calls the index.GET method on an instance of that class to actually handle the request. This function runs and simply returns a string for what Sinatra should send to the browser.
  5. Finally, Sinatra has handled the request and sends this response to the browser, which is what you are seeing.

Make sure you really understand this. Draw up a diagram of how this information flows from your browser, to Sinatra, then to index.GET and back to your browser.

Stopping and Reloading Sinatra

When you make a change to your Sinatra application you need to stop it and run it again. Simply hit CTRL-c and it will stop. Then run the command ruby bin/app.rb again to start it. You can also use a tool called rerun to automate this. You install rerun using sudo gem install rerun and then use it like this:

$ rerun 'ruby bin/app.rb'

Now make a change to the bin/app.rb file that's small and it should stop then rerun your Sinatra application. It can take a few seconds for rerun to detect the change, but it should work.

Fixing Errors

Go to http://localhost:8080/hello/ with your browser and watch as Sinatra gives you a standard error page. You're receiving this error because you have not created the views/index.erb template yet which line 14 needs to work correctly. We'll fix this in the next section, but for now read through this and familiarize yourself with what an error looks like.

  1. Look at the top right of the page for the main error, "Errno::ENOENT at /hello/" followed by a better description, the file involves, and possible line.
  2. Look at the BACKTRACE section. This shows you a trace of each line involved in the error that should look familiar to you from other errors you've seen.
  3. Look at the GET and POST section. These will say there is no data, but in later exercises you'll be passing data that might show up here as variables from forms.
  4. Look at the COOKIES section. This is data your browser stores for each request, but we won't be using this in this book yet.

Create Basic Templates

You can break your Sinatra application, but did you notice that the "Hello World" isn't a very good HTML page? This is a web application, and as such it needs a proper HTML response. We'll create one in the second handler for /hello/ that's in line 12-15. A "handler" is simple a chunk of code that runs when your browser goes to a new place in the application. I'll explain a lot more in the enxt two exercise, but for now put this in the file views/index.erb:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<html>
    <head>
        <title>Gothons Of Planet Percal #25</title>
    </head>
<body>

<p>
I just wanted to say <em style="color: green; font-size: 2em;"><%= greeting %></em>.
</p>

</body>
</html>

If you know what HTML is, then this should look fairly familiar. If not, research HTML and try writing a few web pages by hand so you know how it works. This HTML file, however, is a template, which means that Sinatra will fill in "holes" in the text depending on variables you pass in to the template. Every place you see $greeting will be a variable you'll pass to the template that alters its contents.

Once you have that in place, visit http://localhost:8080/hello/?greeting=Hi in your browser and you should see a different message in green. You should also be able to do a View Source on the page in your browser to see that it is valid HTML. You can also try http://localhost:8080/hello/ alone and see the default message.

This may have flown by you very fast, so let me explain how a template works:

  1. The handler get '/hello/' do specifies what happens when your browser goes to /hello/.
  2. In the handler we are getting the ?greeting=Hi part by using the params Hashmap. Sinatra builds params from the request, and we want to make the greeting from it. Notice the || "Hi There" at the end? This is a way of saying, "Either use what's in params or use 'Hi There'."
  3. Next the /hello/ handler uses the erb function (remember Ruby lets you not use parenthesis on function calls) to "render" the :index view. The :locals = {'greeting' => greeting} part says, "Also give this greet view the local variables greeting with this setting."
  4. When I say :greet view I mean the file views/index.erb you created above. Sinatra knows this too and loads it for you, passing in the greeting variable as you requested.
  5. The views/index.erb file is what's called a template, and it has holes in it that is dynamically created. The part that reads <%= greeting %> does that, so Sinatra runs this, takes the resulting HTML, and sends it to your browser.

To get deeper into this, change the greeting variable and the HTML to see what effect it has. We will get more into how this works in the last two exercises of this book, but play with this some more and see what you can do.

Study Drills

  1. Read the documentation at http://http://www.sinatrarb.com/documentation.html.
  2. Experiment with everything you can find there, including their example code.
  3. Read about HTML5 and CSS. It's a large topic but just try to get familiar with it.
  4. Put some content in static/howdy.html and go to http://localhost:8080/howdy.html. If it doesn't work, make sure your app.rb file has the correct :public_folder setting.

Common Student Questions

I can't seem to connect to http://localhost:8080/.
Try going to http://127.0.0.1:8080/ instead.
I can't use port 8080 on my computer.
You probably have an anti-virus program installed that is using that port. Try a different port by changing the set :port, 8080 line to a different number above 1024.

Video