Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Mix

curriculum-main/reading/mix.livemd

Mix

Mix.install([
  {:jason, "~> 1.4"},
  {:kino, "~> 0.9", override: true},
  {:youtube, github: "brooklinjazz/youtube"},
  {:hidden_cell, github: "brooklinjazz/hidden_cell"}
])

Navigation

Home Report An Issue Pascal’s TriangleGames Project

Review Questions

Upon completing this lesson, a student should be able to answer the following questions.

  • What is the purpose of Mix?
  • Where should our application go inside of a mix project?
  • How do we compile a mix project and explore it using the IEx shell?

Mix

Mix is a build tool that ships with Elixir that automates tasks for creating, compiling, and testing your application.

We start a new Mix application by running the following in the command line.

mix new app_name

Where app_name is the name of the project.

Your Turn

Create a hello_world application.

You should notice that the command generated several files.

$ mix new hello_world
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/hello_world.ex
* creating test
* creating test/test_helper.exs
* creating test/hello_world_test.exs

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

    cd hello_world
    mix test

Run "mix help" for more commands.

Your project should have the following file and folder structure.

hello_world/
  lib/
    hello_world.ex
  test/
    hello_world_test.exs
    test_helper.exs
  .formatter.exs
  .gitignore
  mix.exs
  README.md

We’ll walk through the mix project structure and how you can build Elixir applications.

The lib Folder

The lib/ folder contains the files for the project. We start with a single module named after the project. You should have a HelloWorld module in the lib/hello_world.ex file.

We use @moduledoc and @doc to document our code.

defmodule HelloWorld do
  @moduledoc """
  Documentation for `HelloWorld`.
  """

  @doc """
  Hello world.

  ## Examples

      iex> HelloWorld.hello()
      :world

  """
  def hello do
    :world
  end
end

Running A Mix Project

To execute the Elixir code for our Mix project, you can load the project into the IEx Shell by running the following command in the project folder.

iex -S mix

Now, all of the .ex files and modules under the lib/ folder are available in the IEx shell environment.

Mix ignores .exs files, so any modules in a .exs file will not be available in the IEx shell.

Your Turn

Load your project into the IEx Shell.

iex -S mix

Call the hello/0 function.

iex> HelloWorld.hello()
:world

Notice that the iex -S mix command compiles your project into the _build folder. This folder contains .beam files which are executable files generated by the Erlang compiler.

You can also compile the project by running mix compile in the command line.

Tests

ExUnit is a built-in testing framework. Test files are under the test/ folder in .exs files.

In your hello_world project, there should be an example test file test/hello_world_test.exs with the following content.

defmodule HelloWorldTest do
  use ExUnit.Case
  doctest HelloWorld

  test "greets the world" do
    assert HelloWorld.hello() == :world
  end
end

Test files generally correspond to another file in the lib/ folder.

For example, a lib/greeting/hola.ex file might have a corresponding test/greeting/hola_test.exs file.

We will dive deeper into how to test Elixir projects in the ExUnit section.

For now, know that you can execute all of your tests by running the following in the command line.

$ mix test

Your Turn

Run mix test in your hello_world project folder from the command line, and you should see an output similar to the following.

Compiling 1 file (.ex)
Generated hello_world app
..

Finished in 0.02 seconds (0.00s async, 0.02s sync)
1 doctest, 1 test, 0 failures

Randomized with seed 768874

File And Module Names

Mix compiles all .ex files under the lib/ folder and bundles them together into a project.

Generally, Elixir projects organize modules under the main project namespace.

So all of the modules in the HelloWorld application would be grouped under the HelloWorld namespace.

For example, a new Greeting module would be defined under HelloWorld.Greeting.

defmodule HelloWorld.Greeting do
  def salutations(name) do
    "Salutations, #{name}!"
  end
end

Nested modules generally relate to each other, often sub-modules deal with a more specific application of the higher-level module.

For example, a Formal module would be under the Greeting module.

defmodule HelloWorld.Greeting.Formal do
  def charmed() do
    "Charmed, I'm sure."
  end
end

While not enforced, module and file structure often follow a pattern, where the module name matches its folder and file.

For example, the HelloWorld.Greeting.Formal module would be in a lib/greeting/formal.ex file, and the HelloWorld.Greeting will be in the lib/greeting.ex file.

lib/
  greeting.ex
  hello_world.ex
  greeting/
    formal.ex

Your Turn

Modules can call functions from any other modules in .ex files under the lib/ folder, just as you have already seen in livebook cells.

defmodule ModuleA do
  def run do
    "A"
  end
end

defmodule ModuleB do
  def run do
    ModuleA.run() <> "B"
  end
end

ModuleB.run()

In your hello_world application, create a new module, HelloWorld.Name in a lib/name.ex file, which will return a random name.

defmodule HelloWorld.Name do
  def random do
    Enum.random(["Peter", "Bruce", "Tony"])
  end
end

Then, call the HelloWorld.Name.random/0 function from the HelloWorld module in hello_world.ex.

defmodule HelloWorld do
  def hello do
    "Hello, #{HelloWorld.Name.random()}."
  end
end

Run your project in the IEx shell and ensure the HelloWorld.hello/0 function works as expected.

$ iex -S mix
iex> HelloWorld.hello()
"Hello, Peter."

Mix.exs

The mix.exs file’s main responsibility is to configure the mix project.

It defines two public functions, project/0 and application/0.

The project/0 function returns project configuration such as the project name, current version, and installed dependencies. The project/0 function relies on the private deps/0 function to provide a list of dependencies.

The application/0 function returns application configuration used to generate an application manifest file in the _build folder. This file is used under the hood to run the project. For more, you can see the Application module on HexDocs. Sometimes removing the _build folder and recompiling a project can resolve compilation bugs.

Dependencies

Other developers create open-source Elixir projects hosted on GitHub, which we can add as dependencies to our project.

There is a massive ecosystem of Elixir projects, which means you don’t have to reinvent the wheel and solve already solved problems. The Hex.pm contains a list of available Elixir projects you may find useful. For a curated list, check out the awesome-elixir project.

To install a dependency, include the name of the project and desired version in a tuple inside the deps/0 function in mix.exs. Notice that mix.exs includes comments on installing a dependency using the project version or GitHub URL.

  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end

Your Turn

Add the Faker project to your hello_world application. Faker provides functions for generating fake data, often useful for randomized test data.

It’s often most reliable to check the Hex page of a project for the latest version.

Ensure you add :faker with the latest version to the deps/0 function in mix.exs.

  defp deps do
    [
      {:faker, "~> 0.17.0"}
    ]
  end

Then run mix deps.get to update your project dependencies.

Once installed, you have access to the Faker module.

Use the Faker.Person.first_name/0 function in your HelloWorld.hello/0 function.

For more on how to use Faker, you can read the Faker Documentation.

def hello do
  "Hello, #{Faker.Person.first_name()}."
end

Ensure your hello/0 function works in the IEx shell.

iex -S mix
iex> HelloWorld.hello()
"Hello, Salvador." # The name will be random each time.

Versions

Project Versions are generally represented by Major.Minor.Patch numbers.

When installing dependencies, we specify a version requirement. We can even specify a version between two numbers using comparison operators. i.e. >= 2.0.0 and < 2.1.0,

The locally installed dependency may not exactly match the version requirement, but must belong in the range specified.

The ~> symbol you see in dependencies means it will never go above its upper bound.

  • ~> 2.1 would allow any version below 3.0.0 and above or equal to 2.1.0.
  • ~> 2.1.2 would allow any version below 2.2.0 and above or equal to 2.1.2.

The Version documentation goes into further depth.

You can automatically update project versions by running the following in your command line.

mix deps.update example_dep

Or with the --all option to update all deps.

mix deps.update --all

Formatting

The .formatter.exs file contains project configuration for how to format Elixir source code files automatically.

You can run mix format from the command line in the project folder to format elixir code.

Your Turn

Indent the code in hello_world.ex. Then, run mix format and notice the code automatically formats. If it does not, ensure that you do not have syntax errors that prevent code formatting.

README.md

Most projects include a README.md file which contains basic information about the project written in markdown.

There is no universal README structure however it’s common to include:

  • The repository name.
  • The project purpose.
  • Setup instructions.
  • Collaborators on the project.

GitHub pages display the README on the front of the repository page for the project.

When building your own projects, you’ll want to make sure you update the default README to include relevant information.

View the Markdown cheat-sheet for a quick guide on markdown syntax.

Mix Environments

Mix projects can run in different environments. The default environment is your development :dev environment.

Run your hello_world project in the IEx shell and call Mix.env() to see that :dev is the current environment.

$ iex -S mix
iex> Mix.env()
:dev

The project will use the :test environment when running tests and :prod when deployed to production.

We can change project behavior or configuration based on the current environment. For example, if you had SMS (text) notifications in your project, you might want to disable them in the :dev and :test environment to ensure that developers don’t accidentally send out text messages to users.

Further Reading

For more on Mix, consider reading the following.

Commit Your Progress

DockYard Academy now recommends you use the latest Release rather than forking or cloning our repository.

Run git status to ensure there are no undesirable changes. Then run the following in your command line from the curriculum folder to commit your progress.

$ git add .
$ git commit -m "finish Mix reading"
$ git push

We’re proud to offer our open-source curriculum free of charge for anyone to learn from at their own pace.

We also offer a paid course where you can learn from an instructor alongside a cohort of your peers. We will accept applications for the June-August 2023 cohort soon.

Navigation

Home Report An Issue Pascal’s TriangleGames Project