Sunday, November 22, 2015

Getting off the ground in Elm: project setup

*Update*: Deprecated. These days, to create a new Elm project, I use rug, as described in this post: http://blog.jessitron.com/2016/12/using-rug-with-elm.html


If you have tried Elm and want to make a client bigger than a sample app, this post can help you get set up. Here, find what goes into each of my Elm repositories and why. This template creates a fullscreen Elm application with the potential for server calls and interactivity. This post is up-to-date as of Elm 0.16.0 on 11/21/2015, on a Mac.
TL;DR: Clone my sample repo; Change CarrotPotato to your project name everywhere it appears (elm-package.json; src/CarrotPotato.elm; index.html). Replace origin with your remote repository.

Step 0: Install Elm (once)

Run the latest installer.
To check that this worked, run `elm` at a terminal prompt. You should see a long usage message, starting with `Elm Platform 0.16.0 - a way to run all Elm tools`

Bonus: getting Elm text highlighting in your favorite text editor is a good idea. That's outside the scope of this post, because it was hard. I use Sublime 2 and this Elm plugin.

Step 1: Establish version control (every project)

Step 1A: create a directory and a repository. 

Make a directory on your computer and initialize a git repository inside it.
mkdir CarrotPotato
cd CarrotPotato
git init

 Step 1B: configure version control

In every project, I use the first commit to establish which files do not belong under version control.
I'm going to have the Elm compiler write its output to a directory called target. I want to save the source code I write, not stuff that's generated from it, so git should not save the compiler output. Git ignores any files or directories whose names are in a file called .gitignore, so I put target in there.
The Elm package manager uses a directory called elm-stuff for its work. That doesn't belong in our repository, so put it in .gitignore too. I recommend making .gitignore the first file committed in any new repository.
echo "target" >> .gitignore
echo "
elm-stuff" >> .gitignore
git add .gitignore
git commit -m "New Elm project"

Step 2: Bring in core dependencies

The Elm package manager will install everything you need, including the core language, including the configuration it needs. To bring in any dependency, use `elm package install <dependency>`, where <dependency> is specified as github-user/repo-name. Most of the packages come from github users elm-lang or evancz (Evan Czaplicki is the author of Elm). All the packages that elm-package knows about are listed on package.elm-lang.org.

In keeping with the Elm Architecture, I use StartApp as the basis for all my projects. Bring it in:
elm package install evancz/start-app
elm-package is very polite: it looks at your project, decides what it needs to do, and  asks nicely for permission before doing anything. It will add the dependency to elm-package.json (creating the file if it doesn't exist), then install the package you requested (along with anything that package depends on) in a directory called elm-stuff.

Here's a gotcha: the StartApp install downloads its dependencies, but you can't use them directly until they are declared as a direct dependency of your project. And you can't actually use StartApp without also using Effects and Html. So install them too:
elm package install evancz/elm-html
elm package install evancz/elm-effects
Note: This step won't work without internet access. Elm's package manager doesn't cache things locally; everything is copied into elm-stuff within each project. On the upside, you can dig around in elm-stuff to look at the code (and embedded documentation) of any of your project's dependencies.

Step 3: Improve project configuration

3A: Welcome to elm-package.json

You now have an elm-package.json file in your project directory. Open it in your text editor.
{
    "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": "3.0.0 <= v < 4.0.0",
        "evancz/start-app": "2.0.2 <= v < 3.0.0"
    },
    "elm-version": "0.16.0 <= v < 0.17.0"
}
The project version, summary, etc. become crucial when you publish a new library to the central Elm package list. Until then, you can update them if you feel like it.

Note: the project's dependencies are specified as ranges. Elm is super specific about semantic versioning. It is impossible for one of the libraries you use to introduce a compilation-breaking change without going up a major version (the first section in the version number), so Elm knows that (for instance) any version of StartApp that's at least as high as its current one "2.0.2" and less than the next major version "3.0.0" is acceptable. This matters if you publish your project as a library for other people to use. For now it's just cool.

3B: Establish a source directory

With the default configurtion, Elm looks for project sources in "." (the current directory; project root). I want to put them in their own directory, so I change the entry in "source-directories" to "src". Then I create a directory called `src` in my project root.
mkdir src
[editor] elm-package.json
and set:
"source-directories": [
       
"src"
    ],

Step 4: Create the main module

4A: Bring in "hello world" code

Create a file src/CarrotPotato.elm (if the name of your project is CarrotPotato), and open it in your text editor.
touch src/CarrotPotato.elm
[editor] 
src/CarrotPotato.elm
Every StartApp application starts about the same. I cut and paste most of this out of the StartApp docs, then added everything necessary to make it compile. It had to do something, so it outputs Hello World in an HTML text element.

Copy from this file, or this gist.

To understand this code, do the Elm Architecture Tutorial. (It's a lot. But it's the place to go to understand Elm.)

4B: compile the main module

I want this compiled into a JavaScript file in my `target` directory, so this is my build command:
elm make --output target/elm.js src/CarrotPotato.elm
When this works, a target/elm.js file should exist.

Note: by default, elm-make (v0.16) creates an index.html file instead of elm.js. That's fine for playing around, but in any real project I want control over the surrounding HTML.
Note: I ask elm-make to build the top-level module of my project. Once I add more source files, elm-make will compile all the ones that my top-level module brings in.

To remind myself of how to do this correctly, I put it in a script:
echo "elm make --output target/elm.js src/CarrotPotato.elm" >> build
chmod u+x 
build
Then every time I want to compile:
./build

Step 5: Run the program in a web page

Elm runs inside a web page. Let's call that page index.html because that's the default name for these things. Create that file and put something like this into it:
touch index.html
[editor] 
index.html
put this in:
<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8">
  <title>CarrotPotato</title>
  <script type="text/javascript" src="target/elm.js"></script>
</head>
<body>
</body>

<script type="text/javascript">
  var app = Elm.fullscreen(Elm.
CarrotPotato, {});Elm.CarrotPotato.fullscreen();
</script>

</html>
The important parts here are:
  • in the header, set the page's title
  • in the header, bring in the compiler output; this matches the file I told elm-make to write to
  • in the header, you're free to bring in CSS
  • the body is empty
  • the script tag at the end activates my Elm module. (The strikethrough is Elm 0.16; the correction is Elm 0.18)
Save this file, and open it in your default browser:
open index.html
You should see "Hello World". Quick, make a commit!

Note: opening index.html as a file doesn't always work smoothly. If the browser gives you trouble, try running an http server in that directory instead. There's a very easy one available from npm.

Step 6: Go forth and Elminate

The foundation is set for an Elm project. From here, I can start building an application. Here are some things I often do next:
  • change the view function to show something more interesting. see elm-html for what it can retusrn.
  • make a git repository, push my project to it; update this in elm-package.json, and create a README.md
  • create a gh-pages branch to serve my project on the web (blog post on this coming soon, I hope)
  • break out my project's functionality into more modules, by creating files like src/CarrotPotato/View.elm and importing them from my main module
You can get everything up to this point without doing it yourself by cloning my elm-sample repo.
I do this:
git clone git@github.com:jessitron/elm-sample.git carrot-potato
< create repo at github called my-user/carrot-potato; copy its git url>
cd carrot-potato
git remote set-url origin git@github.com:my-user/carrot-potato.git
 
Comments and suggestions welcome! I'm sure this isn't the most optimal possible setup.



6 comments:

  1. There is WIP on elm-format (rationale: http://elm-lang.org/docs/style-guide , code: https://github.com/avh4/elm-format ).

    If you build it and have it on your PATH, you can let your editor run it everytime you save your .elm file, and it will beautify the code.

    E.g. VIM:
    autocmd BufWritePost *.elm silent execute "!elm-format --yes %" | edit! | set filetype=elm

    There are still some warts (until recently comments did not work; there are bugs; some syntax is still wonky) but having something beautify the code is a great feeling. :)

    ReplyDelete
  2. Nice! I will try to integrate this with https://github.com/ivanoats/elm-brunch-starter

    ReplyDelete
  3. The first note in 4B you say 'That's fine for playing around, but in any real project I want control over the surrounding HTML.'.

    Is this because you need/want access to the head element and that one can only declare from the body element down?

    Flowing on from this, are you using a CSS library to control style or going elmtastic?

    Thank you for the post.

    Jono

    ReplyDelete
  4. This is great! Thanks for taking the time to publish your experiences.

    I got a basic GitHub pages script running here:
    https://github.com/mikegehard/elm-minesweeper/blob/master/publish

    ReplyDelete
  5. I bet they'd like a feed of your elm posts over at http://planet.elm-lang.org/

    ReplyDelete
  6. Thankyou for this Jess, really helpful!

    ReplyDelete