I had a wonderful time giving a talk at the Elixir January Tech Meetup here in Toronto. Big thanks to Mattia for organizing and PagerDuty for hosting the meetup!
I wanted to capture the talk in a blog post and here it is.
1. Canada
Many of us have used cancan for authorization in our Rails applications. When I was searching for a similar package in Elixir, I found the awesome canada package.
It’s DSL is pretty straightforward
1 | # In this example we have a User and a Post entity. |
When using packages, I try to take a peek at the source code and
understand how things work. And, I was shocked when I saw just 10 lines of
code in the lib
folder! See for yourself:
1 | # lib/canada.ex |
The protocol is what allows you to define your custom rules for authorization
and the Canada
module defines a neat little macro which allows you to test if
a user is authorized to perform an action using syntax like: can? user,
read(post)
. How cool is that!
2. Readable binary match specs
Postgrex is another one of those packages which is filled with neat Elixir code. When I was skimming through the code, I ran into a piece of code which surprised me:
1 | defmodule Postgrex.BinaryUtils do |
I was having a difficult time understanding how signed-64
could be valid
Elixir code. I quickly spun up an iex console and typed in signed-64
and
unsurprisingly it threw an error. Upon further searching I found that this was
actually used in binary pattern matches all over the code:
1 | defmodule Postgrex.Messages do |
So, the macro int32
would actually be spliced inside of a binary pattern
match. I would never have thought of doing this! And it makes the code so much
more readable and easy to follow.
3. Compiling lookup tables in Modules
While browsing through postgrex, I found a text file called errcodes.txt
which
I thought was a bit strange. Here is a snippet of that file:
1 | # |
This file maps error codes to their symbols. The reason this was in the lib
folder was because it was supposed to be used as a source for error codes
mapping. Upon further reading I found that this was being used in a module
called Postgrex.ErrorCode
. Here are the interesting pieces of that module:
1 | defmodule Postgrex.ErrorCode do |
This code file uses our errorcodes text file to define around 400 functions which
embed the actual code to name mapping. And whenever you wanted to do the actual lookup you could just use
Postgrex.ErrorCode.code_to_name(error_code)
4. Validating UUIDs
Did you know that you don’t need the uuid
package to generate UUIDs? UUID
generation is available in Ecto as part of the Ecto.UUID
module. And it even
has a function which allows you to validate a UUID. Most of us would quickly
reach for a regex pattern to validate a UUID, However, the Ecto library uses an
interesting approach:
1 | defmodule Ecto.UUID do |
This code is pretty self explanatory and is a literal translation of how you would validate a UUID using a pen and paper.
5. Honorable Mentions
Static struct assertions/checks in functions
With Elixir you can assert that the argument your function receives is of a specific type by using a pattern like below:
1 | defmodule User do |
This code would blow up if the argument passed was not a User
struct. This is
a nice way of asserting the type. However, you can overdo this by using it
everywhere. A good rule of thumb is to use this pattern in your public API at
the periphery where data comes in.
Tagged with blocks
You can wrap your with
matches in tagged tuples like below if you want to
handle errors differently for different failures.
1 | with {:parse, {:ok, user_attrs}} <- {:parse, Jason.parse(body)}, |
Delegating function calls on your root API module
defdelegate
allows you to delegate function calls to a different module using
the same arguments.
1 | defmodule API do |
Enforcing Keys
While defining a struct you can also define which keys are mandatory.
1 | defmodule User do |
Interpolation in docs
1 | defmodule HTTPClient do |
Suppressing logs in your tests
1 | ExUnit.start |