fceruti
Macro Monday - Share your macros!
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
Now it’s your turn!
Most Liked
Nicd
I don’t like macros. They’re obscure and inscrutable and irritating, and they get everywhere.
That said, I have two macros that I use to add type information to my code, my typed struct and typed Ecto schema macros. The latter is ugly and incomplete, but the former I can link for you: lib/geo_therminator/typed_struct.ex · 8fa72cbfa5b0402c46c53e13e5e994bf5a390f89 · Mikko Ahlroth / GeoTherminator · GitLab
I use it like this then:
defmodule Register do
deftypedstruct(%{
register_name: String.t(),
register_value: {integer(), 0},
timestamp: DateTime.t()
})
end
It defines the struct, a @type t, and @enforce_keys for me all in one go. I copy it to every new project I start.
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









