Rudziankoŭ
Rudziankoŭ

Reputation: 11251

Elixir: dynamic parameter in use statement

How could I pass adapter parameter dynamically to Nebulex.Cache module initialization?

defmodule MyApp.RedisCache do    
  use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: if true do NebulexRedisAdapter else Nebulex.Adapters.Nil end
end

Upvotes: 0

Views: 186

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

The easiest way would be to move the conditional one level up.

defmodule MyApp.RedisCache do    
  if true do
    use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: NebulexRedisAdapter
  else
    use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: Nebulex.Adapters.Nil
  end
  ...
end

Another way would be to create a private macro with a parameter, or, alternatively, create a module attribute storing the adapter and pass it to use (assuming Nebulex.Cache does Macro.expand/2 arguments in the __CALLER__ context.)


The reason is that the macro receives the AST, so what will __using__/1 receive, is quote do: if .... which won’t be even attempted to compile at the first compilation stage.


The below code might shed a light on how the AST is passed between calls and what Macro.expand/2 does.

defmodule M do
  @param if(true, do: M1, else: M2)

  defmacrop t1(arg: args) do
    IO.inspect(args, label: "#1")
  end
  defmacrop t2(arg: args) do
    IO.inspect(Macro.expand(args, __CALLER__), label: "#2")
  end

  def test do
    t1(arg: if(true, do: M1, else: M2))
    t2(arg: if(true, do: M1, else: M2))
    t1(arg: @param)
    t2(arg: @param)
  end
end

It results in:

#1: {:if, [line: 19],
 ...
#2: {:case, [optimize_boolean: true],
 ...
#1: {:@, [line: 21], [{:param, [line: 21], nil}]}
#2: M1

Note, that if was expanded to case, and the module attribute was not expanded unless the explicit Macro.expand/2 was called on it.

Upvotes: 1

Related Questions