elixir - how to add conditional pipe into pipeline?
Asked Answered
I

4

16

I have a small pipeline in elixir, it's about changing ecto model state:

model
|> cast(params, ~w(something), ~w())
|> conditional
|> Repo.update

The problem is that I have conditional pipe which can be nil sometimes, so in the case it's nil it should do nothing and can works (I presume it will be fn(x) -> x end)

So, my question is: "how can I do that"?

Irreligion answered 7/5, 2016 at 4:33 Comment(1)
So conditional is either a function or nil?Adamec
M
25
model
|> cast(params, ~w(something), ~w())
|> maybe_do_something(conditional)
|> Repo.update

defp maybe_do_something(changeset, nil), do: changeset

defp maybe_do_something(changeset, func) do
  # Do something with changeset
end

Not sure if I got your question right, but maybe thats what you are looking for.

Massimo answered 7/5, 2016 at 5:7 Comment(0)
A
9

Pipes are great for operations that can't fail and all of them always will be carried. In case you want to stop the pipeline, you can't. You would have to write functions like this:

maybe_repo_update(nil), do: nil
maybe_repo_update(data), do: Repo.update(data)

To solve that problem there is a new special form in Elixir 1.2 called with. It can stop the pipeline at the moment where something doesn't match:

with changeset <- cast(model, params, ~w(something), ~w())
  {:ok, changeset} <- conditional_operation(changeset)
  {:ok, model} <- Repo.insert(changeset)

This will make sure that if conditional operation returns something else than {:ok, changeset} it will not try to run the last repo insert. In Elixir 1.3 you can also use else part.

However for changesets it is more common to use solution suggested by @JustMichael:

def conditional(changeset) do
  if something_to_do do
    transform(changeset)
  else
    changeset
  end
end

This solution will always run the Repo.update part.

Alliteration answered 7/5, 2016 at 10:1 Comment(0)
F
2

I'm Elixir newbie, so please don't be too harsh :).

Why not use anonymous functions for this purpose ?

model
|> cast(params, ~w(something), ~w())
|> (fn(n) -> conditional && n |> Repo.update || n end).()
Falgout answered 13/12, 2017 at 15:48 Comment(1)
It's a good question 😉 In Elixir and other functional programming communities (like Elm), there's a general encouragement towards being explicit and easily readable. There's a good video on this general topic here: youtube.com/watch?v=trgET9YU37MLehr
B
0

In 2021, Elixir v1.12 introduced then/2.

Thanks to this function, we can now easily insert a conditional pipe into pipeline.

Consider the following code:

x = ... # true or false

obj
|> foo()
|> bar()

If you want to run baz/1 between foo/1 and bar/1 only if the value of variable x is true, you can rewrite it as follows:

x = ... # true or false

obj
|> foo()
|> then(fn obj ->
  if x, do: baz(obj), else: obj
end)
|> bar()

Speaking about your case, you could write the following:

model
|> cast(params, ~w(something), ~w())
|> then(fn changeset ->
  conditional(changeset) || changeset
end)
|> Repo.update

Or, more succinctly:

model
|> cast(params, ~w(something), ~w())
|> then(& conditional(&1) || &1)
|> Repo.update
Born answered 7/6 at 0:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.