Chapter 1: ClojureBridge Track 1

Overview

This curriculum will introduce you to Clojure by creating a web app called Chatter. We will teach you to use the command line, make code changes in Atom, and push those changes to GitHub so you can share your app with the world.

Each chapter below will work through different concepts of coding and provide Clojure-specific instructions to help you create your app. Review this page, then work your way through each chapter in the table of contents.

The Chatter App Goals & Tools

Specific Goal of Workshop

Create a web app using Clojure code.

We will introduce you to Clojure by creating and deploying a web app where you and your friends can post messages to each other.

Additional Benefits & Learning Opportunities

Required Tools

These should have all been installed at the Friday night Install Fest

Readme

This is the standard structure of most coding projects. You will have a Readme file that will provide a basic overview of the project and the programs you need to open the project.

App

In this curriculum we will create a web app, the Chatter app, that displays posted messages.

Prerequisites

You will need Leiningen 2.0.0 or above installed.

Running The Server & Entering Commands in the terminal

Server

Confirm your server is running properly. To start a web server for the application, enter the following command in the terminal:

lein --version

This will confirm what version of lein and what version of java you have on your machine.

Entering Commands

Confirm you have all the required software/apps installed prior to starting your project. The required tools were installed during Install Fest on Friday night.

We will show examples of commands in terminal (also called the command line). Our prompt is the dollar sign; everything after the dollar sign is the command you enter. You do not need to type $: in your terminal. We may we enter multiple commands in the same snippet — the dollar sign indicates a new command to enter. You should enter each command individually.

The terminal, or command line, you use may have different prompts ($:), only enter the text after the prompt.

Open your terminal and enter the commands below to confirm you have the correct tools installed.

	$: lein -version
    Leiningen 2.5.1 on Java 1.8.0_40-internal OpenJDK 64-Bit Server VM

    $: git --version
    git version 2.1.4

    $: heroku --version
    heroku-toolbelt/3.25.0 (x86_64-linux-gnu) ruby/2.1.5
    You have no installed plugins.

Only enter the first line in the command line; the following lines are the results of your command and indicate the versions of software you have installed on your computer.

If your commands and results look like the example above, you are ready to move on and start working on your Chatter app.

Moving Forward

Once you confirm your server is working properly, move to the next page where were will briefly go over what Clojure is, what the Web is and how we interact with it, and how HTML works.

Next page, Chapter 2: Big Picture

Chapter 2: Big Picture

Clojure, the Big Picture

Clojure is a modern Lisp, which is a programming language, with a focus on functional programming. There are lots of programming languages, and Clojure is just one of them. The image below shows similar programming languages grouped by color. Clojure is in the bottom half in the center; it is a dialect of the Common-Lisp language on the far right.

Clojure is great because:

Most programmers have to use multiple languages to get their jobs done. Web applications often use HTML, CSS, and JavaScript. We'll touch on each of those as we build our web app.

The Web: a basic overview

The Internet is a bunch computers all over the world communicating with each other using a variety of computer programs. Some of those programs are servers that listen for requests and respond with data.

Your web browser is a program that sends requests over HTTP (HyperText Transfer Protocol). Entering, https://github.com into your browser's address bar tells your browser that you want to see that page.

https://github.com is actually a human-readable alias for the numerical address of GitHub's servers. Since your computer isn't directly connected to GitHub, your computer (through your web browser) asks the computers connected to GitHub to forward the request. This request may be passed through various computers to retrieve the data (web page).

On Linux or a Mac, the command traceroute shows you the number of hops request takes to get to GitHub. (On Windows, the command is called tracert.)

On my machine, working from home:

        $: traceroute github.com
        traceroute to github.com (192.30.252.130), 30 hops max, 60 byte packets
        1  192.168.1.1 (192.168.1.1)  5.913 ms  5.908 ms  6.000 ms
        2  * 96.120.49.33 (96.120.49.33)  30.033 ms  30.066 ms
        3  te-0-3-0-1-sur02.webster.mn.minn.comcast.net (68.85.167.149)  30.553 ms  32.776 ms  32.785 ms
        4  te-0-7-0-12-ar01.roseville.mn.minn.comcast.net (69.139.219.134)  32.778 ms  36.059 ms te-0-7-0-13-ar01.roseville.mn.minn.comcast.net (68.87.174.185)  35.686 ms
        5  he-1-13-0-0-cr01.350ecermak.il.ibone.comcast.net (68.86.94.81)  42.354 ms *  43.213 ms
        6  be-10206-cr01.newyork.ny.ibone.comcast.net (68.86.86.225)  63.574 ms  59.894 ms  62.441 ms
        7  pos-2-5-0-0-cr01.dallas.tx.ibone.comcast.net (68.86.85.25)  64.643 ms  64.659 ms  64.653 ms
        8  he-3-14-0-0-cr01.dallas.tx.ibone.comcast.net (68.86.85.1)  67.039 ms  67.079 ms  67.056 ms
        9  he-0-10-0-0-pe07.ashburn.va.ibone.comcast.net (68.86.83.66)  63.013 ms  67.045 ms  88.273 ms
        10  * * *
        11  * * *
        12  * * *
        13  * * *
        14  * * *
        15  * * *
        16  * * *
        17  * * *
        18  * * *
        19  * * *
        20  * * *
        21  * * *
        22  * * *
        23  * * *
        24  * * *
        25  * * *
        26  * * *
        27  * * *
        28  * * *
        29  * * *
        30  * * *

When you enter https://github.com in the address bar, your browser makes a GET request to the GitHub server. There are several types of HTTP requests, but GET is the one that asks the server to send data from a specified resource. The server sends back an HTML page and the Web browser turns the HTML into the web page you see through a process called rendering. You can see the HTML by right-clicking on the page and selecting View Page Source.

HTML Proper

HTML stands for "HyperText Markup Language".

Hypertext means documents can contain links to other pages or images. The structure of the HTML document is encoded using a markup language consisting of opening and closing elements (also called tags). Each segment of text is formatted according to the type of tag (<body>, <title>, <h2>, etc). Each segment of text needs an opening tag, <body>, and a closing tag, </body>. The / signifies the end of the formatting for that segment.

Let's see it in action

Open a text editing program and enter the following text:

<!DOCTYPE html>
<html>
  <head>
    <title>Sample HTML Page</title>
  </head>
  <body>
    This is a sample html page.  This is more text.
  </body>
</html>

Save the file as 'sample.html'. Then open the file in the web browser. On Windows or a Mac, double-click the file to open it in the default browser, or right-click and select Open With. In your browser you should see something like this,

Notice the address bar. Instead of making an HTTP request to a server over the Internet, the browser opened a local file and displayed the HTML. Remember, the web browser renders the HTML to make it appear as you see it in the browser. Right-click the page and select View Page Source to see the HTML elements of the file you saved.

The first line of the source, the DOCTYPE, announces the document is HTML. The text is enclosed between the opening <html> and closing </html> tags, or elements.

Titles, Headers, & Body Text

Inside the HTML document, we have a head and body. These are both elements or tags. The head contains the title; in our example it's "Sample HTML Page". The title is the name of the web page. You will see this text as the tab name and when you bookmark the page in your browser. It is not actually a part of the text on the page.

To add a title on the page itself, add it within the body of the HTML using a header tag. HTML uses various heading tags to indicate the size of the title or heading. The largest is <h1> and the smallest header is <h6>.

Let's add some headers to our example. In your text editing file, add <h2> Our HTML </h2> in the body of the HTML. This will display "Our HTML" as a title. Your text file will look like this:

<!DOCTYPE html>
<html>
  <head>
    <title>Sample HTML Page</title>
  </head>
  <body>
  <h2>Our HTML</h2>
    This is a sample html page.  This is more text.
  </body>
</html>

Save the file and refresh the browser. Experiment with different size heading tags from <h1> to <h6>. Remember to open and close the HTML tags, meaning you must surround the header text with the an opening and closing tag.

Tables

HTML supports tables as well. Add a table to your sample HTML by adding the following within the <body> tags of the text file:

<table>
  <tr>
    <td>Hydrogen</td>
    <td>1</td>
    <td>H</td>
  </tr>
  <tr>
    <td>Helium</td>
    <td>2</td>
    <td>He</td>
  </tr>
  <tr>
    <td>Neon</td>
    <td>10</td>
    <td>Ne</td>
  </tr>
</table>

table encloses the entire table.

tr wraps a table row.

td wraps table data, creating a cell within a row.

When you save and refresh the page, you see the table. You might notice it also looks pretty bad. The HTML we've been using describes the basic structure of the document and leaves the display entirely up to the browser. Another language, called CSS, is used so the browser can display the page in a more pleasing way. We'll touch on CSS later.

The HTML file we have is static HTML. The HTML we see in the web browser is simply the code we have written in the text file. Static HTML works great for some kinds of pages, but our page will change depending on the messages people post to it. So instead of having a file with HTML, we will have a program listening for requests that generate the HTML. As people make requests and post messages, it will generate HTML that reflects the posts.

There's a lot more to HTML, but this gives us enough knowledge for our Chat app.

In the next section, Chapter 3: Starting the Project, we'll start coding our Chatter app.

More HTML Resources

w3schools.com is a great source for learning more about html. Start with the HTML Introduction.

Mozilla Developer Network is another useful resource.

Chapter 3: Starting The Project

Creating a Clojure project

We're going to start by asking Leiningen to start a web application for us.

  $: lein new compojure chatter

lein is a tool for managing Clojure projects.

The command above requests permission to create a new compojure project called "Chatter". This command results in the creation of a directorychatter which appears as a folder in your computer's directory (Finder on Mac, Windows Explorer on Windows)

In the terminal, move into the project "Chatter" directory by entering,

  $: cd chatter

Start the server with this command

  $: lein ring server

lein will download a bunch of stuff from the Internet the first time it runs. After this finishes, your default browser opens a page that says, "Hello World".

Notice the address bar.

In the web browser, right-click and select View Page Source. You see that it's not even an HTML document, it's just the string "Hello World".

In the terminal you see the message, "Started server on port 3000". This means your server is working and you will be able to view your app in the browser. We initiated this connection earlier when we entered lein ring server in the command line.

Stop the server connection by hitting "Ctrl + C" in the command line.

Let's take a closer look at what's in the chatter directory. In Atom, it looks like this:

.
├── project.clj
├── README.md
├── resources
│   └── public
├── src
│   └── chatter
│       └── handler.clj
└── test
    └── chatter
        └── handler_test.clj

6 directories, 4 files

project.clj is a Clojure file that describes what our project does and what other programs it needs to run.

README.md is a markdown file (.md), which describes the program's prerequisites, how to run the program, and the license.

resources is a folder where you store static HTML, CSS, and images.

src is where the source code lives.

test is where the tests are stored. (A best-practice method is to test the directory before starting the project — we will skip this step in this tutorial.)

A closer look at the src directory

In the editor, open the file src/chatter/handler.clj.

The file that ends with ".clj" indicates this is a Clojure file. Clojure programs are made up of expressions. Expressions are either a single name, number, string, or a list of expressions beginning with a paren (or parenthesis). These expressions make your app appear and function in a web browser.

In the src/chatter/handler.clj file, the first expression (ns chatter.handler ... tells Clojure what we want to call the namespace (ns) being defined in this file. In this case we want to call it "chatter.handler".

The sub-expression, the expression below, begins with (:require .... This is importing the ring and compojure libraries. Those are low-level Clojure libraries for building web apps.

The second expression is (defroutes app-routes .... defroutes is specific to Clojure web apps. It creates a set of routes and gives them the name app-routes. The expressions after symbol app-routes are the route definitions. There are two here.

The first is:

(GET "/" [] "Hello World")

There are four parts to this expression:

  1. GET: this is the type of HTTP request we want to handle. GETs are requests for information or data.
  2. "/": this is the name of the web page. / means the top level.
  3. []: this is an empty parameter vector. When you do a search or fill out a form, the parameters narrow the result.
  4. "Hello World": the result that gets sent back to the requesting browser.

After the GET request is the second route definition app-route, which is:

(route/not-found "Not Found")

This means when the server gets any kind of request other than GET, it should return "Not Found."

In your browser, right-click on the page and select Inspect (if Chrome) or Inspect Element (with Firefox).

The HTML tab shows what the HTML document looks like. The default is an empty head and a body with the string "Hello World". This is different from what we saw when we used View Page Source. The browser requested html but only got a string back, and it fleshes out a legal page from this information.

Click on the Net tab and refresh the page. You see the request is actually a GET request and the response contained a status code of 200 — that indicates the request was successful.

In your browser address bar, type http://localhost:3000/non-existent-page. Now you see the GET request is in red and has a status of 404, which indicates that the server couldn't find the page. This was handled by the line,

(route/not-found "Not Found")

In the editor, the third and final expression of the handler.clj file says,

(def app ...

def is how you declare a variable in Clojure. The format is: (def name doc-string? init?)

  1. def: introduces the def expression.
  2. name: the name you want to give the variable.
  3. doc-string?: an optional description on what the variable is and how it is meant to be used.
  4. init?: an optional value the variable will be set to. If unset, the variable is unbound.

The name of the variable is app and it's being assigned the result of (wrap-defaults app-routes site-defaults)

Stop the server by going back to the terminal and holding the "Ctrl+C".

git

Since we haven't made any changes yet, this is a good time to put the code under version control. Version control allows developers to keep track of their changes over time. It makes it easy to experiment and coordinate work with others.

In the Chatter directory, enter the command:

    $: git init
    Initialized empty Git repository in /home/crk/chatter/.git/

Now git is monitoring our directory. Verify the status of our directory by entering,

    $: git status

    On branch master

    Initial commit

    Untracked files:
      (use "git add <file>..." to include in what will be committed)

	.gitignore
	README.md
	project.clj
	src/
	test/

    nothing added to commit but untracked files present (use "git add" to track)

We're on the master branch. The master branch is the main place where our code will be. It says "Initial commit" because we're just initializing git. Git doesn't know anything about the files in our project, but it has spotted the README.md and project.clj files as well as the src and test directories. It also spotted a file called .gitignore. Files beginning with a dot are normally hidden unless you specifically ask to see them. .gitignore is a special file; it tells git what kind of files we don't want git to track. These will mostly be compiled code, test reports, and log files.

Git follows a two-step process. First you add the changes; they become staged. Then, you commit all of the staged changes. Let's add everything. Enter the following in the command line:

    $: git add .

The "." tells git the current directory and below. We are telling git to add all of the Chatter files to the master branch.

Now when we ask git for the status:

    $: git status
    On branch master

    Initial commit

    Changes to be committed:
      (use "git rm --cached <file>..." to unstage)

	new file:   .gitignore
	new file:   README.md
	new file:   project.clj
	new file:   src/chatter/handler.clj
	new file:   test/chatter/handler_test.clj

All of our stuff is ready to be committed. That requires a commit message. After -m enter a short message that will help you identify the changes in this commit.

    $: git commit . -m "initial commit"
    [master (root-commit) 44a560f] initial commit
    5 files changed, 66 insertions(+)
    create mode 100644 .gitignore
    create mode 100644 README.md
    create mode 100644 project.clj
    create mode 100644 src/chatter/core/handler.clj
    create mode 100644 test/chatter/core/handler_test.clj

Now when we ask git for the status:

    $: git status
    On branch master
    nothing to commit, working directory clean

There haven't been any changes since our last commit, so there's nothing to see. Let's check the log.

    $: git log
    commit 44a560f1653770afac01aea2c9279a7af46a46eb
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 16:43:37 2014 -0600

        initial commit

We see there's been one commit, the commit hash (which uniquely identifies every commit), the author's name, the date, and the commit comment.

By keeping track of changes, git makes it easy to go back to an earlier point — you are creating different versions of the file. By itself, it won't do much if our hard drive suddenly dies. But git allows you to have repositories on other computers, so if your computer dies, your code lives on. GitHub is a company hosting source code; it's free if you don't mind that other people can see your code. As a safety measure, and for version control, we will put our code on GitHub.

Log into https://github.com and click, "create repository" (the "+" sign on the top menu). Name it "Chatter". That will open a page for your new repository. We want to push an existing repository, enter the following:

NOTE: be sure to use the url that GitHub gives you, not the one listed.

    $: git remote add origin https://github.com/crkoehnen/chatter.git
    $: git push -u origin master
    Username for 'https://github.com': crkoehnen
    Password for 'https://crkoehnen@github.com':
    Counting objects: 13, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (7/7), done.
    Writing objects: 100% (13/13), 1.54 KiB | 0 bytes/s, done.
    Total 13 (delta 0), reused 0 (delta 0)
    To https://github.com/crkoehnen/chatter.git
    * [new branch]      master -> master
    Branch master set up to track remote branch master from origin.

Back on GitHub, click on the "Chatter" link and you'll go to the main page for the repository. Note there is a single commit and the text is identical to what's in our README.md file.

Workflow

Now we're going to start changing the templated code to make it our web app. We're going to follow a certain workflow:

  1. branch the code
  2. write some code
  3. try the code
  4. repeat 2-3 times until we're happy with the changes
  5. merge the branch into master
  6. push the changes to GitHub

This methodology allows us to isolate changes in their own branch. If we change our minds or discover we've made a mistake, it's easy to revert back to an earlier version using git.

In Chapter 4 we will create a new branch to make changes on, change the code, then commit and merge the changes and push to GitHub.

Chapter 4: Branches, Merges, Commits

In this section, we will alter the code. We will start with something small and fix the readme.md. Before we do that, we want to branch the code to ensure we can track the changes we make. Branching the code is a version control method; we keep the original files on the master branch and create a separate branch where we will make the changes. When we are confident our changes are correct, we will merge the new branch to the existing master branch. The updated files on the new branch essentially overwrite the files on the master branch.

Branch the code

Branching tells git that we are making changes to the current set of files and that we want to have a specific name for this version. To check what branch you are currently on, enter:

    $: git status
    On branch master
    Your branch is up-to-date with 'origin/master'.
    nothing to commit, working directory clean

This tells us we are currently on the master branch. This is where the original code lives, we want to create a new branch where we can make changes. To create a new branch, use the "git branch" command and name the new branch. Let's call our new branch "fix-readme". This is a reminder to ourselves what kind of changes we are making in this new branch.

    $: git branch fix-readme

If you enter git branch without the specific branch name, the terminal will list all branches that exist in this directory.

    $: git branch
    fix-readme
    * master

Notice there are now two branches, master and fix-readme. The asterisk (*) indicates which branch you are on. In the command above, you see we are on the master branch. We want to switch to the fix-readme branch. We do this by "checking out" the branch we want to be on. Then we will confirm we are on the new branch.

    $: git checkout fix-readme
    Switched to branch 'fix-readme'


    $: git branch
    * fix-readme
    master


    $: git status
    On branch fix-readme
    nothing to commit, working directory clean

We've confirmed we are now on the fix-readme branch.

It's important to check out the branch when making changes. Git does not do it automatically, so you can end up committing changes to the master branch. This usually isn't disastrous, but it's often very messy to clean up if things go wrong and it's difficult to keep track of versions.

Changing README.md

Open the README.md file in your editor and replace the two "FIXMEs" with different text. Save the file.

Now, if you ask git for the status, it will show that README.md has changed.

    $: git status
    On branch fix-readme
    Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md

    no changes added to commit (use "git add" and/or "git commit -a")

We can ask git to show us exactly what changed.

    $: git diff
    diff --git a/README.md b/README.md
    index 9493433..718893f 100644
    --- a/README.md
    +++ b/README.md
    @@ -1,6 +1,6 @@
    # chatter

    -FIXME
    +This is a web app that will display posted messages.

     ## Prerequisites

    @@ -16,4 +16,4 @@ To start a web server for the application, run:

     ## License

    -Copyright © 2014 FIXME
    +Copyright © 2014 clojurebridgemn.org

git diff is telling us the README.md file changed. In the example above, we removed the line that said "FIXME" and added a line saying "This is a web app that will display posted messages." We also changed the FIXME in the copyright to an email address. You should see the specific changes you made in your command line.

Since we changed the description in the README.md, we might as well change the description in project.clj file. Make the change and save that file too. Now the git status should be:

    $: git status
    On branch fix-readme
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md
        modified:   project.clj

    no changes added to commit (use "git add" and/or "git commit -a")

Adding And Committing The Changes

Now that our the changes in the editor are saved, let's add and commit the changes to our new branch. Adding files to the branch puts the updated files on the branch. Committing the updated files to the branch saves creates a specific version of the branch - it puts a timestamp and version description of the current state of the branch. You must add, then commit files to the branch.

First, add the specific file(s) with "git add + file name" to the branch you are on:

$: git add README.md project.clj

Confirm the file was added by checking the status:

    $: git status
    On branch fix-readme
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)

	modified:   README.md
	modified:   project.clj

Two files have been added (modified) and the changes can be committed to the branch. When you enter the git commit command, make sure to enter a description of the changes you are committing; this will help you identify versions and changes. Add a quick note after the file names -m "comment on changes".

    $ git commit -m "fixing README.md description"
    [fix-readme 57aff88] fixing README.md description
     2 files changed, 3 insertions(+), 3 deletions(-)

Check the branch status again:

    $: git status
    On branch fix-readme
    nothing to commit, working directory clean

Git status reports no uncommitted changes because we successfully committed the updated files to the fix-readme branch.

You can also the check the log to see what commits have been made, when they were made, and the brief comment on the commit.

    $: git log
    commit 57aff889c81698394faf8568b63f14130d32599a
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 17:33:41 2014 -0600

        fixing README.md description

    commit 44a560f1653770afac01aea2c9279a7af46a46eb
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 16:43:37 2014 -0600

        initial commit

You see two commits: the last commit from our original branch (initial commit) and our new commit (fixing README.md description).

Merging The Changes

Once we are satisfied with the updated files and they are successfully committed on the fix-readme branch, we'll merge those changes on to the master branch. This means we are going to overwrite the existing files (or add new files) on the master branch.

First, go to the master branch and check its log.

    $: git checkout master
    Switched to branch 'master'
    Your branch is up-to-date with 'origin/master'.

    $: git log
    commit 44a560f1653770afac01aea2c9279a7af46a46eb
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 16:43:37 2014 -0600

        initial commit

Note that master is lagging behind the fix-readme branch. It doesn't have the commit with the README changes. We will fix that by merging our fix-me branch into the master branch.

    $: git merge fix-readme
    Updating 44a560f..57aff88
    Fast-forward
     README.md   | 4 ++--
     project.clj | 2 +-
     2 files changed, 3 insertions(+), 3 deletions(-)

The merge brought in the changes. If we check the log, we'll see two commits now. The initial commit and our "readme.md" changes.

    $: git log
    commit 57aff889c81698394faf8568b63f14130d32599a
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 17:33:41 2014 -0600

        fixing README.md description

    commit 44a560f1653770afac01aea2c9279a7af46a46eb
    Author: crkoehnen <crkoehnen@gmail.com>
    Date:   Sun Dec 28 16:43:37 2014 -0600

        initial commit

Deleting the fix-readme branch

Now that we've pulled the changes from the fix-readme branch into master, we no longer need the fix-readme branch, so let's delete it.

    $: git branch -d fix-readme

Pushing to GitHub

The final step will be to push our changes to GitHub. This allows other people to view our code changes. Enter the following (you will use your own username and password when prompted):

    $: git push origin master
    Username for 'https://github.com': crkoehnen
    Password for 'https://crkoehnen@github.com':
    Counting objects: 4, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (4/4), done.
    Writing objects: 100% (4/4), 572 bytes | 0 bytes/s, done.
    Total 4 (delta 2), reused 0 (delta 0)
    To https://github.com/crkoehnen/chatter.git
       44a560f..57aff88  master -> master

Refresh GitHub in your browser and you will see the text and the commit count change in your repository page. Your changes are now available for collaborators to review!

That's all the command line git we will need for this tutorial. GitHub maintains a list of resources where you can learn more about git and GitHub.

In Chapter 5, we'll make more to the code.

Good Resources for Learning Git and GitHub.

Chapter 5: More Code Changes

Now let's change the app's main page from "Hello, World" to something a little more chatty.

First, let's create and checkout a new branch called, view-messages. If you need a refresher on creating and checking out branches, review Chapter 4. Once you are on the new view-messages branch, we'll move on to updating the code.

Adding Hiccup

We need to write code that will generate HTML. To do this, we will use a library called hiccup. We don't have this library yet, so we're going to add it. Adding a new library requires two steps:

1) Add the library to the dependency section of the project.clj file. This tells lein your program needs another program.

Add hiccup by updating the project.clj file to look like this:

  :dependencies [[org.clojure/clojure "1.9.0"]
                 [compojure "1.6.1"]
                 [ring/ring-defaults "0.3.2"]
                 [hiccup "1.0.5"]]

2) Import the library into the namespace you will use it in by adding the import to the ns declaration. Our ns declaration will be part of the handler.clj file:

(ns chatter.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [hiccup.page :as page]))

Let's use hiccup to generate the html by changing app-routes:

(defroutes app-routes
  (GET "/" []
       (page/html5
        [:head
         [:title "chatter"]]
        [:body
         [:h1 "Our Chat App"]]))
  (route/not-found "Not Found"))

Once the code is updated, let's try it out. In the command line, start the server:

$: lein ring server

Now http://localhost:3000 displays "Our Chat App". Right-click and select View Page Source to see it's now proper HTML complete with head, title, and body.

The hiccup function(*) page/html5 generates an HTML page. It expects Clojure vectors with symbols representing corresponding HTML tags. Hiccup will automatically add the closing tag when it reaches the end of the vector.

Compare the hiccup to HTML in View Page Source to the HTML we wrote by hand earlier.

How Does Hiccup Work?

Vectors are a Clojure data structure used to contain sequences of things, including other vectors. Vectors are often written using square brackets. For example, [1 2 3] is a vector containing the numbers 1, 2, and 3. Hiccup uses vectors of keywords to represent sections of HTML.

Keywords are names that begin with a colon. :title, :x, and :favorite-color are all keywords. Clojure often uses keywords where other languages use strings. If you were to use Clojure to query a database, Clojure would probably use keywords to represent the column names. Hiccup uses keywords to represent the names of HTML elements.

Where HTML uses <body>, hiccup would expect :body. Where HTML uses <title>, hiccup uses :title. Because the keywords are enclosed in a vector, the closing of the HTML tag is unnecessary. The closing of the surrounding vector signals where the HTML section ends.

A problem with our new app-routes is that it has two different functions right now. Its main role is to take the incoming request and decide what to do. Right now it's doing that, but it is also generating a full HTML page. As we add more pages, this will become too complicated to manage. We'll get ahead of the game by splitting out the task of generating the HTML into a helper function.

Clojure defines a function using this syntax:

(defn name
  doc-string?
  params-vector
  expression)
  1. defn - introduces the defn expression.
  2. name - what you call the function.
  3. doc-string? - an optional description of the function.
  4. params-vector - a vector of symbols naming the functions arguments.
  5. expression - the body of the function.

hello-world, a traditional first function, might be programmed in Clojure like:

(defn hello-world
  "ye olde 'Hello, World'"
  []
  "Hello, World")

hello-world takes no arguments and returns the string "Hello, World".

user> (hello-world) "Hello, World"

Our new code should look like:

(defn generate-message-view
  "This generates the HTML for displaying messages"
  []
  (page/html5
   [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]]))
(defroutes app-routes
  (GET "/" [] (generate-message-view))
  (route/not-found "Not Found"))

generate-message-view is a function that takes no arguments. It calls a hiccup function page/html5 to generate html from a vector representing the head sections and a vector representing the body elements of the html.

Save handler.clj, and refresh the browser to make sure our page still works. From the outside, we shouldn't see a change. The page should still display "Our Chat App" and the html should be identical. Now, let's double check our git status:

    $: git status
    On branch view-messages
    Changes not staged for commit:
       (use "git add <file>..." to update what will be committed)
       (use "git checkout -- <file>..." to discard changes in working directory)

          modified:   project.clj
          modified:   src/chatter/core/handler.clj

    no changes added to commit (use "git add" and/or "git commit -a")

That looks right so add, commit, merge the changes back to master, and then push to GitHub. Then, delete the view-messages branch. You should see the commit numbers go up on GitHub.

Adding Messages

Our app is not displaying messages, nor do we have a way of adding messages. Let's make that happen now.

Create and check out a branch to work on.

Let's change the app so it displays messages. We'll represent the messages as a vector of maps. Each map will have a :name and :messagekey and the corresponding value, in quotes. For example, the code below will represent blue's first post.

{:name "blue" :message "blue's first post"}

This is a map with two keys.

  1. `:name` is "blue", because blue posted it
  2. `:message` is the content of the post and its value is "blue's first post".

Programs often need to associate keys with values and the usual data structure for doing that are hash tables. Clojure calls them maps and they look like this:

(def cities
   {"Tokyo" 37900000
    "Delhi" 26580000
    "Seoul" 26100000})

Here cities is a hash table whose keys are strings (in this case the names of cities) and the values are the populations of each city.

To get a value from a map, pass the map and key into the get function. For example,

(get cities "Tokyo")

returns 37900000. When the keys are keywords, you can also use the keyword as a function that takes the map and returns the values.

(:name {:name "blue" :message "blue's first post"})

returns "blue".

(:message {:name "blue" :message "blue's first post"})

returns "blue's first post".

Maps are everywhere in Clojure and are used for many things where other languages might use objects.

Let's call the vector simply chat-messages and hard code(*) some samples to get started. Add a chat-messages variable to handler.clj.

After the ns expression, add:

(def chat-messages [{:name "blue" :message "hello, world"}
                    {:name "red" :message "red is my favorite color"}
                    {:name "green" :message "green makes it go faster"}])

Next, we'll modify the HTML to display the messages. We will also add a parameter to the generate-message-view function so that we can give it a messages we want displayed.

(defn generate-message-view
  "This generates the HTML for displaying messages"
  [messages]
  (page/html5
   [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]
    [:p messages]]))
(defroutes app-routes
  (GET "/" [] (generate-message-view chat-messages))
  (route/not-found "Not Found"))

Save handler.clj and refresh the browser.

This blows up spectacularly.

illegal-argument-exception

This is a stack trace - it gives us an idea what the program was doing when it hit the problem. Ignore all the files that aren't ones you wrote for the project. In my case, the first file of interest is handler.clj, line 14, the generate-message-view function.

The exception message on the top, "... is not a valid element name", is a clue to what's wrong. Elements are what fragments of html are called. Hiccup is responsible for generating html from Clojure symbols. The problem is that we've got a map with symbols in it and hiccup thinks they're html. They're not, so it creates an error.

We can fix the issue by converting our maps to strings.

(defn generate-message-view
  "This generates the HTML for displaying messages"
  [messages]
  (page/html5
   [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]
    [:p (str messages)]]))

Save handler.clj, and refresh the browser.

This fixes the exception but it's ugly.

ugly hack

Let's take the messages and put them in a table using HTML's table, tr, and td elements. We're going to write a function that takes a message and creates an HTML row. Then, inside a table, we're going to apply that function to all of our messages.

Clojure uses defn to create a function, but those functions are named. Sometimes, we want a specialized function that isn't reusable. For those cases, Clojure has a way of creating an anonymous function.

(fn params-vector expression)

Our function is going to take a message, we'll call it "m" within the function, and extract both the :name and :message, wrapping them in :td to make table cells and putting them both within a :tr to make the row. Since the keys to the message hashmap are keywords, we can use them as functions to get the values. In Clojure, the function looks like:

(fn [m] [:tr [:td (:name m)] [:td (:message m)]])

Making a new collection by applying a function to all of the elements of an existing collection is such a common thing that Clojure has it functionality predefined. It's a function called map. This is different than "mapping" (the function) over a collection of maps (hash tables), which is what we are doing.

The syntax is:

(map fn coll)

map - signifies that we're going to be invoking the map function.

fn - the function we're going to apply to every element.

coll - the collection containing the elements.

Mapping our anonymous function over our vector of messages looks like:

(map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)

Now our generate-message-view looks like:

(defn generate-message-view
  "This generates the HTML for displaying messages"
  [messages]
  (page/html5
     [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]
    [:p
     [:table
      (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]]]))

Save handler.clj, then refresh the browser. Our hard-coded messages should now display in the page.

hard coded messages

Forms

We still don't have a way of adding new messages. This requires HTML forms and importing the form functions from hiccup. The form allows the user to send messages in the parameters of an HTML POST. We will need to extract the message and add it to our collection of messages. This will be the most complicated set of changes in our app.

In HTML, a form is used to send input from the browser to the server. The form element contains a pair of attributes.

action - which specifies the route that should handle the input.

method - which specifies the type of request.

Up until now, we've only used GET to show the messages. To send messages, we'll need to add a POST.

forms contain text and input elements. The input elements define the content the form will send to the server. input elements have a number of attributes:

id - a way of identifying the input

name - the name of the input

type - the kind of input

value - the default value for the input

We're going to use hiccup to generate html that looks like,

<form action="/" method="POST">
  Name: <input id="name" name="name" type="text">
  Message: <input id="msg" name="msg" type="text">
  <input type="submit" value="Submit">
</form>

First, we need to import some libraries into our handler.clj file. Add:

[hiccup.form :as form]

to the :require section of the ns declaration. It should now look like:

(ns chatter.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [hiccup.page :as page]
            [hiccup.form :as form]))

Now that we have imported the hiccup form function, we can use it to generate the HTML form.

(form/form-to
 [:post "/"]
 "Name: " (form/text-field "name")
 "Message: " (form/text-field "msg")
 (form/submit-button "Submit"))

form/form-to is a hiccup function for generating the form.

[:post "/"] is a vector with the keyword :post and the string "/". This tells hiccup to make the method a POST to the / location.

"Name: " is a string that will be the text displayed before the input field.

form/text-field is a hiccup function for generating an input field of type "text". We're passing in the string "name".

"Message: " is a string that will be the text displayed before the input field.

form/submit-button is a hiccup function for generating the submit button.

We want to generate the form button below the title but above the list of messages in the generate-message-view function.

Finally, we're going to change our definition of the app

from:

(def app
  (wrap-defaults app-routes site-defaults))

to:

(def app app-routes)

This is a simplification for the tutorial.

Now our code looks like:

(ns chatter.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [hiccup.page :as page]
            [hiccup.form :as form]))

(def chat-messages [{:name "blue" :message "blue's first post"}
                    {:name "red" :message "red is my favorite color"}
                    {:name "green" :message "green makes it go faster"}])

(defn generate-message-view
  "This generates the HTML for displaying messages"
  [messages]
  (page/html5
   [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]
    [:p
     (form/form-to
      [:post "/"]
      "Name: " (form/text-field "name")
      "Message: " (form/text-field "msg")
      (form/submit-button "Submit"))]
    [:p
     [:table
      (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]]]))

(defroutes app-routes
  (GET "/" [] (generate-message-view chat-messages))
  (POST "/" [] (generate-message-view chat-messages))
  (route/not-found "Not Found"))

(def app app-routes)

Save handler.clj and refresh the browser. We should now have a form on the page where a user could submit a new message.

unwired form

Wiring the form

We see the form now, but submitting it does nothing. The problem now is that we're extracting the params during the POST but aren't actually doing anything with them. To fix this, we have to extract the parameters from the form, build a message, and store the message in our messages vector. This might be the hardest part of our app.

First, we need to import a library to extract the information sent by the form. Add the following to the :require section,

[ring.middleware.params :refer [wrap-params]]

Next, change the app definition from:

(def app app-routes)

to:

(def app (wrap-params app-routes))

This enables us to have access to the information sent back in our form.

We want to be able to add new messages to our messages vector. Clojure was designed from the ground up to make it easier to write concurrent programs. Concurrent programs are programs that do more than one thing at a time. It does that by having data structures that do not change. Variables can be changed to point to something else, but Clojure requires that doing so happens using particular functions, so it can ensure the program stays in a safe state. We're going to use the atom mechanism to allow us to update our messages.

An atom is like a box that protects information from being changed in an unsafe way. You simply pass the information into the atom.

Instead of having the chat-messages variable point to our vector of messages, we're going to have it point to the atom protecting the vector.

Instead of:

(def chat-messages [{:name "blue" :message "blue's first post"}
                    {:name "red" :message "red is my favorite color"}
                    {:name "green" :message "green makes it go faster"}])

We'll use:

(def chat-messages
     (atom [{:name "blue" :message "blue's first post"}
            {:name "red" :message "red is my favorite color"}
            {:name "green" :message "green makes it go faster"}]))

Now chat-messages is pointing to the atom protecting our vector of hashes.

Because chat-messages is pointing to the atom, we can't simply map over it in generate-message-view. Now, we have to tell Clojure that we want to generate HTML for the contents of the atom. This allows Clojure to ensure the messages are always read in a consistent state, even though something could be modifying them.

Reading what's stored in an atom is called "dereferencing" and is represented by the @ character.

We will dereference the chat-messages atom just before it is passed to the generate-message-view function. We can do this by changing our routes from:

(defroutes app-routes
  (GET "/" [] (generate-message-view chat-messages))
  (POST "/" [] (generate-message-view chat-messages))
  (route/not-found "Not Found"))

to:

(defroutes app-routes
  (GET "/" [] (generate-message-view @chat-messages))
  (POST "/" [] (generate-message-view @chat-messages))
  (route/not-found "Not Found"))

If you save handler.clj and refresh the browser, the hard coded examples should display as before. We still won't see any new messages because we still need to extract the information from the form and modify chat-messages.

To add messages to chat-messages, we will need to introduce two more functions: conj and swap!.

conj

There are many ways to work with collections of values in Clojure. One commonly used function is conj. The name is short for "conjoin". This function takes a collection and one or more item(s) to add to the collection. It then returns a new collection without modifying the original collection.

(conj [:one :two] :three)
=> [:one :two :three]

(conj [:one :two :three] :four :five)
=> [:one :two :three :four :five]

swap!

To modify an atom, Clojure provides swap!.

(swap! atom update-function arguments...)

atom - the atom to be updated. update-function - the function that is applied to the value protected by the atom. It returns a new value which will replace the original.

arguments... - zero or more arguments to be passed to the update-function.

The swap! function will:

  1. Dereference the atom.
  2. Pass this dereferenced value to the update-function along with any additional arguments. You can think of it like this: (update-function @atom arguments...)
  3. Safely replace the inner content of the atom with the value returned from the update-function, and finally...
  4. Return the new content of the atom.
(def a-number (atom 1))

@a-number
=> 1
(swap! a-number + 2)
=> 3
@a-number
=> 3

In our case, we're going to "swap" the content of chat-messages by "conjing" a new message onto the vector of messages.

We'll also put it in a helper function to make it easier to maintain.

(defn update-messages!
  "This will update a message list atom"
  [messages name new-message]
  (swap! messages conj {:name name :message new-message}))

Now, we have to modify our app-routes. We have to make two changes; it needs to extract the form information when somebody POSTs a new message, and it needs to add the new message to our chat-messages before returning the page to the user. Both of these changes need to happen in the POST route.

The new app-routes looks like

(defroutes app-routes
  (GET "/" [] (generate-message-view @chat-messages))
  (POST "/" {params :params} (generate-message-view
                               (update-messages! chat-messages
(get params "name") (get params "msg"))
                               ))
  (route/not-found "Not Found"))
  1. {params :params} is a shorthand notation that tells Clojure to extract all of the data submitted from the HTML form and call that data params.
  2. (get params "name") and (get params "msg") extract the values of the "name" and "msg" fields from the form data.
  3. The update-messages! function is then called with the chat-messages atom and the values of the "name" and "msg" fields from the form.
  4. After update-messages! has added the new message to the inner content of chat-messages it returns the new, dereferenced, vector of messages held by the atom.
  5. generate-message-view is called with the updated collection of messages and builds the HTML response for the user.

Another way of writing this, which may make the intent more clear, is to name some of the intermediate values using let. This will allow us to temporarily provide names for the results of some of the expressions.

(defroutes app-routes
  (GET "/" [] (generate-message-view @chat-messages))
  (POST "/" {params :params}
    (let [name-param (get params "name")
          msg-param (get params "msg")
          new-messages (update-messages! chat-messages name-param msg-param)]
      (generate-message-view new-messages)
      ))
  (route/not-found "Not Found"))
  1. Extract the "name" field from the form data in params and name it name-param.
  2. Extract the "msg" field from the form data in params and name it msg-param.
  3. Execute the update-messages! function for the chat-messages atom and the values of the previously established name-param and msg-param names.
  4. Assign the name new-messages to the result of update-messages!.
  5. Execute generate-message-view for the new collection of messages now called new-messages.
  6. Return the HTML produced by generate-message-view and forget about the names name-param, msg-param, and new-messages.

let

let expressions are used to temporarily associate names with the results of other expressions, similar to how a function assigns names to its arguments. These named values can also be re-used without the cost of re-evaluating the expression that generated them.

(let [name-one expression-one] name-two expression-two] (some-function name-one name-two))

  1. name-one - a name for the result of evaluating expression-one.
  2. name-two - a name for the result of evaluating expression-two.
  3. Call some-function and pass it the values assigned to name-one and name-two.
  4. Return the result of the last expression within the let, and forget about the names we had created.
(let [two   2
      three (+ two 1)]
  (* two three))
=> 6

Save the handler.clj file, we will be able to use the form to add messages to the page.

Since we can add messages through the form, we can remove our hard-coded messages. Change the messages to an empty vector.

(def chat-messages (atom []))

Now, the app is taking our new messages, but it's adding new messages to the end. That's going to be hard to read. We can fix that by changing from a vector to a list.

(def chat-messages (atom '()))

Like vectors, lists are sequential collections. Vectors are better for accessing random elements fast (which we aren't doing). Lists are better at adding an element to the front, which we want to do. Since they are both collections, conj works with either.

Our app now looks like:

(ns chatter.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [ring.middleware.params :refer [wrap-params]]
            [hiccup.page :as page]
            [hiccup.form :as form]))

(def chat-messages (atom '()))

(defn generate-message-view
  "This generates the HTML for displaying messages"
  [messages]
  (page/html5
   [:head
    [:title "chatter"]]
   [:body
    [:h1 "Our Chat App"]
    [:p
     (form/form-to
      [:post "/"]
      "Name: " (form/text-field "name")
      "Message: " (form/text-field "msg")
      (form/submit-button "Submit"))]
    [:p
     [:table
      (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]]]))

(defn update-messages!
  "This will update a message list atom"
  [messages name message]
  (swap! messages conj  {:name name :message message}))

(defroutes app-routes
  (GET "/" [] (generate-message-view @chat-messages))
  (POST "/" {params :params}
    (let [name-param (get params "name")
          msg-param (get params "msg")
          new-messages (update-messages! chat-messages name-param msg-param)]
      (generate-message-view new-messages)
      ))
  (route/not-found "Not Found"))

(def app (wrap-params app-routes))

Add, commit, merge the changes to master, push master to GitHub, and delete the branch.

Bootstrap

The app is working but is ugly. We can improve it by using CSS and JavaScript from a package of software called Twitter Bootstrap.

Create and checkout a new branch.

In the head section of our HTML, we're going to include Bootstrap:

   [:head
    [:title "chatter"]
    (page/include-css "//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css")
    (page/include-js  "//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js")
    (page/include-js  "//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js")]

Start the server again, and you will see the fonts change. You'll also notice the table get smashed together.

Now, let's change the table element from :table to :table#messages.table.

    [:table#messages.table
     (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]

This tells hiccup that we want the table to have an id of messages and a class of table. CSS works by looking for combinations of classes and structure and changing the appearance when an element matches a pattern. Bootstrap uses a set of predefined CSS to look for a common set of classes. One of them is table.

Save the file, then refresh the browser. It now looks better. Examine the HTML that's generated. You see an id and class field inside the table element.

Let's make the table entries stripped by adding an additional class. Change the table element to :table#messages.table.table-striped.

    [:table#messages.table.table-striped
     (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]

What do you think will happen if you change table-striped to table-bordered? Try it and refresh your browser to find out!

HTML elements can have multiple classes and CSS uses this to create more complex effects. Try adding table-hover to the table element: :table#messages.table.table-bordered.table-hover.

    [:table#messages.table.table-hover
     (map (fn [m] [:tr [:td (:name m)] [:td (:message m)]]) messages)]

Now, when you move the mouse over a row, the entire row becomes highlighted. Dynamic effects in the browser are implemented using a language called JavaScript. We won't talk about JavaScript except to say that it exists and the JavaScript part of Bootstrap was imported into the page with the include-js call.

Let's create our own CSS file to center the heading. Create a file chatter.css in the resources/public directory. Inside, paste:

h1 {
    text-align: center;
}

CSS works using pattern matching. In this case, we're saying that if the element is an h1 element, center the text. Save the file and add another page/include-css expression to handler.clj to pull in chatter.css.

   [:head
    [:title "chatter"]
    (page/include-css "//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css")
    (page/include-js  "//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js")
    (page/include-js  "//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js")
    (page/include-css "/chatter.css")]

Refresh the page. We want to see the h1 tag centered, but you'll see it's not. Right click in the browser and Inspect. Go to the network tab in the inspection window and watch the traffic as you refresh the page. We're getting a 404 when it's trying to download the css.

The problem is in our defroutes. We have a route handling browser GET or POST requests, but anything else is falling through to our route/not-found call. We need to tell defroutes where to find our resources. Change the defroutes to:

(defroutes app-routes
  (GET "/" [] (generate-message-view @chat-messages))
  (POST "/" {params :params}
    (let [name-param (get params "name")
          msg-param (get params "msg")
          new-messages (update-messages! chat-messages name-param msg-param)]
      (generate-message-view new-messages)
      ))
  (route/resources "/")
  (route/not-found "Not Found"))

In Chapter 6, we will complete the project by pushing your Chatter app live so you can share with your friends!

Chapter 6: Push to Live

Heroku

Up to this point, we've been running the server on our computer, localhost(*). This works great while writing or developing the program because it makes testing changes fast and easy. Eventually we'll want to put it on the internet for others to see and use.

To put it on the internet, we're going to use a company called Heroku for hosting(*). They're going to run our program on a machine visible to anybody on the Internet. We will use use git to send them our program.

The advantage of using a company like Heroku is that they handle the work of actually maintaining a server. One of the disadvantages is that Heroku can be expensive, but for a little program like ours it's free.

First, we need to make a couple of changes to our app so it can interact with Heroku.

In handler.clj, we will add a couple of functions to print log messages when Heroku starts and stops the program.

(defn init []
  (println "chatter is starting"))


(defn destroy []
  (println "chatter is shutting down"))

Then we need to add a main function. This is what Heroku will actually invoke(*) to start our program. Add this to the end of handler.clj.

(defn -main [& [port]]
  (let [port (Integer. (or port (env :port) 5000))]
    (jetty/run-jetty #'app {:port port :join? false})))

Finally, we need to change our project.clj so it knows how to prepare our application for Heroku.

Our new project.clj should look like:

(defproject chatter "0.1.0-SNAPSHOT"
  :description "clojure web app for displaying messages"
  :url "http://example.com/FIXME"
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.9.0"]
                 [compojure "1.6.1"]
                 [ring/ring-defaults "0.3.2"]
                 [ring/ring-jetty-adapter "1.7.0"]
                 [hiccup "1.0.5"]
                 [hickory "0.7.1"]
                 [environ "1.0.2"]]
  :plugins [[lein-ring "0.9.7"]
            [lein-environ "1.0.0"]]
  :ring {:handler chatter.handler/app
         :init chatter.handler/init
         :destroy chatter.handler/destroy}
  :aot :all
  :main chatter.handler
  :profiles
  {:dev
   {:dependencies [[javax.servlet/servlet-api "2.5"]
                   [ring/ring-mock "0.3.2"]]}
   :production
   {:ring
    {:open-browser? false, :stacktraces? false, :auto-reload? false}
    :env {production true}}}
  :uberjar-name "chatter-standalone.jar")

The ns of handler.clj should look like:

(ns chatter.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
            [ring.middleware.params :refer [wrap-params]]
            [ring.adapter.jetty :as jetty]
            [hiccup.page :as page]
            [hiccup.form :as form]
            [ring.util.anti-forgery :as anti-forgery]
            [environ.core :refer [env]]))

We will still be able to start the app using lein ring server but now we can also start by entering lein run -m chatter.handler in the command line. When we start it this way, it defaults to port 5000, and you should see that in the browser. Port 3000 does not work anymore, but you see the app if you switch to 5000.

If we create a jar with lein uberjar, we can also start the app with java $JVM_OPTS -cp target/chatter-standalone.jar clojure.main -m chatter.handler

These new methods of starting the app are closer to what Heroku will use to start the app.

We also need a Procfile in the root directory of our project (next to project.clj) containing the line web: java $JVM_OPTS -cp target/chatter-standalone.jar clojure.main -m chatter.handler

Stop the server using control-c, then restart with the command:

lein with-profile trampoline run

If the local web page still works; add and commit your changes, then merge your branch back into master. Push the master branch to github.

Now we'll deploy to heroku with the following commands: * heroku create * git push heroku master * heroku ps:scale web=1 * heroku open

The final command will open the browser and point it at your app on Heroku.

Try the traceroute command again against the address Heroku assigned your app:

    $: traceroute obscure-brushlands-9918.herokuapp.com
    traceroute to obscure-brushlands-9918.herokuapp.com (50.16.239.160), 30 hops max, 60 byte packets
    1  192.168.1.1 (192.168.1.1)  15.820 ms  15.797 ms  15.773 ms
    2  96.120.49.33 (96.120.49.33)  23.895 ms  25.196 ms  25.192 ms
    3  te-0-2-0-6-sur01.webster.mn.minn.comcast.net (68.85.167.145)  25.179 ms  25.168 ms  25.362 ms
    4  te-0-7-0-13-ar01.crosstown.mn.minn.comcast.net (68.87.174.189)  30.743 ms  31.979 ms te-0-7-0-12-ar01.crosstown.mn.minn.comcast.net (69.139.219.129)  31.979 ms
    5  pos-0-0-0-0-ar01.roseville.mn.minn.comcast.net (68.87.174.194)  31.967 ms * *
    6  he-1-12-0-0-cr01.350ecermak.il.ibone.comcast.net (68.86.94.77)  39.269 ms  25.568 ms  26.422 ms
    7  be-10206-cr01.newyork.ny.ibone.comcast.net (68.86.86.225)  45.276 ms  63.727 ms  66.090 ms
    8  he-0-11-0-0-pe03.111eighthave.ny.ibone.comcast.net (68.86.83.98)  58.386 ms  58.374 ms  58.358 ms
    9  as16509-3-c.111eighthave.ny.ibone.comcast.net (50.242.148.118)  56.143 ms  58.280 ms  65.988 ms
    10  54.240.229.76 (54.240.229.76)  65.977 ms 54.240.229.82 (54.240.229.82)  65.982 ms *
    11  54.240.228.190 (54.240.228.190)  65.874 ms 54.240.228.202 (54.240.228.202)  65.888 ms 54.240.228.196 (54.240.228.196)  65.969 ms
    12  54.240.229.223 (54.240.229.223)  42.112 ms 54.240.229.200 (54.240.229.200)  45.620 ms 54.240.229.221 (54.240.229.221)  47.673 ms
    13  54.240.228.173 (54.240.228.173)  47.630 ms 54.240.228.177 (54.240.228.177)  52.382 ms 54.240.228.173 (54.240.228.173)  45.498 ms
    14  72.21.220.100 (72.21.220.100)  52.589 ms 72.21.220.116 (72.21.220.116)  52.527 ms 54.240.228.139 (54.240.228.139)  50.825 ms
    15  72.21.220.135 (72.21.220.135)  56.875 ms 72.21.220.167 (72.21.220.167)  59.011 ms 72.21.220.151 (72.21.220.151)  59.014 ms
    16  72.21.220.108 (72.21.220.108)  48.813 ms 205.251.245.242 (205.251.245.242)  49.425 ms  48.614 ms
    17  * * *
    18  * * *
    19  * * *
    20  216.182.224.85 (216.182.224.85)  55.704 ms 216.182.224.223 (216.182.224.223)  66.895 ms 216.182.224.227 (216.182.224.227)  54.629 ms
    21  * * *
    22  * * *
    23  * * *
    24  * * *
    25  * * *
    26  * * *
    27  * * *
    28  * * *
    29  * * *
    30  * * *

Try going to each other's app.

To delete the app from Heroku, select the app in the dash board, click settings, delete app is on the bottom. Then,git remote remove heroku in the command line.