Today I Learned

A Hashrocket project

210 posts about #elixir

Check List Membership In Elixir

You can use the in operator to check if something appears in a list. This is a handy way of checking if a variable is one of a few acceptable or expected values.

For instance, a common DateTime comparison pattern relies on this to check if a DateTime is >= or <= to another DateTime.

{:ok, datetime} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")

DateTime.compare(datetime, DateTime.utc_now()) in [:lt, :eq]

Alternatively, you can check that something does not have membership in a list by also including the not operator.

DateTime.compare(datetime, DateTime.utc_now()) not in [:lt, :eq]

Using When Clauses In A With Construct In Elixir

Because Elixir’s with construct supports the full power of the language’s pattern matching, we can use when clauses to further narrow down our matches.

For instance, if we want to match against the response to an API request, but only for response status codes in the 2xx range, we can do something like the following:

with %{status_code: code, body: body}
       when code >= 200 && code < 300 <- HTTPoison.get!(url),
     {:ok, decoded_body} <- Poison.decode(body) do
  {:ok, decoded_body}
end

See the docs for with for more details.

Match On A Map In A With Construct In Elixir

Many usage example of the with construct show a series of matches on a tuple.

with {:ok, width} <- Map.fetch(opts, :width),
     {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
end

You can match on more than just tuples though. Here is how you might match on a map.

with %{status_code: 200, body: body} <- HTTPoison.get!(url),
     {:ok, decoded_body} <- Poison.decode(body) do
  {:ok, decoded_body}
end

In fact, you have the full power of Elixir’s pattern matching available to you in your series of matches for a with construct.

Comparing DateTime Structs In Elixir

Remember, comparisons in Elixir using ==/2, >/2, </2 and friends are structural and based on the DateTime struct fields. For proper comparison between datetimes, use the compare/2 function.

As the DateTime docs say, you’ll want to use compare/2 in order to accurately compare two DateTime structs.

{:ok, older} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], "Etc/UTC")
{:ok, newer} = DateTime.from_naive(~N[2017-11-24 13:26:08.003], "Etc/UTC")

DateTime.compare(older, newer)
#=> :lt

DateTime.compare(newer, older)
#=> :gt

DateTime.compare(newer, newer)
#=> :eq

When using compare/2, you’ll get one of :lt, :gt, or :eq as a result, meaning less than, greater than, or equal respectively.

Build a CLI with Elixir

Elixir Mix ships with built-in CLI support, called escript. From the docs:

An escript is an executable that can be invoked from the command line. An escript can run on any machine that has Erlang/OTP installed and by default does not require Elixir to be installed, as Elixir is embedded as part of the escript.

Yesterday I built a CLI that reads an input file, does some calculations, and prints a result. For me, CLIs are often the mark of a fully-realized project, and I appreciate how the Elixir community treats CLIs as a first-class idea.

In lieu of a longer explanation, here are the docs:

mix escript.build

Viewing your test coverage in Elixir

Curious about test coverage in your Elixir application? Mix.Tasks.Test comes with a handy --cover option.

$ mix test --cover
...
Generating cover results ...

Percentage | Module
-----------|--------------------------
   ...
   100.00% | TilexWeb.PixelController
    75.00% | Mix.Tasks.Deploy
    30.00% | TilexWeb
   ...
-----------|--------------------------
    68.33% | Total

By default it uses a wrapper around OTPs built in cover, however that’s configurable. If you wanted to use something like excoveralls you could:

def project() do
  [
    ...
    test_coverage: [tool: ExCoveralls]
    ...
  ]
end

Other niceties, like color based thresholds, can be found in the docs

Pattern match against Dates in Elixir using struct

A recent TIL by my coworker Ryan reminded me of an alternative to pattern matching on dates (and other structs) by matching on their Name. Using __struct__ to grab the name allows us to use a guard and only match on struct types we want:

def foo(%{__struct__: struct_name} = datetime)
  when struct_name in [Date, DateTime] do
    Timex.to_naive_datetime(datetime) |> foo
end

def foo(%NaiveDateTime{} = datetime) do
  IO.inspect({"My Naive Datetime", datetime})
end

Pattern matching with `Kernel.match`

Pattern matching is powerful, but when iterating over a list with an Enum function you must allow all variants of the list to be processed. So this fails:

sample = [{1, "A"}, {2, "B"}]
 > Enum.filter(sample, fn ({_, "B"}) -> true end)
** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/1

    The following arguments were given to :erl_eval."-inside-an-interpreted-fun-"/1:

        # 1
        {1, "A"}

    (stdlib) :erl_eval."-inside-an-interpreted-fun-"/1
    (stdlib) erl_eval.erl:826: :erl_eval.eval_fun/6
    (elixir) lib/enum.ex:2898: Enum.filter_list/2

Instead of using pattern matching here we can just use an anonymous function that takes all args and makes a comparison.

sample = [{1, "A"}, {2, "B"}]
Enum.filter(sample, fn ({_, letter}) -> letter == "B" end)
# [{2, "B"}]

But the cool way to do it is with Kernel.match?. Which according to the docs is:

A convenience macro that checks if the right side (an expression) matches the left side (a pattern).

What that looks like:

sample = [{1, "A"}, {2, "B"}]
Enum.filter(sample, &match?({_, "B"}, &1))
# [{2, "B"}]

H/T Taylor Mock

Pattern matching against Dates in Elixir

Dates in Elixir aren’t native values, so there aren’t any guard clause functions available for use with date/datetime. You can, however, use pattern matching:

def foo(%Date{} = date) do
  Timex.to_naive_datetime(date) |> foo
end

def foo(%DateTime{} = datetime) do
  Timex.to_naive_datetime(datetime) |> foo
end

def foo(%NaiveDateTime{} = datetime) do
  IO.inspect({"My Naive Datetime", datetime})
end

Serialize an Elixir pid

I was wondering how to serialize/deserialize a pid for some tricks with Ecto Sandbox and I found out some nice code on phoenix_ecto code. Here it goes my version:

defmodule MyApp.Serializer do
  @spec serialize(term) :: binary
  def serialize(term) do
    term
    |> :erlang.term_to_binary()
    |> Base.url_encode64()
  end

  @spec deserialize(binary) :: term
  def deserialize(str) when is_binary(str) do
    str
    |> Base.url_decode64!()
    |> :erlang.binary_to_term()
  end
end

And the test:

defmodule MyApp.SerializerTest do
  use ExUnit.Case, async: true

  alias MyApp.Serializer

  describe "serialize/1" do
    test "serializes a pid" do
      pid = self()
      assert pid |> Serializer.serialize() |> is_binary()
    end

    test "serializes a map" do
      map = %{foo: :bar}
      assert map |> Serializer.serialize() |> is_binary()
    end
  end

  describe "serialize/1, deserialize/1" do
    test "serializes and deserializes a pid" do
      pid = self()
      assert pid |> Serializer.serialize() |> Serializer.deserialize() == pid
    end

    test "serializes and deserializes a map" do
      map = %{foo: :bar}
      assert map |> Serializer.serialize() |> Serializer.deserialize() == map
    end
  end
end

Send an event to a Channel from outside Phoenix

So this one was non-obvious to me.

In the following example, any module on any process can call TopicChannel.send_to_channel/1and that will be handled by the handle_info call below and sent to the socket.

defmodule MyAppWeb.TopicChannel do
  use Phoenix.Channel

  def send_to_channel(data) do
    Phoenix.PubSub.broadcast(
      MyApp.PubSub, 
      "topic:subtopic",
      %{type: "action", payload: %{data: data}
    )
  end

  def join("topic:subtopic", message, socket) do
    {:ok, socket}
  end
  
  def handle_info(%{type: "action"}=info, socket) do
    push socket, "action", info

    {:noreply, socket}
  end
end

Parameter filtering in Elixir Phoenix logs

Sometimes you want to filter out parameters that are being logged (think GDPR compliance). Enter Phoenix’s handy config filter_parameters :

config :phoenix, :filter_parameters, ["password", "birthday"]

Or if you want to be extra safe, you can filter all parameters and only whitelist ones you want to see. For example: id and order:

config :phoenix, :filter_parameters, {:keep, ["id", "order"]}

Enable gzip for all phoenix responses

Enabling gzip for static assets in phoenix couldn’t be simpler. In lib/myapp_web/endpoint.ex change gzip from false to true:

 plug(Plug.Static, at: "/", from: :tilex, gzip: true, only: ~w(assets ...))

But why stop there? We can compress our dynamic document bodies just as easily. In config/prod.exs, add compress: true to the http config of the endpoint.

config :my_app, MyAppWeb.Endpoint,
  http: [port: {:system, "PORT"}, compress: true]

Client Connection Vars with Ecto

Postgres has dozens of connection variables it will take, a couple of my favorite ones are statement_timeout and application_name.

Ecto takes a parameters connection option, which is a keyword list of connection parameters.

# dev.exs

...

# Configure your database
config :myapp, Myapp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "myapp_dev",
  socket_dir: "/var/run/postgresql",
  parameters: [application_name: "My App Development", statement_timeout: "5000"],
  pool_size: 10



Example of statement_timeout being respected in Ecto:

Ecto.Adapters.SQL.query!(Myapp.Repo, "select pg_sleep(86400)")

** (Postgrex.Error) ERROR 57014 (query_canceled): canceling statement due to statement timeout
    (ecto) lib/ecto/adapters/sql.ex:200: Ecto.Adapters.SQL.query!/5


Client Connection Variables references:https://www.postgresql.org/docs/current/static/runtime-config-client.html

Use a unix socket with Ecto

Ecto allows the use of a unix socket to connect to postgres, instead of TCP. In order to use a unix socket in a exs file, you should remove the hostname list item, and provide the socket_dir list item.

# dev.exs

...

# Configure your database
config :myapp, Myapp.Repo,
  adapter: Ecto.Adapters.Postgres,
  database: "myapp_dev",
  socket_dir: "/var/run/postgresql",
  pool_size: 10

Elixir String Manipulation

To get the list of string that compose a longer string, use String.codepoints/1

iex> cdp =  String.codepoints("abcdefg")
["a", "b", "c", "d", "e", "f", "g"]
iex> Enum.at(cdp, 0)
"a"

To get the list of codepoints that represent each letter in the string, use String.to_charlist/1

iex> chr = String.to_charlist("abcdefg")
'abcdefg'
iex> Enum.at(chr, 0)
97

Shell Out With Elixir

Today I wrote a staging and production deploy script for Tilex, learning about System.cmd/3 along the way. The first argument to this function is the command, the second are arguments, and the third are options.

Here’s an example implementation from our script that deletes a Git tag and pushes that updated reference to origin:

System.cmd("git", ["tag", "-d", "staging"])
System.cmd("git", ["push", "origin", ":refs/tags/staging"])

Get application's current version in production

If you use Distillery to produce Elixir releases for production you may be in a situation where your application is deployed and running but you are not sure what version is loaded into memory.

To verify the version:

  1. SSH into your server
  2. navigate into the bin directory of your release (e.g. app_release/your_project/bin)
  3. run the remote console ./your_project remote_console
  4. Run the spec function on Application: Application.spec(:your_project, :vsn)

You will get back a string such as ‘0.0.1’.

Mix xref and Elixir compiler warnings

The release of Elixir v1.3 added a mix task called xref for performing cross reference checks of your code. mix xref gives us some handy tools!

Find unreachable code:

❯ mix xref unreachable
lib/exonk8s/foo.ex:16: SomeModule.non_existant/0

Find callers of modules and functions:

❯ mix xref callers SomeModule
lib/exonk8s/foo.ex:16: SomeModule.non_existant/0

Generate a dependency graph:

> mix xref graph
lib/exonk8s.ex
├── lib/exonk8s/repo.ex
└── lib/router.ex
    └── lib/exonk8s/musician_controller.ex (compile)
        ├── lib/exonk8s/foo.ex
        ├── lib/exonk8s/musician.ex
        └── lib/exonk8s/repo.ex
...snipped for brevity

The release of Elixir v1.6 added the module attribute @deprecated to enable library authors to warn users of deprecated functions.

❯ mix xref deprecated
Compiling 2 files (.ex)
Foo Loaded
warning: ExOnK8s.Foo.lazy/0 is deprecated. You should be more eager.
  lib/exonk8s/musician_controller.ex:10

lib/exonk8s/musician_controller.ex:10: ExOnK8s.Foo.lazy/0

Lastly I should note that xref deprecated and xref unreachable are included in the compiler warnings, so you you’ve likely seen these at work even if you didn’t know they were there.

Learn more

Add Elixir files to your compiled list

While working on an Elixir package today, I wanted to add the test/support directory to my compiled files. I knew the Phoenix Framework did this so I went there to take a look and discovered elixirc_paths.

The Phoenix frameworks example looks like this:

  # mix.exs
  def project do
    [
      ...
      elixirc_paths: elixirc_paths(Mix.env),
      ...
    ]
  end
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

Perfect! Now we can add additional directories to our compiled path that are environment specific.

Your slowest Elixir tests

Sometimes its handy to see what tests are taking the longest in an Elixir project.

Enter mix test --slowest N

Just replace N with how many slow tests you would like to see and you’ll get some handy output:

$ mix test --slowest 5

Top 5 slowest (17.6s), 30.2% of total time:
  * test enters a body that is too long (4504.4ms) (DeveloperCreatesPostTest)
  * test the page does not have a Create Post link (4384.9ms) (VisitorVisitsHomepageTest)
  * test and clicks 'like' for that post (3944.2ms) (VisitorViewsPostTest)
  * test fills out form and updates post from post show (2558.6ms) (DeveloperEditsPostTest)
  * test fills out form and submits (2255.5ms) (DeveloperCreatesPostTest)

Phoenix Select Form Helper

The select form helper allows you to easily add a select input to your forms.

= form_for @changeset, resource_path(@conn, :create), fn f ->
  = select f, :book_id, @books
  = submit "Save Post", class: "btn"

Among the types of arguments that the select helper can accept are two-item tuples. The first item in the tuple is used as the label for the option and the second item is used as the value for the option.

Media.list_books returns a list of structs representing all of the books in the database. We’ll need to get from a list of structs to a list of tuples.

To do that, we’ll pipe the list of Book structs to Enum.map and use an anonymous function to generate two-value tuples from those structs.

def new(conn, _params)
  ...
  books =
    Media.list_books
      |> Enum.map(&{"#{&1.title} by #{&1.author}", &1.id})

  render conn, "new.html", changeset: changeset, books: books
end

Aliasing an Elixir Module Within Itself

I was attempting to compile a Phoenix application and I got this error:

Post.__struct__/0 is undefined, cannot expand struct Post

The issue was in a function I defined in the module.

def changeset(%Post{}= post, attrs \\ %{}) do
    ...
end

I assumed that you would get references to a module within said module for free. That’s not the case. There are two ways to fix the error. One is to use the full module name in the parameter list.

def changeset(%Forum.Post{}= post, attrs \\ %{}) do
    ...
end

Alternatively, I can alias the module within itself.

alias Forum.Post

Comply with the erlang IO protocol

Below is a basic IO server/device implementation, it receives an :io_request message and responds with a :io_reply message.

defmodule MYIODevice do
  def listen() do
    receive do
      {:io_request, from, reply_as, {:put_chars, :unicode, message}} ->
        send(from, {:io_reply, reply_as, :ok})
        IO.puts(message)
        IO.puts("I see you")
        listen()
    end
  end
end

pid = spawn_link(MYIODevice, :listen, [])

IO.puts(pid, "Hey there")

The above code outputs the following to stdout:

Hey there
I see you

The first argument of IO.puts/2 is a pid representing the device that is being written to, it is generally defaulted to :stdio)

The documentation is dense, enjoy! Erlang IO Protocol

H/T Brian Dunn

Checking that an association is loaded

Ecto will NOT load associations automatically, that is something that you must do explicitly and sometimes you might expect an association to be loaded but because of the code path travelled, it is not.

You can check to see if an association is loaded with Ecto.assoc_loaded?

case Ecto.assoc_loaded?(post.channel) do
  true -> IO.puts('yep its loaded')
  false -> IO.puts('you do not have the data')
end

Mix tasks accessing the db with `Mix.Ecto`

UPDATE: ensure_started is no longer available. Try Application.ensure_all_started(:tilex), where tilex is the module/atom that represents your application.


When running reports or one-off data operations it might be necessary to create a mix task that can access the database. Ecto provides convenience functions in the Mix.Ecto module to help facilitate setting up and starting the Ecto repos.

The function parse_repo(args) will process the arguments used when calling the mix task. It looks specifically for -r MyApp.Repo but if you don’t pass anything it will return all the repos from the configuration.

The function ensure_started(repo) takes the repo as an argument ensures that the Repo application has been started. Without calling this function the Repo will throw an error when used.

Put it all together:

defmodule Mix.Tasks.MyApp.SayHi do
  use Mix.Task
  import Mix.Ecto

  def run(args) do
    repo = parse_repo(args) |> hd

    ensure_started(repo)

    result = repo.query("select 'hi!';")

    result.rows
    |> hd
    |> hd
    |> IO.puts
  end
end

Print Information about an Elixir Data Type

IEx ships with a neat feature, def i(term \\ v(-1)), which provides information about an argument. Here are two examples:

iex(1)> i ["one", "two"]
Term
  ["one", "two"]
Data type
  List
Reference modules
  List
Implemented protocols
  IEx.Info, Collectable, Enumerable, Inspect, List.Chars, String.Chars
iex(1)> i "three"
Term
  "three"
Data type
  BitString
Byte size
  5
Description
  This is a string: a UTF-8 encoded binary. It's printed surrounded by
  "double quotes" because all UTF-8 encoded codepoints in it are printable.
Raw representation
  <<116, 104, 114, 101, 101>>
Reference modules
  String, :binary
Implemented protocols
  IEx.Info, Collectable, Inspect, List.Chars, String.Chars

Use this to inspect an item, or learn more about Elixir.

`with` statement has an `else` clause

with statements are used to ensure a specific result from a function or series of functions, using the results of those functions to take actions within the with block. If a function does not return a specific result (think :error instead of :ok) then you can either define specific clauses for the things you expected to go wrong, or you can just return the result that did not conform to the with clause expectations.

This is the general form:

with {:ok, a} <- {:ok, 123} do
  IO.puts "Everythings OK"
end

A with with an else block:

with {:ok, a} <- {:error, 123} do
  IO.puts "Everythings OK"
else 
  result -> IO.puts("Not OK")
end

A with else clause with pattern matching:

with {:ok, a} <- {:error, "something went wrong"} do
  IO.puts "Everythings OK"
else 
  {:error, message} -> IO.puts(message)
  error -> IO.puts("I'm not sure what went wrong
end

A with without an else clause where the error is returned from the with block:

result = with {:ok, a} <- {:error, "something went wrong"} do
  IO.puts "Everythings OK"
end

{:error, message} = result
IO.puts "This went wrong #{message}"

Exit IEx Gracefully with `respawn()`

I love a debugger in my Elixir code. A pattern I follow is to load and call IEx in a Phoenix template or controller with:

require IEx; IEx.pry;

Then, I call functions and check data in the middle of an action, by starting my Phoenix server with the following command:

$ iex -S mix phx.server

Once finished, I usually end up just killing the server with CTRL-C.

Today I learned there’s a better way: respawn().

respawn() respawns the current shell by starting a new shell process. Which I think is a good thing. It lets me back out of my IEx session without killing my server, a much more graceful development experience.

View your outdated packages

To see which packages in a mix app need updating, you can run mix hex.outdated

$ mix hex.outdated
Dependency           Current  Latest  Update possible
appsignal            1.3.2    1.3.3   Yes
basic_auth           2.1.4    2.1.4
cachex               2.1.0    2.1.0
...

It prints out a handy table letting you quickly view the current and latest versions and if they can be updated. If Update possible is No, check your semantic version lockdown of that package in mix.exs.

mix hex.outdated accepts a few arguments:

  • --all which shows all outdated packages, including children of packages defined in mix.exs
  • --pre which include pre-releases when checking for newer versions (be adventurous!)

Happy updating!

Generate New Phoenix App Without Brunch

By default when you create a new Phoenix app using phx.new, a set of files and configurations will be generated for Brunch. Though the Phoenix team decided to use Brunch, you don’t have to. You may not want Phoenix to handle asset building or you may just prefer another build tool. Either way, if you’d like to opt out, you can include the --no-brunch flag when generating the project.

$ mix phx.new --no-brunch my_app

If you have an existing project that you’d like to remove Brunch from, there is some information in Phoenix’s Static Assets documentation.

Visualize Your Elixir Dependencies

To visualize your Elixir dependencies, try this:

$ mix deps.tree

This prints a tree showing your dependencies (and their dependencies):

$ mix deps.tree
tilex
├── gettext ~> 0.13 (Hex package)
├── hackney 1.8.0 (Hex package)
│   ├── certifi 1.1.0 (Hex package)
│   ├── idna 4.0.0 (Hex package)
│   ├── metrics 1.0.1 (Hex package)
│   ├── mimerl 1.0.2 (Hex package)
│   └── ssl_verify_fun 1.1.1 (Hex package)

This is really handy when trying to track down where a deprecation warning or error is coming from.

h/t Dorian Karter

Serve Static Assets From Custom Phoenix Directory

When you new up a Phoenix project, an endpoint.ex file will be generated. This file is full of different plugs for handling incoming traffic. The Plug.Static declaration specifies how your application will handle and serve requests for static files.

  plug Plug.Static,
    at: "/", from: :my_app, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

The from options declares where these static files are located. In this case it references our application (:my_app) as the target which will translate to its priv/static directory.

If you instead want to serve your files from a different, custom directory, you can replace it with the path to that directory.

  plug Plug.Static,
    at: "/", from: "priv/my_frontend/static", gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

source

Implied applications and `extra_applications`

In your mix file (mix.exs) the application function returns a keyword list. Two options in that list determine what applications are started at runtime.

The applications is by default implied based on your app’s dependencies. The default list is thrown away through if this option is set in your mix.exs file.

If you want to add an extra application without disrupting the default, implied list then you can add the optionextra_applications. This leaves the default, implied list of applications untouched.

H/T Jose Valim PR

def application do
[
  mod: {Tilex, []},
  extra_applications: [:logger]
]
end

Specifying The Digest Directory For Phoenix

By default, Phoenix targets priv/static when preparing digested assets for production. This process happens when running mix phx.digest.

If you are doing some custom work with your assets such that they are in a different location, you’ll need to tell Phoenix where to look. To do this, just include an optional path argument.

$ mix phx.digest path/to/my/assets

The digests will be put in that target directory. If you’d like to specify a different output directory, such as priv/static, include the -o flag.

$ mix phx.digest path/to/my/assets -o priv/static

Joining URI parts in Elixir

Elixir 1.3 introduced a standard way to join URIs.

For example, say we have a base URI for an API: https://api.hashrocket.com and different endpoints on that URI: events, developers, applications.

To join the URI into one properly formatted string:

def endpoint_uri(endpoint) do
  "https://api.hashrocket.com"
  |> URI.merge(endpoint)
  |> URI.to_string()
end

# then call it

endpoint_url("events") # => "https://api.hashrocket.com/events"

URI.merge accepts both strings and URI structs as the first object so you can easily continue adding URI parts to the pipeline including query params:

"https://test.com"
|> URI.merge("events") 
|> URI.merge("?date=today") 
|> URI.to_string()

# => "https://test.com/events?date=today"

Conditional Variables in Phoenix Templates

A common Ruby on Rails technique is setting instance variables in a controller action, then using them in the view layer:

# app/controllers/users_controller.rb
def show
  @show_button = true
end
# app/views/users/show.html.haml
- if @show_button
  .button

Doing the same thing in Elixir/Phoenix is a little different.

Since Phoenix 0.14.0, the framework raises on missing assigns (link). Thus our conditional will fail on any loaded template whose controller function that does not define the variable.

One solution is to check if the variable is assigned:

# lib/my_app_web/templates/users/show.html.eex
<%= if assigns[:show_button] do %>
  <div class="button"></div>

If show_button is assigned in your controller function (via assign), the button will be displayed. If not, the button will not be displayed, and the application will not raise an error.

Run an Elixir function on Module load

If you want to run a function on Module load, you can turn to Modules @on_load hook.

#hello_world.ex
defmodule Example do
  @on_load :hello_world

  def hello_world do
    IO.puts("Hello World!")
    :ok
  end
end
$ elixir hello_world.ex
Hello World!

Your function must return :ok otherwise the module load will be aborted.

Using @on_load can be an efficient way to run examples, quick scripts, setup requirements, benchmarking or load in libraries from other languages.

Converting strings to atoms safely

If your elixir system accepts any outside inputs and takes any part of those outside inputs and calls String.to_atom with the input as an argument then your elixir system is subject to a denial of service attack.

Malicious actors can submit input designed to dynamically create a large number of atoms until the atom limit is reached, knocking out your elixir applications.

Consider using String.to_existing_atom instead. If the argument to this function cannot be converted to an existing atom then an exception will be thrown.

> String.to_existing_atom("I don't exist")
** (ArgumentError) argument error
    :erlang.binary_to_existing_atom("nothere", :utf8)
> String.to_atom("I don't exist")
:"I don't exist"
> String.to_existing_atom("I don't exist")
:"I don't exist"

Current number of atoms in the atoms table

In Elixir and Erlang there is a hard limit on the number of atoms you can create. Atoms are not garbage collected so its important to ensure you don’t exceed the limit. You can check what the limit is with:

> :erlang.system_info(:atom_limit)
1048576

Likewise, you can check the current number of atoms in the atoms table with:

> :erlang.system_info(:atom_count)
9654

On my system using Elixir 1.5.1 I use 9654 atoms just to start iex.

Magically insert `iex -S` in front of a command

Often times you need to execute an elixir function with iex to enable pry breakpoints.

I found that I was doing a lot of fumbling in zsh to go back to the previous command, jump to the beginning of it and type out iex -S.

Since I like to automate repeatitive processes, I came up with this:

bindkey -s "^Xi" "^[Iiex -S ^[A"

Dump this line in your .zshrc or .bashrc and then all you have to do is Ctrl+xi to insert iex -S in front of the previously ran command.

preview

Magic. 🎩