Elixir - DailyTrip [001.3] Mix and Modules

3829 ワード

mix help
mix help new

mix new hello_world

cd hello_world
mix test
> $ tree
├── _build
│   └── test
│       └── lib
│           └── hello_world
│               ├── consolidated
│               │   ├── Elixir.Collectable.beam
│               │   ├── Elixir.Enumerable.beam
│               │   ├── Elixir.IEx.Info.beam
│               │   ├── Elixir.Inspect.beam
│               │   ├── Elixir.List.Chars.beam
│               │   └── Elixir.String.Chars.beam
│               └── ebin
│                   ├── Elixir.HelloWorld.beam
│                   └── hello_world.app
├── config
│   └── config.exs
├── lib
│   └── hello_world.ex
├── mix.exs
├── README.md
└── test
    ├── hello_world_test.exs
    └── test_helper.exs

Let's look at the lib/hello_world.ex file to check out our HelloWorld module:
defmodule HelloWorld do
  @moduledoc """
  Documentation for HelloWorld.
  """

  @doc """
  Hello world.

  ## Examples

      iex> HelloWorld.hello
      :world

  """
  def hello do
    :world
  end
end

Before we move on, let's run the REPL with our project loaded into it. Normally, if you just run iex , you won't have these modules loaded. You can load your project by running:
iex -S mix
iex(1)> HelloWorld.hello()
:world

Let's create a division function:
defmodule HelloWorld do
  # ...
  def div(a, b) do
    a / b
  end
end

And we can use it:
iex -S mix
iex(1)> HelloWorld.div(1, 2)
0.5

Of course, this suffers from a bit of a problem:
iex(2)> HelloWorld.div(1, 0)
** (ArithmeticError) bad argument in arithmetic expression
    (hello_world) lib/hello_world.ex:20: HelloWorld.div/2
defmodule HelloWorld do
  # ...
  def div(a, 0) do
    :no_dice
  end
  def div(a, b) do
    a / b
  end
end
iex -S mix
iex(1)> HelloWorld.div(1, 0)
:no_dice

Let's change this a bit so we can pattern match on whether or not the function was successful:
defmodule HelloWorld do
  def div(a, 0) do
    {:error, "attempt at division by zero"}
  end
  def div(a, b) do
    {:ok, a / b}
  end
end

Now we can handle this result in a case statement. Let's do it in a test:
vim test/hello_world_test.exs
defmodule HelloWorldTest do
  use ExUnit.Case
  doctest HelloWorld

  test "division" do
    {:ok, result} = HelloWorld.div(2, 1)
    assert result == 2.0
  end
  test "division by zero" do
    {:error, err} = HelloWorld.div(1, 0)
    assert err == "attempt at division by zero"
  end
end

When you use a pipe, which looks like |>, you're really just moving the first argument to a function out of the function call, to the left of the pipe. We'll open an iex session to check it out:
iex -S mix

This:
iex(1)> HelloWorld.div(1, 2)
{:ok, 0.5}

is the same as this:
iex(2)> 1 |> HelloWorld.div(2)
{:ok, 0.5}
vim test/hello_world_test.exs
defmodule HelloWorldTest do
  # ...
  test "pipes and strings" do
    # If we import the `String` module we can use its functions without
    # qualifying them fully - so we get `upcase` instead of the more-verbose
    # `String.upcase`
    import String

    val =
      "josh"
        |> reverse
        |> capitalize
        |> reverse

    assert val == "josH"
  end
end