Elm Drip 008.2: Project Setup

Until now, we've just thrown Elm files into a directory, compiled them or run them in the REPL, and dealt with what we were given. However, we'd really like to be able to introduce tests at some point, and keep our main source files separate, and also wouldn't it be nice if our built artifacts weren't in the same directory? We'll fix all this. Let's get started.

Project

We'll be starting another quick project, this time an implementation of a game.

mkdir frogger
cd frogger
git init .

Now historically our next step was to just plop open a Main.elm file and start working, but that leads to a very unorganized mess if you end up with a lot of files. It also leads to the problem I see often (and am guilty of just as often) where Elm projects include the compiled elm.js file in their code repository, which is really a build artifact and has no business in source control.

We know we don't want elm's package manager's artifacts in our repo, so let's gitignore those:

echo "elm-stuff" >> .gitignore

We're going to go ahead and install elm's html package, which will generate an elm-package.json for us.

elm package install elm-lang/html

Let's look at the elm-package.json file:

vim elm-package.json
{
    "version": "1.0.0",
    "summary": "helpful summary of your project, less than 80 characters",
    "repository": "https://github.com/user/project.git",
    "license": "BSD3",
    "source-directories": [
        "."
    ],
    "exposed-modules": [],
    "dependencies": {
        "elm-lang/core": "4.0.1 <= v < 5.0.0"
    },
    "elm-version": "0.17.0 <= v < 0.18.0"
}

It's worth filling this out for your projects just on principle, but for now we're focused on the "source-directories" key. This says "sources live in the current directory" which is not what we want - we want a src directory. So we'll update this:

    "source-directories": [
        "src"
    ],

Next, let's make the src directory:

mkdir src
# And make a shell for our application
vim src/Frogger.elm
module Frogger exposing (..)


import Html.App as App
import Html exposing (..)


type Msg
  = NoOp


type alias Model = Int


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    NoOp ->
      (model, Cmd.none)


view : Model -> Html Msg
view model =
  div [] [ text "Hello Frogger" ]


main : Program Never
main =
  App.program
    { init = (0, Cmd.none)
    , update = update
    , view = view
    , subscriptions = subscriptions
    }


subscriptions : Sub Msg
subscriptions model =
  Sub.none

Here's our starter application with just some basic text on the screen.

Obviously, if we save that we can open it in the elm-reactor whenever we want to test it out. Let's go ahead and make an HTML file for it though:

vim index.html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Frogger</title>
    <script type="text/javascript" src="target/elm.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      var app = Elm.Frogger.fullscreen();
    </script>
  </body>
</html>

Alright, so this will look for a file in a target directory. This is where we'll be putting our build output. Since we don't want build output in our source control, let's add it to .gitignore:

mkdir target
echo "target" >> .gitignore
# We'd also like to avoid any artifacts that elm-reactor creates from going into
# source control
echo "elm.js" >> .gitignore

Now to build our file there, we can just use elm-make:

elm make --output target/elm.js src/Frogger.elm

So that works, but no one wants to type that constantly. We'll make a build script:

echo "elm make --output target/elm.js src/Frogger.elm" >> build
chmod u+x build

Now we have a nice script that encapsulates that process:

./build

We can just pull up our index.html in a browser now to make sure it works. We'll serve it up, in my case with my servedir alias, but there are a ton of ways to easily serve a directory. Jessica Kerr mentions the node package http-server if you don't already have your preferred means of doing this.

Alright, visit the HTTP server...and our app is being served. OK, so is that it? No, our goal here is to get to a good project. A project without a README is just cruel. We'll add one:

vim README.md

Frogger

Frogger is an elm-based implementation of the old standby Frogger game. To build it, you need the following dependencies installed:

  • Elm, version 0.17 or greater.

You can build it with:

./build

This will generate a target/elm.js file. Once that's been generated, you can visit the index.html file, serving it with any web server, and play the game.

Development

To run the tests, just run:

./test # NOTE: Not yet implemented sorry!

To publish to github pages:

./publish

GitHub Pages

GitHub provides a nice way to serve up static files. This makes it very convenient for hosting your built Elm applications to show off.

We can do this with a second script:

vim publish
#!/usr/bin/env bash

set -e

git checkout -b gh-pages
./build
git add -f target/
git commit -m"Update with latest code."
git push -f origin gh-pages
git checkout master
git branch -D gh-pages
chmod u+x publish

Now you can publish your Elm project trivially:

hub create # Creating my project on GitHub
# Note that the above `hub` command is provided by https://hub.github.com/
git push origin master # Updating my source code
./publish # Pushing to GitHub Pages

And we can view it on github:

http://knewter.github.io/frogger/

Summary

So that's a pretty good setup for a general project, that has a bit more to it than just "throw some Elm files in there". It also provides a chance for us to add tests outside of the source files. I know it didn't include a ton of new Elm-specific learning, but it's nice to spend some time thinking about how you structure your projects, and it gives us a baseline for adding some tooling. See you soon!

Resources