fceruti
Small useful macro |> case_continue
I just wanted to share with you a small macro that has cleaned up many many lines of code for me.
Basically in my contexts I had this kind of code everywhere
|> Repo.transaction()
|> case do
{:ok, %{goal: goal}} = result ->
PubSub.broadcast_goal_created(user, goal)
result
error ->
error
end
A possible solution would be to add maybe functions
|> Repo.transaction()
|> maybe_broadcast_goal_created(user)
..
def maybe_broadcast_goal_created({:ok, %{goal: goal}} = result, user) do
PubSub.broadcast_goal_created(user, goal)
result
end
def maybe_broadcast_goal_created(result, user), do: result
Which is not ideal since I’d have to create one pair for each function in context.
Macros to the rescue!
Inspired by how IO.inspect prints and keeps moving, I created case_continue, which runs case, ignores it’s result and continues the pipe as if it was never there.
defmodule Timetask.Helpers.Macros do
defmacro case_continue(prev, do: block) do
quote do
case unquote(prev) do
unquote(block ++ [{:->, [], [[{:_, [], nil}], nil]}])
end
unquote(prev)
end
end
end
Now I can write it like this.
|> Repo.transaction()
|> case_continue do
{:ok, %{goal: goal}} -> PubSub.broadcast_goal_created(user, goal)
end
Much nicer huh? What do you think? Do you like the name? I also had in mind case_bridge or case_skip.
Most Liked
RudManusachi
-
Piping into
caselately became a subject for debates
(see don’t pipe into case statements) -
Macros are awesome! ..mostly for stuff I do on my own

Especially adding up to the language syntax meant to be commonly used across the project might not be obvious to the team who work with the code.Macros should only be used as a last resort. Remember that explicit is better than implicit . Clear code is better than concise code.
_https://elixir-lang.org/getting-started/meta/macros.html#foreword_
Whenever I notice case of just two branches one of which just returns the input - I think of with
result =
...
|> Repo.transaction()
with {:ok, %{goal: goal}} <- result do
PubSub.broadcast_goal_created(user, goal)
result
end
With your macro we can handle more than just one happy path, though.
al2o3cr
I don’t find the error -> boilerplate that annoying, personally. I always prefer to unpack results (both successand error) from passing Ecto.Multi to Repo.transaction as close to the transaction as possible, because otherwise operation names become an accidental part of the function’s interface.
I’m also not sure how the above macro accomplishes the task - it seems like a result other than {:ok, %{goal: _}} will fail to match in the generated case statement.
Sebb
Where is the debate? This just says: dont do it!
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
- #javascript
- #code-sync
- #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








