Mix Build tool

One of the best features of elixir language are the tools that ships with it. One of then Is Mix. Mix is build tool that helps create, setup and run an elixir project. To demonstrate how mix do all this, let's create a simple project for a popular Kata named FizzBuzz.

The rules of FizzBuzz are:
The program must output a line from 1 to a given number with three exceptions:

  1. If the value of that line is divided by 3 then the output must be Fizz.
  2. If the value of that line is divided by 5 then the output must be Buzz.
  3. If the value of that line is divided by 3 and by 5 the output of that line must be FizzBuzz.

Now that we have all the rules. Let's start by creating a new app. The fist step is to type mix new fizz_buzz at the terminal we will see this output:

* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/fizz_buzz.ex
* creating test
* creating test/test_helper.exs
* creating test/fizz_buzz_test.exs

Your mix project was created successfully.  
You can use mix to compile it, test it, and more:

    cd fizz_buzz
    mix test

Run `mix help` for more commands.  

Our app is now created, we can now enter at the fizz_buzz folder and see the file structure for a typical elixir app. The three most important files are the mix.exs, test/fizz_buzz_test.exs and lib/fizz_buzz.ex. mix.exs is the file that configures your project. It has 2 main public functions application and project. The project function must return a list with the project information just as the app name, the app version and its dependencies. The application function is where you put the information that will be used to generate an .app file.

# mix.exs
defmodule FizzBuzz.Mixfile do  
  use Mix.Project

  def project do
    [app: :fizz_buzz,
     version: "0.0.1",
     elixir: "~> 1.1.0-dev",
     deps: deps]
  end

  def application do
    [applications: [:logger]]
  end

  defp deps do
    []
  end
end  

The lib/fizz_buzz.ex is the file generated that is intended to be the main file of our app. It is created by default and only has an empty module to be implemented.

# fizz_buzz.ex
defmodule FizzBuzz do  
end  

The test/fizz_buzz_test.exs file is where you put your tests. In fact every file that lies inside the test will be executed as a test file. By default when we create a project, mix creates a simple test that tests if 1 + 1 is equals 2.

# fizz_buzz_test.exs
defmodule FizzBuzzTest do  
  use ExUnit.Case

  test "the truth" do
    assert 1 + 1 == 2
  end
end  

You can run this test with mix test command at the root of the project. We will see that our test will be pass. Try changing the value of the sum from 2 to 3 and see what happens.

Let's start to implement our FizzBuzz by writing some tests. First Let's create a test. Guess the first thing that we got to do is to create the first test from 1 to 3 where it will output this:

1  
2  
Fizz  

And here is the code of the test.

  test "compute values from 1 to 3" do
    expected_result = """
    1
    2
    Fizz
    """
    assert FizzBuzz.compute(3) == expected_result
  end

If we try to run the tests again we will see an error. Saying that the function compute/0 is not defined. Let's go and implement it.

# fizz_buzz.ex
defmodule FizzBuzz do  
  def compute(number) do
    """
    1
    2
    Fizz
    """
  end
end  

Aha! I bet you already realized what I did. In elixir the last expressions is the return value of the function. So what our function does is just returning the expected output for our test. if we run mix test we will see that everything is passing and we are very happy. This is part of the TDD philosophy, first we write the test and it should fail, then we write a small piece of software so the test can pass. Then finally we refactor the code. We already known what we want so we can go straight to our code.

defmodule FizzBuzz do  
  def compute(number) when number > 1 do
    compute(number - 1) <> output_line(number)
  end

  def compute(1) do
    output_line(1)
  end

  defp output_line(number) do
    if rem(number, 3) == 0 do
      "Fizz\n"
    else
      "#{number}\n"
    end
  end
end  

We can see that at our function compute/1 definition we are defining some rules for two specific cases. In Elixir what makes a function signature is its names and its arity (number of values you can pass to a function). But we can define some rules for a function to execute, just like we did with when. Note that we are using recursion instead of using a conventional loop, it's a common thing at functional languages to use recursion calls. Run mix test and our test pass. Let's write the test to print until 5 so we can see the Buzz word at screen.

  test "compute values from 1 to 5" do
    expected_result = """
    1
    2
    Fizz
    4
    Buzz
    """
    assert FizzBuzz.compute(5) == expected_result
  end

If we try to run this test it will fail. This is because we have not implemented a rule of what happens when the number is divided by 5. Since we have refactored our code before, we can just modify our output_line/1 function and just add some simple rule there and everything will pass! yey!

  defp output_line(number) do
    if rem(number, 3) == 0 do
      "Fizz\n"
    else
      if rem(number, 5) == 0 do
        "Buzz\n"
      else
        "#{number}\n"
      end
    end
  end

Now that everything is passing we can try to refactor that code. I really don't like the inner block thing that we are producing for every new rule we must explicitly create a new if block and close it. Let's try another conditional that is more clear to use.

  defp output_line(number) do
    cond do
      rem(number, 3) == 0 ->
        "Fizz\n"
      rem(number, 5) == 0 ->
        "Buzz\n"
      true ->
        "#{number}\n"
    end
  end

Okay now just one more case we must cover that is if the number is divided by 3 and 5 at the same time. First let's write our test case until 15 run the tests and see it failing.

  test "compute values from 1 to 15" do
    expected_result = "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n"
    assert FizzBuzz.compute(15) == expected_result
  end

It fails as expected let's write the last rule first so we can make sure it has a higher precedent.

  defp output_line(number) do
    cond do
      rem(number, 3) == 0 && rem(number, 5) == 0 ->
        "FizzBuzz\n"
      rem(number, 3) == 0 ->
        "Fizz\n"
      rem(number, 5) == 0 ->
        "Buzz\n"
      true ->
        "#{number}\n"
    end
  end

And our tests are passing again. By this time we must already solve the problem. You can try by creating a string that goes from 1 to 100 and will see that our tests will pass. At this point we have our mix application completely done. We can run test on it. Now let's try to run our code at the console. If we type iex -S mix we will enter at an interactive Elixir console and we will have access to all our code thought here. Let's try by typing FizzBuzz.compute(5). The output of our FizzBuzz challenge is done there. And you can try any other value that you want.

This was a quick introduction to mix, a build tool that ships with Elixir. There's a lot to go from here. But having a such nice tool to create a project with a full functional test suite is already great.