Using Rug with Elm

At elm-conf and CodeMesh and YOW! Australia this year, I did live demos using automated code modification with Atomist Rug.

Rug is now officially open source, and the Rug CLI is available so that you can try (and change! and improve!) these editors on your own Elm code. This blog post tells you how.

I usually start a new Elm project as a static page, make it look like something; then turn it into a beginner program, add some interactivity; then turn it into an advanced program and add subscriptions. I like how this flow lets me start super-simple, and then add the pieces for access to the world as I need it.

Now you can do this too!

Watch out: these editors (and the parser behind them) work for the code I’ve tried them on. As you try them, you’ll find cases I didn’t cover. Please file an issue when you do, or find me on Atomist-Community slack.

Install Rug

The local version of the Rug runtime is the Rug CLI. Complete installation instructions are here.

TL;DR for Mac:

brew tap atomist/tap
brew install rug-cli

Generate a project

This will create a directory containing a new static Elm app, with a build script etc. This will put a project named banana under your current directory, make it a git repo and make an initial commit:

rug generate -R jessitron:elm-rugs:StaticPage banana

Inside banana, edit src/Main.elm. Put something in that empty div.
Run ./build
Open target/index.html to see the results.

Upgrade it to a beginner program

After your banana looks OK, make it interactive. Run this inside your project directory:

rug edit jessitron:elm-rugs:UpgradeToBeginnerProgram

Now your src/Main.elm contains the beginnings of a beginner program. The model is empty and the only message is Noop, which does nothing. This is the beginner program template from the Elm tutorial, except that the view function is populated based on your main from the static page.

You could add a button:

rug edit jessitron:elm-rugs:AddButton button_text=”Push Me” button_message=ButtonPushed

Now your src/Main.elm contains a new message type, ButtonPushed. Your update function handles it, but does nothing interesting.

type Msg
    = Noop
    | 
ButtonPushed

update : Msg -> Model -> Model
update msg model =
    case msg of
        Noop ->
            model

        ButtonPushed ->            model

Find a new function hanging out at the end of the file, buttonPushedButton. Incorporate that into your view to display the button. Run ./build and refresh target/index.html; push the button and see the message in the debugger.

Try adding a text input in a similar way, with

rug edit jessitron:elm-rugs:AddTextInput input_name=favoriteColor

This adds a function, a message, and a field to the model so that you’ll have access to the content of the text input.

Try passing -R to rug, and it’ll make a commit for you after the editor completes. You have to make a commit yourself right before running rug, or it’ll complain.

For further edit operations, see my elm-rugs repo. You can upgrade to a full program, and add subscriptions to clicks and window size.

Change these editors! Add more!

The best part of running locally is running local versions.
Clone my repository: git clone git@github.com:jessitron/elm-repo.git
Now, go to the secret directory holding the editors: cd elm-repo/.atomist/editors
Here, you can see the scripts that work on the code, like AddButton.rug.

To run the local versions, be in that elm-rugs directory, and point rug at your project directory with -C:

rug -l -C /path/to/my/projects/banana edit AddButton button_message=Yay button_text=”Say hooray”

I don’t have to qualify the editor name with jessitron:elm-rugs when it’s local.

There’s more information in the Atomist docs on how rug works. TL;DR is, the files in the top level of elm-rugs/ are the starting point for newly generated project. NewStaticPage.rug, as a generator, starts from those and then changes the project name. The editors all start from whatever project they’re invoked on, and they can change files in place, or create new ones from templates in the elm-rugs/.atomist/templates directory. (Most of my templates are straight files, with a .vm suffix to make Rug’s merge function work.)

Questions very welcome on either the elm-lang slack in the #atomist channel, or the Atomist community slack on the #rug-elm channel.

Pull requests are even more welcome. Issues, too. These rugs work for the narrow cases I’ve tested them on. It’ll be a community effort to refine and expand them!

Property Testing in Elm

Elm is perfectly suited to property testing, with its delightful data-in–data-out functions. Testing in Elm should super easy.

The tooling isn’t there yet, though. This post documents what was necessary today to get a property to run in Elm.

Step 1: elm-test

This includes an Elm library and a node module for a command-line runner. The library alone will let you create a web page of test results and look at it, but I want to run them in my build script and see results in my terminal.

Installation in an existing project:

elm package install deadfoxygrandpa/elm-test
npm install -g elm-test

The node module offers an “elm test init” functionality to put some test files in the current directory: TestRunner (which is the Main module for test runs[1]) and Tests.elm which holds actual tests. Personally, I found it necessary to follow the following steps as well.

  • create a test directory (I don’t want tests in my project home), and move the TestRunner.elm and Tests.elm files there.
  • add that test directory to the source directories in elm-package.json

Step 2: elm-check

The first thing to know is: which elm-check to install. You need the one from NoRedInk:

elm package install NoRedInk/elm-check

The next thing is: what to import. Where do all those methods used in the README live?

Here is a full program that lets elm-test execute the properties from the elm-check readme.
TL;DR: You need to import stuff from Check and Check.Producer for all properties; and  for the runner program, ElmTest and Check.Test and Signal, Console, and Task.

Name it test/Properties.elm and run it with

elm test test/Properties.elm

The output looks like

Successfully compiled test/Properties.elm
Running tests…
  1 suites run, containing 2 tests
  All tests passed

Here’s the full text just in case.

module Main (..) where
import ElmTest
import Check exposing (Evidence, Claim, that, is, for)
import Check.Test
import Check.Producer as Producer
import List
import Signal exposing (Signal)
import Console exposing (IO)
import Task

console : IO ()
console =
  ElmTest.consoleRunner (Check.Test.evidenceToTest evidence)

port runner : Signal (Task.Task x ())
port runner =
  Console.run console

myClaims : Claim
myClaims =
  Check.suite
    “List Reverse”
    [ Check.claim
        “Reversing a list twice yields the original list”
        `that` (\list -> List.reverse (List.reverse list))
        `is` identity
        `for` Producer.list Producer.int
    , Check.claim
        “Reversing a list does not modify its length”
        `that` (\list -> List.length (List.reverse list))
        `is` (\list -> List.length list)
        `for` Producer.list Producer.int
    ]

evidence : Evidence
evidence =
  Check.quickCheck myClaims

How to write properties is a post for another day. For now, at least this will get something running.

See also: a helpful post for running elm-check in phantom.js

[1] How does that even work? I thought modules needed the same name as their file name. Apparently this is not true of Main. You must name the module Main. You do not have to have a ‘main’ function in there (as of this writing). The command-line runner needs the ‘console’ function instead.

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: https://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 `, where 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:


 
  CarrotPotato
  <script type="text/javascript" src="target/elm.js“>




  var app = Elm.fullscreen(Elm.
CarrotPotato, {});Elm.CarrotPotato.fullscreen();

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.

An Opening Example of Elm: building HTML by parsing parameters

I never enjoyed front-end development, until I found Elm. JavaScript with its `undefined`, its untyped functions, its widely scoped mutable variables. It’s like Play-Doh, it’s so malleable. And when I try to make a sculpture, the arms fall off. It takes a lot of skill to make Play-Doh look good.

Then Richard talked me into trying Elm. Elm is more like Lego Technics. Fifteen years ago, I bought and built a Lego Technics space shuttle, and twelve years ago I gave up on getting that thing apart. It’s still in my attic. Getting those pieces to fit together takes some work, but once you get there, they’re solid. You’ll never get “method not found on `undefined`” from your Elm code.

Elm is a front-end, typed functional language; it to JavaScript for use in the browser. It’s a young language (as of 2015), full of opportunity and surprises. My biggest surprise so far: I do like front-end programming!

To guarantee that you never get `undefined` and never call a method that doesn’t exist, all Elm functions are Data in, Data out. All data is immutable. All calls to the outside world are isolated. Want to hit the server? Want to call a JavaScript library? That happens through a port. Ports are declared in the program’s main module, so they can never hide deep in the bowels of components. Logic is in one place (Elm), interactions in another.

one section (Elm) has business logic and is data-in, data-out. It has little ports to another section( JavaScript) that can read input, write files, draw UI. That section blurs into the whole world, including the user.


This post describes a static Elm program with one tiny port to the outside world. It illustrates the structure of a static page in Elm. Code is here, and you can see the page in action here. The program parses the parameters in the URL’s query string and displays them in an HTML table.[1]

All web pages start with the HTML source:


  URL Parameters in Elm
  <script src="elm.js” type=”text/javascript”>
 

  var app = Elm.fullscreen(Elm.UrlParams,
                           { windowLocationSearch:
                               window.location.search
                           });

This brings in my compiled Elm program and some CSS. Then it calls Elm’s function to start the app, giving it the name of my module which contains main, and extra parameters, using JavaScript’s access to the URL search string.

Elm looks for the main function in my module. The output of this function can be a few different types, and this program uses the simplest one: Html. This type is Elm’s representation of HTML output, its virtual DOM.

module UrlParams where

import ParameterTable exposing (view, init)
import Html exposing (Html)

main : Html
main = view (init windowLocationSearch)

port windowLocationSearch : String

The extra parameters passed from JavaScript arrive in the windowLocationSearch port. This is the simplest kind of port: input received once at startup. Its type is simply String. This program uses one custom Elm component, ParameterTable. The main function uses the component’s view function to render, and passes it a model constructed by the component’s init method.

Somewhere inside the JavaScript call to Elm.fullscreen, Elm calls the main function in UrlParams, converts the Html output into real DOM elements, and renders that in the browser. Since this is a static application, this happens once. More interesting Elm apps have different return types from main, but that’s another post.

From here, the data flow of this Elm program looks like this:

The three layers are: a main module, a component, and a library of functions.
The main module has one input port for the params.  That String is transformed by init into a Model, which is transformed by View into Html. The Html is returned by main and rendered in the browser. This is the smallest useful form of the Elm Architecture that I came up with.

Here’s a piece of the ParameterTable module:

module ParameterTable(view, init) where

import Html exposing (Html)
import UrlParameterParser exposing (ParseResult(..), parseSearchString)

— MODEL
type alias Model = { tableData: ParseResult }

init: String -> Model
init windowLocationSearch =
  { tableData = parseSearchString windowLocationSearch }

— VIEW
viewModel -> Html
view model =
  Html.div …

The rest of the code has supporting functions and details of the view. These pieces (Model, init, and view) occur over and over in Elm. Often the Model of one component is composed from the Models of subcomponents, and the same with init and view functions.[2]

All the Elm files are transformed by elm-make into elm.js. Then index.html imports elm.js and calls its Elm.fullscreen function, passing UrlParams as the main module and window.location.search in the extra parameter. And so, a static (but not always the same) web page is created from data-in, data-out Elm functions. And I am a happy programmer.


[1] Apparently there’s not a built-in thing in JavaScript for parsing these. Which is shocking. I refused to write such a thing in JavaScript (where by “write” I mean “copy from StackOverflow”), so I wrote it in Elm.

[2] Ditto with update and Action, but that’s out of scope. This post is about a static page.