I’d like to implement a generic helper method that would work this way:
def is_active(query \\ __MODULE__)
def is_active(query) when :is_active in __MODULE__.__schema__(:fields) do
where([q], q.is_active == true)
end
def is_active(query), do: query
But it doesn’t work as I can’t pattern match __MODULE__.__schema__. Any solution to write this?
No, not really, as you cannot call remote functions at compile time for module you are compiling. However why would you define something like that? I assume that this is generated by macro. Maybe DB views would be something more readable (depending on the context).
You can not call remote functions in guards, as @hauleth wrote, because it happens in compile time. __MODULE__.__schema__/1 is a remote function. Just use cond (or if):
def is_active(query \\ __MODULE__) do
cond do
:is_active in __MODULE__.__schema__(:fields) -> where(query, [q], q.is_active == true)
true -> query
end
end
Assuming this function is being defined by a macro and you don’t mind very slightly touching ecto internals, you could branch at compile time instead of runtime. You could check a module attribute directly and conditionally define the function, but your schemas would have to call your macro after defining all the fields. To get around that, I’d use @before_compile in your macro.
# in your main macro
@before_compile MyApp.IsActive
Then define another macro that will get called for the module before it’s compiled.
defmodule MyApp.IsActive do
def __before_compile__(%{module: module}) do
if {:is_active, :boolean} in Module.get_attribute(module, :ecto_fields, []) do
quote do
def is_active(query) do
require Ecto.Query # if you aren't importing in main macro
Ecto.Query.where(query, [q], q.is_active == true)
end
end
else
quote do
def is_active(query) do
query
end
end
end
end
end