fceruti

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

RudManusachi

  1. Piping into case lately became a subject for debates :smiling_imp: (see don’t pipe into case statements)

  2. Macros are awesome! ..mostly for stuff I do on my own :neutral_face:
    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

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

Sebb

Where is the debate? This just says: dont do it!

Where Next?

Popular in Discussions Top

vans163
So useless benchmarks aside, Its possible to write a webserver that can serve 300k requests per second (perhaps more with optimizations)....
New
cvkmohan
The upcoming Phoenix 1.6 release looks very interesting. Became a habit to watch the commits - and - what they are bringing in. phx.gen...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
thojanssens1
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.: redirect "/", UserController, ...
New
sashaafm
Piggy backing a bit on @dvcrn topic BEAM optimization for functions with static return type?, I’ve been trying to understand in a deeper ...
New
chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
Qqwy
I would like to spark a discussion about the static access operator: .. For whom does not know: it is used in Elixir to access fields of...
New
rms.mrcs
A couple of days ago I was discussing with a friend about different approaches to write microservices. He said that if he was going to w...
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New
Markusxmr
Since Drab has been developed for a while in the open, introducing the Liveview functionality in a way it happend appears to undermine th...
New

Other popular topics Top

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement