It's time to start working with Mix, a tool “that provides tasks for creating, compiling, and testing Elixir projects, managing its dependencies, and more” according to the docs. In this lab, you will become familiar with these most common uses.
Mix is described in Chapter 13 of the Programming Elixir book, the Elixir Guides and at Elixir School. The more you know about using Mix, the more streamlined your work will be from this point forward.
We're going to use Mix two different ways: to create and build a complete application, and to create a playground for exploring libraries and their APIs.
Before you get started, it's a good idea to create a Projects directory somewhere sensible to hold all the Elixir projects we'll be using.
Yes, it's my favorite projects/example: Conway's Game of Life. This version is due to Saša Jurić (we'll use a modified version - Sasa's original is meant to run as a script using the elixir command).
Start by creating a new, empty project:
$ mix new conway
This produces a project consisting of a directory conway
with subdirectories for each aspect of the project (source code, tests, etc.) and a configuration script mix.exs that can be used to customize the project, by writing simple Elixir code.
You should see:
* creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/conway.ex * creating test * creating test/test_helper.exs * creating test/conway_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd conway mix test Run "mix help" for more commands.
The source code files for the project go in the conway/lib directory. Right now it contains a dummy file containing starter code called conway.ex. This is where the code will go.
Open the file conway/lib/conway.ex in your favorite editor. Have a quick look at the starter code, then replace it with the contents of this file. Save and return to the conway directory.
From here you can run the appropriate mix commands. To build the project:
$ mix compile
If all went well, you'll see some warnings having to do with the Erlang random
library. The project will still run. To see it run, maybe this will work?
$ mix run
Huh. That doesn't do anything. Can you tell why? If you said “this is a .ex file containing only module definitions,” you got it right. We need an executable expression to run this thing. The Conway
module contains the main function, run_game
, and mix gives us a way to invoke it. Try it and see!
$ mix run -e 'Conway.run_game'
You should be watching the program run through its patterns for a couple of minutes. When it finishes, show me your history to get credit for Part 1.
The Conway code uses a number of common Elixir idioms, and nothing that you haven't seen before except maybe structs, which are just a straightforward extension of maps covered in Chapter 8. We'll spend some time trying to understand this code in Homework 3.
It's worth having a project around just to try things out in, sometimes called a “playground” or “sandbox”. Starting back in your Projects directory (or whatever you named it), create a new project:
$ mix new playground
You should see:
* creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/playground.ex * creating test * creating test/test_helper.exs * creating test/playground_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd playground mix test Run "mix help" for more commands.
As before, the lib subdirectory contains the source code, starting with a dummy file playground.ex. We don't need this right now, but its worthwhile to try out the commands mix just suggested:
$ cd playground $ mix test
The main thing you want to see here is 2 tests, 0 failures
For the rest of this, you're going to record responses on a sheet of paper to turn in.
We're going to use mix.exs to modify the project configuration. Our main use will be to setup dependencies for the libraries we want to use. These are defined in the list returned by the private function deps
. The list contains a set of tuples, each containing an atom
representing the library and a string
representing the version number our program needs.
Let's load up and explore the HTTPoison
library. This library provides a set of modules and functions to query web resources programmatically. Edit mix.exs, by adding the following map to the list in deps
:
{:httpoison, "~> 1.0"}
Save the file and run
$ mix deps.get
(If it asks you to install Hex, say yes.) Running the deps.get
option checks if the libraries listed as dependencies are already on the computer. If not, they will be downloaded and installed, along with any other libraries needed. This may take a few minutes! Once installed, they can be used in our programs.
We can also try out the library interactively. To do this, issue the command
$ iex -S mix
Now mix is going to compile all those libraries it just installed. It may also ask you to install yet another library; again say yes (I was asked to install rebar3
). Toward the end it should say:
==> httpoison Compiling 2 files (.ex) Generated httpoison app ==> playground Compiling 1 file (.ex) Generated playground app Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)
If so, it's time to play around with HTTPoison
(some of these examples come from the GitHub page for the library, and some I tried on my own). The library provides functions to send requests directly to a webserver and get data back. The start
function gets everything set up:
HTTPoison.start
If HTTPoison
functions have @doc
comments, we can use the iex helpers to learn about them. First, let's find out what public functions the library exports:
exports HTTPoison
Quite a list! Veterans of CSci 284 or 428 might recognize some of these functions as names of HTTP methods like GET and PUT. Use help to find out more about the get!
function:
h HTTPoison.get!
Let's try it out…see what happens when you make this call:
HTTPoison.get! "http://scarl.sewanee.edu"
This should return a struct %HTTPoison.Response
with four fields. Find and record the field names (think maps, again) and the type of data in them. You might recognize the contents of the first field as HTML.
Let's save this response in a variable:
response = HTTPoison.get! "http://scarl.sewanee.edu"
Now ask it for just the headers, and record the result:
response.headers
Try the get!
function again, but this time for a website of your choice, like google.com or similar. Record the request_url
and status_code
fields.
According to the docs, the get!
function just returns the struct, or fails if it can't find the website. Try to get it to fail. Record its response.
The get
function works much the same way, but it returns a tuple indicating success or failure along with the struct. Try calling this function on both a valid and invalid page.
Ask for a page on my classes website that probably doesn't exist. Record the webpage requested and the status_code
you get.
Finally, try these. For the first two, record the headers
list. For the rest, record the first element of the response tuple and the status_code
if it works.
HTTPoison.get! "http://httparrot.herokuapp.com/get" HTTPoison.get! "http://httparrot.herokuapp.com/" HTTPoison.get "http://127.0.0.1" HTTPoison.get! "http://127.0.0.1" HTTPoison.get "http://api.github.com/repos/elixir-lang/elixir/issues" HTTPoison.get "https://api.github.com/repos/elixir-lang/elixir/issues"
All done! Exit iex
.