Happy Macro Monday to everyone!
I was wonder what kinds of crafty macros you think are awesome and have come to rely upon.
I’ll go first:
Sigil m
defmacro sigil_m({:<<>>, _line, [string]}, []) do
spec =
string
|> String.split()
|> Stream.map(&String.to_atom/1)
|> Enum.map(&{&1, {&1, [], nil}})
{:%{}, [], spec}
end
This one I copy-pasted from the web (can’t remember the origin) and it’s truly a killer in readability and speed of development.
I use it in many places, but on tests & liveview really shines:
Tests
test "happy path", ~%{user: user, project: project, task_1: task_1, task_2: task_2} do
test "happy path", ~m{user project task_1 task_2} do
Liveview
%{current_user: current_user, project: project} = socket.assigns
~m{current_user project} = socket.assigns
Maybe Case
This one is “my own creation” (quotes apply as I received tons of help).
Think tap/1
meets case
.
defmacro maybe_case(prev, do: block) do
quote do
prev = unquote(prev)
case prev do
unquote(block ++ [{:->, [], [[{:_, [], nil}], nil]}])
end
prev
end
end
The use I have for this is the following. In my context layer I have all this functions such as schedule_task
, unschedule_task
etc, which in principle all look something like this:
Multi.new()
|> Multi.run(...)
|> Multi.run(...)
|> Multi.run(...)
|> Repo.transaction()
|> case do
{:ok, %{action: ...}} -> {:ok, ...}
{:ok, %{action_2: ...}} -> {:ok, ...}
{:error, %{action: ...}} -> {:error, ...}
end
That’s great until you want to introduce side effects like logging and pubsubing. I could of course capture the result of the transaction, make conditional statements for publish & log and then shape the response.
results = Multi.new()
|> Multi.run(...)
|> Multi.run(...)
|> Multi.run(...)
|> Repo.transaction()
case results do
{:ok, _, _} -> PubSub.publish_change()
{:error, _, _} -> Logger.error()
end
case results do
{:ok, %{action: ...}} -> {:ok, ...}
{:ok, %{action_2: ...}} -> {:ok, ...}
{:error, %{action: ...}} -> {:error, ...}
end
But being a pipe-man myself, I decided I needed my code to look like this:
Multi.new()
|> Multi.run(...)
|> Multi.run(...)
|> Multi.run(...)
|> Repo.transaction()
|> maybe_case do
{:ok, _} -> PubSubs.publish_change(...)
{:error, _} -> Logger.error(...)
end
|> case do
{:ok, %{action: ...}} -> {:ok, ...}
{:ok, %{action_2: ...}} -> {:ok, ...}
{:error, %{action: ...}} -> {:error, ...}
end