Current section
Files
Jump to
Current section
Files
lib/filterable.ex
defmodule Filterable do
@moduledoc """
Filterable allows to map incoming controller parameters to filter functions:
defmodule Filterable do
def title(_conn, query, value) do
query |> where(title: ^value)
end
end
Then we can apply defined query params filters inside controller action:
def index(conn, params) do
posts = Post |> apply_filters(conn) |> Repo.all
render(conn, "index.html", posts: posts)
end
By default `apply_filters` uses filter functions defined in `AppName.ControllerModule.Filterable` module.
Other module can be set explicitly with `filterable` macro:
filterable UserFilters, param: "filter"
"""
@doc false
defmacro __using__(_) do
quote do
import unquote(__MODULE__)
@before_compile unquote(__MODULE__)
@filters_module Module.concat([__MODULE__, unquote(__MODULE__)])
@filter_options []
end
end
@lint false
@doc false
defmacro __before_compile__(_) do
quote do
def apply_filters(query, conn) do
defined_filters = @filters_module.__info__(:functions)
Enum.reduce(defined_filters, query, fn ({filter_name, args_num}, query) ->
param_name = Atom.to_string(filter_name)
value = conn |> filter_params |> Map.get(param_name)
try do
cond do
args_num == 2 && !value ->
apply(@filters_module, filter_name, [conn, query])
args_num == 3 && value ->
apply(@filters_module, filter_name, [conn, query, value])
true -> query
end
rescue
FunctionClauseError -> query
end
end)
end
def filter_params(%{params: params}) do
case Keyword.get(@filter_options, :param) do
nil -> params
key -> Map.get(params, key, %{})
end
end
end
end
@doc """
Allows to select `module` with defined filter functions with options.
## Options
* `:param` - Sets top level query param for filters.
"""
defmacro filterable(module, options \\ []) do
quote do
@filters_module unquote(module)
@filter_options unquote(options)
end
end
end