Today I Learned

A Hashrocket project

128 posts about #elixir

Referencing Values In IEx's History

Each time we execute a statement in an iex session, the counter is incremented. These numbers are references to the history of the session. We can use these references to refer to previously executed values using v/1. This is particularly handy for multi-line statements or when we forget to bind to the result of some function.

Consider the following iex session:

iex(1)> :one
:one
iex(2)> 1 + 1
2
iex(3)> "three" |> String.to_atom()
:three

If we execute v() on its own, it is the same as v(-1) in that it will give us the latest value in the history.

iex(4)> v()
:three

Providing any positive number will refer to the references we see next to each statement.

iex(5)> v(1)
:one

Negative numbers, as we saw with v(-1), will count backwards in the history from where we are.

iex(6)> v(-4)
2

See h v for more details.

Creating A PID

Often times, when invoking a function that spawns a process, the PID of the spawned process is returned and we bind to it. That PID is a reference to some BEAM process in our system.

We can create our own references using the pid/3 function.

Let’s assume we have the following processes, among others, in our system at the moment.

> Process.list |> Enum.reverse |> Enum.take(3)
[#PID<0.284.0>, #PID<0.283.0>, #PID<0.282.0>]

We can create a reference to any of them using the three number parts that they are made up of.

> pid(0, 284, 0)
#PID<0.284.0>

See, it’s alive.

> pid(0, 284, 0) |> Process.alive?
true

What if we make up a PID that doesn’t actually reference any process?

> pid(0, 333, 0) |> Process.alive?
false

Note: there is also a pid/1 version of the function. See h pid for more details.

Specifying The Phoenix Server Port

Running mix phx.server for a Phoenix project with the default settings will attach the server to port 4000.

If you’d like to use a different port in development, you can change it in config/dev.exs.

config :my_app, MyApp.Web.Endpoint,
  http: [port: 4444],
  ...

Alternatively, you can allow it to be configurable from the command line with an environment variable and a fallback port.

config :my_app, MyApp.Web.Endpoint,
  http: [port: System.get_env("PORT") || 4000],
  ...

Running

$ PORT=4444 mix phx.server

will launch the server on port 4444.

Compute md5 Hash Of A String

To compute the md5 digest of a string, we can use Erlang’s top-level md5 function.

> :erlang.md5("#myelixirstatus")
<<145, 148, 139, 99, 194, 176, 105, 18, 242, 246, 37, 69, 142, 69, 226, 199>>

This, however, gives us the result in the raw binary representation. We would like it in a base 16 encoding, as md5 digests tend to be.

We can wrap (or pipe) this with Base.encode16 to get the result we are looking for.

> Base.encode16(:erlang.md5("#myelixirstatus"), case: :lower)
"91948b63c2b06912f2f625458e45e2c7"

source

Counting Records With Ecto

Sometimes you want to know how many records there are in a table. Ecto gives us a couple ways to approach this.

We can use the count1) function that the Ecto query API provides.

> Repo.one(from p in "people", select: count(p.id))

16:09:52.759 [debug] QUERY OK source="people" db=1.6ms
SELECT count(p0."id") FROM "people" AS p0 []
168

Alternatively, we can use the fragment/1 function to use PostgreSQL’s count function.

> Repo.one(from p in "people", select: fragment("count(*)"))

16:11:19.818 [debug] QUERY OK source="people" db=1.5ms
SELECT count(*) FROM "people" AS p0 []
168

Lastly, Ecto.Repo has the aggregate/4 function which provides a :count option.

> Repo.aggregate(from(p in "people"), :count, :id)

16:11:23.786 [debug] QUERY OK source="people" db=1.7ms
SELECT count(p0."id") FROM "people" AS p0 []
168

Unique Indexes With Ecto

You can create a unique index in a migration for one or more columns using the unique_index/3 function.

For example, if you are creating a join table for followers and want to ensure that duplicate follower entries are prevented, you may want to include a unique index like so:

create table(:followers) do
  add :followed_user, references(:users), null: false
  add :following_user, references(:users), null: false
end

create unique_index(:followers, [:followed_user, :following_user])

Keep in mind that unique_index/3 is a shorthand for index/3 when you set unique: true.

Responding with :stop allows call of `terminate`

A GenServer in Elixir has two lifecycle methods, init and terminate. init is called when the GenServer is started with GenServer.start_link.

What I learned today is that there is a terminate method in case there’s any resources that need to be cleaned up when the GenServer is shut down. The terminate method is called when the :stop message is returned from either handle_cast or handle_call:

defmodule Cache do
  use GenServer
  
  #... a lot of other code

  def handle_cast({:something, 1}, state) do
    IO.puts "This executes first"
    {:stop, "This is my reason for stopping", state}
  end

  def terminate(reason, state)
    IO.puts "Then this executes"
  end
end

Creating Indexes With Ecto

Using indexes in the right places within relational databases is a great way to speed up queries.

To add a basic index in an Ecto migration, use Ecto.Migration.index\2:

create index(:users, [:email])

Creating a composite index doesn’t require jumping through any hoops; just put the relevant column names in the list:

create index(:posts, [:user_id, :title])

See h Ecto.Migration.index for more details.

Custom Phoenix Validations

Today I learned how to write a custom Phoenix model validation. It came from the Phoenix port of this very application, which requires that posts be 200 words or less.

Phoenix doesn’t have a word length validation, so I had to make my own. Here it is, minus some irrelevant lines of code:

# web/models/post.ex

def changeset(struct, params \\ %{}) do
  struct
  |> cast(params, [:title, :body, :channel_id])
  |> validate_length_of_body
end

defp validate_length_of_body(changeset) do
  body = get_field(changeset, :body)
  validate_length_of_body(changeset, body)
end

defp validate_length_of_body(changeset, body) do
  if length(String.split(body, ~r/\s+/)) > 200 do
    add_error(changeset, :body, "should be at most 200 word(s)")
  else
    changeset
  end
end

When the post body is greater than 200 words (as defined by splitting on whitespace characters), the validation adds an error to the body field that mimics the errors Phoenix provides for character-length validation failures. Including the (s), which I kind of like.

When the body is short enough, changeset is returned from the function of the same name, a form of success.

Pattern matching FTW.

Requiring Keys For Structs

When defining a struct in Elixir, we may want to ensure that certain values are provided. We can require that certain keys are used in the creation of a struct with the @enforce_keys attribute. Here is an example of a Name struct.

defmodule Name do
  @enforce_keys [:first, :last]
  defstruct [:first, :middle, :last]
end

With this defined, we can create a struct that uses all of the keys.

> jack = %Name{first: "Jack", middle: "Francis", last: "Donaghy"}
%Name{first: "Jack", last: "Donaghy", middle: "Francis"}

We can also ignore :middle and just provide the required keys.

> liz = %Name{first: "Liz", last: "Lemon"}
%Name{first: "Liz", last: "Lemon", middle: nil}

We cannot, however, omit any of the keys specified in @enforce_keys. If we do omit any of them, Elixir will raise an error.

> tracy = %Name{first: "Tracy"}
** (ArgumentError) the following keys must also be given when building struct Name: [:last]
    expanding struct: Name.__struct__/1
    iex:6: (file)

Virtual Fields With Ecto Schema

If you’d like to include a particular key-value pair in an Ecto changeset, it needs to be included as a field in the schema. In the case of something akin to a password field, you want to be able to perform validations against it, but the password itself does not have a column in the database. In other words, you want to use the password in memory as part of the validation process but not save it to the database. To accomplish this, you need to specify that it is a virtual field.

schema "users" do
  field :username, :string
  field :password_digest, :string
  field :password, :string, virtual: true
end

With that schema, you can then validate the :password and transform it into the corresponding :password_digest field.

def registration_changeset(model, params) do
  model
  |> changeset(params)                  # do other standard validations
  |> cast(params, [:password])          # include :password in the changeset
  |> validate_length(:password, min: 8) # validations
  |> put_pass_hash()                    # transform into :password_digest
end

Pivoting a 2-dimensional list

In one of the exercism.io exercises, a grid is represented by a list of lists where each inner list represents a row.

The exercise then asks you to pivot the grid, that is, transform the list of lists so that each list represents a column.

From someone else’s solution I learned that an easy way to conduct this transform is to use List.zip and Tuple.to_list.

grid = [[1,2,3, 4], [1,2,3, 4], [1, 2, 3, 4]]
List.zip(grid) 
|> Enum.map(&Tuple.to_list/1)
# [[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4]]

Tuple’s to_list method is needed because List.zip returns an array of tuples.

Merge maps with a callback

Merging two maps together is something I’m familiar with:

iex> Map.merge(%{a: 1, b: 2}, %{c: 3})
%{a: 1, b: 2, c: 3} 

But this function suffers from a unilateral decision that’s made when a key in the first argument also exists in the second argument. The value from the first map always overwrites the value from the second.

iex> Map.merge(%{a: 1, b: 2, c: 100}, %{c: 3})
%{a: 1, b: 2, c: 100}

But Elixir’s Map module has a merge function that takes a function as an additional argument. It lets you decide what to do in case of a key conflict. You could write this function so that the second argument overwrites the first or better yet add the two values together.

iex> Map.merge(%{a: 1, b: 2, c: 100}, %{c: 3}, fn(k, v1, v2) -> v1 + v2 end)
%{a: 1, b: 2, c: 103}

Three data types that go `into` a Map

Enum.into can be used to put different types of things into different types of collections. For instance I can put elements from one list into another list.

iex> Enum.into([4], [1,2,3])
[1, 2, 3, 4]

It works with Maps too and there’s different types of things than can go into a map. A map can certainly go into another map:

iex> Enum.into(%{"a" => 1}, %{"b" => 2})
%{"a" => 1, "b" => 2}

Keyword lists also go into a map:

iex> Enum.into([a: 1], %{"b" => 2})
%{:a => 1, "b" => 2}

And lists of tuples also go into a map:

iex> Enum.into([{"a", 1}], %{"b" => 2})
%{"a" => 1, "b" => 2}

But only when there are two elements in the tuple:

iex(32)> Enum.into([{"a", 1, 3}], %{"b" => 2})
** (ArgumentError) argument error
    (stdlib) :maps.from_list([{"a", 1, 3}])
    (elixir) lib/enum.ex:1072: Enum.into/2

Using assigned value later in same pattern match

I have an array of character lists.

['apple', 'aardvark']

And I want to make sure that each of these two items has the same beginning letter.

I can do this with pattern matching because variables can be reused in an assignment.

iex > [[c | _], [c | _]] = ['apple', 'aardvark']
['apple', 'aardvark']
iex > c
97

Here’s what happens when the first letter does NOT match.

iex > [[c | _], [c | _]] = ['apple', 'hamster']
** (MatchError) no match of right hand side value: ['apple', 'hamster']

Render A Template To A String

Templates in a Phoenix application ultimately get compiled to functions that can be quickly rendered with the necessary data. We can take a look at how a template will be rendered using Phoenix.View.render_to_string/3.

First, we need a template:

# user.html.eex
<h1><%= @user.first_name %></h1>
<h5><%= @user.username %> (<%= @user.email %>)</h5>

We can then render that template for the view with some user:

> user = %User{first_name: "Liz", last_name: "Lemon", username: "llemon", email: "lizlemon@nbc.com"}
%MyApp.User{...}

> Phoenix.View.Render_to_string(MyApp.UserView, "user.html", user: user)
"<h1>Liz</h1>\n<h5>llemon (lizlemon@nbc.com)</h5>\n"

Run ExUnit tests in the order they are defined

You might have a test module like this.

ExUnit.start

defmodule TestOrderTest do
  use ExUnit.Case

  test "first" do
    assert true
  end

  test "second" do
    assert true
  end
end

And when you run it, second runs before first!

In general this is fine, because every test should pass no matter which one runs first. In certain circumstances however, lets say you’re working through some Exercism.io challenges, you might want them to run in order.

To do so include the configuration seed: 0

Elixir.configure seed: 0

And tackle every test in the order they’ve been given to you!

Do You Have The Time? - Part 2

In Do You Have The Time?, I demonstrated a way of using an Erlang function to get at and work with time in Elixir. As of Elixir 1.3, there is now a Time module that provides a sigil and some functions for working with time.

We can use Elixir’s Time module to simplify the example from the previous iteration of this TIL:

defmodule TickTock do
  def current_time do
    Time.from_erl!(:erlang.time)
    |> Time.to_string
  end
end

> TickTock.current_time
"19:58:12"

Check For A Substring Match

Using Erlang’s :binary.match function, you can easily check if a string has a matching substring.

> :binary.match("all food is good", "foo")
{4, 3}
> :binary.match("all food is good", "bar")
:nomatch

As you can see, the return value on a successful match is a tuple with the index of where the match starts and the length of the match. If there is no match, the :nomatch atom is returned.

See the match/2 and match/3 docs for more details.

source

Do You Have The Time?

Elixir doesn’t come with any standard ways of getting at or working with time. There are packages like Timex out there that we can pull in to our projects. However, if we don’t have need for a full-featured date/time library, we can opt for a simpler solution.

Erlang can give us the time.

defmodule TickTock do
  def current_time do
    {hh,mm,ss} = :erlang.time
    "#{hh}:#{mm}:#{ss}"
  end
end

> TickTock.current_time
"11:47:13"

Documentation Lookup With Vim and Alchemist

Which argument position is the accumulator for Enum.reduce/3?

How does group_by work?

I find myself fairly frequently jumping from vim to Chrome to do Google searches for Elixir standard lib documentation. It gets the job done, but it is kinda slow and I’d prefer to avoid the context switch.

With alchemist.vim, Elixir documentation lookup is at your finger tips. Just move the cursor over the module or function you are curious about and hit K (from normal mode).

Curious about Enum.reduce? Type it out in your current Vim buffer, move the cursor over it, and hit K.

Updating Values In A Map

When working with maps in any language, you often need a way to update key-value pairs. Furthermore, you will need a way to handle keys that are not already present in the map, generally associating some default value.

In Elixir, the Map module provides the get_and_update/3 function as a way of accomplishing such a task.

Let’s use a score counting example to see it in action:

> scores = %{}
%{}
# jake scores a point
> {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end)
{nil, %{jake: 1}}
# chris scores a point
> {_, scores} = Map.get_and_update(scores, :chris, fn(x) -> {x, (x || 0) + 1} end)
{nil, %{chris: 1, jake: 1}}
# jake scores another point
> {_, scores} = Map.get_and_update(scores, :jake, fn(x) -> {x, (x || 0) + 1} end)
{1, %{chris: 1, jake: 2}}
# final scores
> scores
%{chris: 1, jake: 2}

We use (x || 0) + 1 as a way of providing an initial score for new keys.

The update function is expected to return a tuple with the original value and the updated value.

See the docs for more details.

Elixir Binaries and Bit Strings

Today I learned that in Elixir Binary is a sub type of Bit String with all elements using a multiple of 8 bits.

"a" # => "a"
?a # => 97
<<97>> # => "a"
is_bitstring <<97>> # => true
is_binary <<97>> # => true
is_bitstring <<97::4>> # => true
is_binary <<97::4>> # => false
is_binary <<97::16>> # => true
  • So String is a UTF-8 Binary.
  • And Binary is a multiple 8 bits Bit String.

Pipe into operators

Since Elixir operators are macros defined on Kernel or in a submodule of Kernel, we can pipe into them when we call the function on Kernel:

true |> || false # this raises a syntax error

true |> Kernel.||(false) # valid Elixir

Real world example from the Phoenix source: https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/router/helpers.ex#L31-L36

Reversing A List

To efficiently work with and transform lists in Elixir, you will likely need utilize a list reversing function from time to time. Your best bet is to reach for the Erlang implementation which is available as part of the lists module.

Here are a couple examples of how to use it:

> :lists.reverse([1,2,3])
[3, 2, 1]
> :lists.reverse([1, :a, true, "what", 5])
[5, "what", true, :a, 1]

Note: though I said “transform lists” above, what is actually going on is that a new version of the list representing my transformation is being created, per Elixir’s functional nature.

`Enum.chunk_by`

I discovered a pretty cool Elixir function this weekend, Enum.chunk_by.

Backstory: I wanted to group a list of strings by equality, while preserving the list order.

Enum.chunk_by takes an enumerable and a function, and breaks that enumerable into an enumerable when the function returns a new or different result.

Here’s a simple example:

iex> Enum.chunk_by(['A', 'A', 'A', 'B', 'A'], fn(l) -> l end)
[['A', 'A', 'A'], ['B'], ['A']]

Anytime l changes, a new list is created.

Slightly more complex:

iex> Enum.chunk_by([1, 2, 3, 4, 5], fn(n) -> rem(n, 3) == 0 end)
[[1, 2], [3], [4, 5]]

The function only returns something different (true) on 3, so 3 is assigned to its own list.

Simplify System.cmd with W sigil

Say we have the following shell command to get the the unix timestamp of the last commit:

$ git log -1 --date=short --pretty=format:%ct

1470067380

In elixir. One might do it like so:

System.cmd("git", ["log", "-1", "--date=short", "--pretty=format:%ct]) 
|> elem(0)

#=> "1470067380"

And here is just a simpler syntax for the same thing:

System.cmd("git", ~w[log -1 --date=short --pretty=format:%ct])
|> elem(0)

#=> "1470067380"

This is just a little clean and feels more like the original command.

Ruby-Like `split` in Elixir

Elixir’s split function is a bit different from the Ruby version you might be familiar with.

Here’s Ruby’s split:

2.1.0 :001 > "FOOBAR".split("")
 => ["F", "O", "O", "B", "A", "R"]

And Elixir’s:

iex(1)> String.split("FOOBAR", "")
["F", "O", "O", "B", "A", "R", ""]

Whoa, what’s that extra "" doing in there? Drink in the Elixir. A small amount of message board reading has led me to conclude this was very deliberate and isn’t going to be changed.

Here’s one way to get a Ruby-like split:

iex(1)> String.split("FOOBAR", "", trim: true)
["F", "O", "O", "B", "A", "R"]

alias is lexically scoped

Aliases are lexically scoped, allowing you to alias a module inside of a function and that alias will only exist in that function’s scope. Also TIL: You can even alias over Elixir standard lib module names.

defmodule Cartography do
  def north_america do
   alias Earth.NorthAmerica.Map
   #Map is now Earth.NorthAmerica.Map
   # You can get to the normal Map module using Elixir.Map
  end

  def foo do
    #Map is still Elixir.Map
  end
end

Named Captures with Elixir Regular Expressions

I can’t believe I’m just now learning about this! How neat!

iex(0)> string = "Here is my phone number: 999-111-1234"
"Here is my phone number: 999-111-1234"
iex(1)> regex = ~r/number: (?<phone>[\d|-]+)/
~r/number: (?<phone>[\d|-]+)/
iex(2)> Regex.named_captures(regex, string)
%{"phone" => "999-111-1234"}

Write a regex capture with ?<> in the beginning and get back a map of your captures with Regex.named_captures/2

Invoke Elixir Functions with Apply

I had a weird problem yesterday: I wanted to pass a function into another function and then invoke it, all for the sake of a friendly API.

Elixir’s apply to the rescue. Here’s the rough implementation.

defmodule Example do
  def do_things(index, rotation, operator) do
    apply(Kernel, operator, [index, rotation])
  end
end

iex> Example.do_things(10, 20, :+)
30
iex> Example.do_things(10, 20, :-)
-10

apply also accepts your own functions:

defmodule Example do
  def do_things(index) do
    apply(__MODULE__, :make_bigger, [index])
  end

  def make_bigger(a) do
    a * 10000
  end
end

iex> Example.do_things(100)
1000000

The module name (Example) will work in place of __MODULE__, if you prefer. This seems like a pretty powerful feature.

Tab Completion in IEx

IEx has tab completion of library functions, just like IRB. To see all the built-in Stream functions, just type Stream. and hit TAB, and you’ll get output like this:

iex(1) Stream.
Reducers        chunk/2         chunk/4         chunk_by/2
concat/1        concat/2        cycle/1         dedup/1
dedup_by/2      drop/2          drop_while/2    each/2
filter/2        filter_map/3    flat_map/2      interval/1
into/3          iterate/2       map/2           reject/2
repeatedly/1    resource/3      run/1           scan/2
scan/3          take/2          take_every/2    take_while/2
timer/1         transform/3     transform/4     unfold/2
uniq/2          with_index/2    zip/2

This is a nice feature when you’re experimenting in the REPL.

h/t Micah Cooper

Find and Open Port with Elixir

iex(1)> {:ok, port} = :gen_tcp.listen(0, []) #listen on an available port
{:ok, #Port<0.1465>}
iex(2)> {:ok, port_number} = :inet.port(port) #get the port number of that port
{:ok, 63470}
iex(3)> port_number #here is the port number!
63470
iex(4)> Port.close port #go ahead and close that port if you want
true

Useful for maybe automating the deployment of a plug app.

An Agent reference can be pid or name.

Each function of the Agent module takes an agent as the first argument:

get(agent, fun, timeout \\ 5000)

but this can be a couple of different things. The documentation defines the agent type as:

agent :: pid | {atom, node} | name

#The agent reference

name ::
  atom |
  {:global, term} |
  {:via, module, term}

#The agent name

So really five different types of values are valid. The pid is a value returned from calling start_link.

> {:ok, agent_pid} = Agent.start_link(fn -> [:thing] end)
> Agent.get(agent_pid, fn (state) -> hd(state) end)
:thing

The agent can be referenced by a name atom also. In the documentation example that name atom is the __MODULE__ for the module that wraps access to the agent. The name for the agent is declared by passing a name option into the start_link function call.

> Agent.start_link(fn -> [:thing] end, name: __MODULE__)
> Agent.get(__MODULE__, fn (state) -> hd(state) end)
:thing

I’m note sure about the {:global, term} and {:via, module, term} values that seem to be valid. I would love to see examples for those.

Dynamically Generating Atoms

Atoms are constants where their name is their own value.

The use of atoms like :ok and :error show up all over the place in Elixir. These are atoms that tend to be statically defined. Atoms can also be dynamically defined using string interpolation.

For example, I can generate a handful of atoms by mapping over a range of integers.

> Enum.map(1..5, &(:"some_atom_#{&1}"))
[:some_atom_1, :some_atom_2, :some_atom_3, :some_atom_4, :some_atom_5]

Note: atoms are not garbage collected. If you dynamically generate atoms in excess, you may run your VM out of heap space.

Periodic messages in Elixir

Today I needed to implement a task that happens once every 24 hours. I immediately thought, well this should be a cron job, but my co-worker suggested using the send_after method in a GenServer instead.

    Process.send_after(self, {:"$gen_cast", :get_data}, interval)

The third argument is a milliseconds argument that determines when the call will be processed. The $gen_cast is a hackish type of thing to get the GenServer to handle the call with handle_cast.

The periodism should start in the init and whenever handle_cast is called, it should schedule a new time that the message will be sent.

  def init(state) do
    work
    Process.send_after(self, {:"$gen_cast", :get_data}, interval)
    {:ok, state}
  end

  def handle_cast(:get_data, state) do
    Process.send_after(self, {:"$gen_cast", :get_data}, interval)
    work
    {:noreply, state}
  end

H/T Micah Cooper

`hd` in Guard Tests

The hd method in Elixir returns the head of a list. It’s kind of like List.first, but raises an error on an empty list.

iex(0)> hd [1,2,3]
1
iex(1)> hd []
** (ArgumentError) argument error
    :erlang.hd([])

Browsing the docs, I learned something interesting: hd is allowed in guard tests.

Here’s a contrived implementation of this behavior:

# example.ex
defmodule TestModule do
  def header(list) when hd(list) == 1, do: IO.puts "We did it"
end
iex(0)> TestModule.header([1,2,3])
We did it
:ok

When the guard’s conditions aren’t met, an error is raised:

iex(1)> TestModule.header([2,3,4])
** (FunctionClauseError) no function clause matching in TestModule.header/1
    iex:13: TestModule.header([2, 3, 4])

h/t Chris Erin & Micah Cooper

Assert An Exception Is Raised

Elixir’s ExUnit comes with a number of different ways to make assertions in your tests. One of those functions is assert_raise which allows you to test that a particular exception is raised when the given function is invoked.

Using assert_raise/2 looks something like this:

assert_raise FunctionClauseError, fn ->
  Enum.chunk([1,2,3], 0)
end

The assert_raise/3 form is also available which allows you to test both the type of exception and the resulting message.

assert_raise FunctionClauseError, ~r/^no function clause matching/, fn ->
  Enum.chunk([1,2,3], 0)
end

Using the regex sigil for the second argument is generally a good way to go to keep tests from getting too brittle.

Capture IO.puts in ExUnit tests

Whenever writing a CLI you often have to communicate something to the user via IO.puts, and while some languages make it complicated to capture output in a test, Elixir makes it extremely straight-forward.

All we have to do is import the ExUnit.CaptureIO module into our test which exposes the capture_io method.

Example

Implementation:

defmodule MyApp.CLI do
  def main(argv), do
    # parse args ...
    # return :help if the -h switch is supplied
    # pass to process
    argv
    |> parse_args
    |> process
  end
  
  def process(:help) do
    IO.puts "usage: my_app <arg1> <arg2>"
  end
end

IO capturing test:

defmodule CliTest do
  use ExUnit.Case
  
  import ExUnit.CaptureIO
  
  test "prints usage instructions when the help switch is supplied" do
    execute_main = fn ->
      MyApp.CLI.main(["-h"])
    end
    
    assert capture_io(execute_main) =~ "usage:"
  end
end

For more information read the ExUnit.CaptureIO documentation

String Interpolation With Just About Anything

Coming to Elixir from Ruby, I am used to being able to interpolate literally anything into a string. In Elixir, this is not the case.

By default, it handles strings, atoms (including nil, true, false and module name aliases like String – which are all just atoms behind the scenes), integers, floats, and some lists. That’s it.

There are two approaches you can take to interpolate everything else into a string. The easier approach is to use Kernel.inspect/2.

> IO.puts "A map #{inspect %{a: 1, b: 2}}"
A map %{a: 1, b: 2}

The other approach is to implement the String.Chars protocol for the thing that you are trying to print. You can read more about that in Elixir String Interpolation for Rubyists.

Pattern Matching In Anonymous Functions

Pattern matching shows up everywhere in Elixir, even where you may not be expecting it. When declaring an anonymous function, you can use pattern matching against different sets and shapes of input parameters to invoke different behaviors.

Here is an example of how you might use this:

> handle_result = fn
  {:ok, result} -> IO.puts "The result is #{result}"
  :error -> IO.puts "Error: couldn't find anything"
end
#Function<6.50752066/1 in :erl_eval.expr/5>

> Map.fetch(%{a: 1}, :a) |> handle_result.()
The result is 1
:ok
> Map.fetch(%{a: 1}, :b) |> handle_result.()
Error: couldn't find anything
:ok

source

Quitting IEx

There are two ways to quit out of an Interactive Elixir shell. The standard way is with Ctrl-c. This gives you a list of options, one of which is a for abort. This will terminate your IEx session and drop you back on the command line where the process started.

Additionally, IEx also understands Ctrl-\ which is control key that will terminate just about any interactive environment. This command will cause IEx to immediately exit with no prompt. Note: IEx does not, however, respond to Ctrl-d. source

Shell History in IEx

Do you like Elixir, but want history in your interactive shell, just like Ruby and other languages? I’ve got you covered.

Check out erlang-history. It’s a must-have.

Here’s the abridged version:

$ git clone https://github.com/ferd/erlang-history.git
$ cd erlang-history
$ sudo make install

Fire up IEx and enjoy.

https://github.com/ferd/erlang-history