Packages

Cut out the middle man. Turn a URI into a scoped, preloaded, encoded response. Automagically.

Current section

Files

Jump to
yax lib builder.ex
Raw

lib/builder.ex

defmodule Yax.Builder do
@moduledoc false
require Ecto.Query
def build(params, module, user) do
build_query(module, params, user)
end
defp build_query(q, params, user) do
Ecto.Query.from(q in q)
|> Bodyguard.scope(user)
|> (&add_assocs/3).(params[:assoc], user)
|> (&add_where/2).(params[:where])
|> (&add_select/2).(params[:select])
end
defp add_select(q, params) do
case params do
nil ->
q
[] ->
q
[:*] ->
q
xs ->
Ecto.Query.select(q, ^xs)
# Ecto.Query.select(q, [x], map(x, ^xs))
end
end
defp add_assocs(q, params, user) do
case params do
nil ->
q
assocs when is_map(assocs) ->
Enum.reduce(assocs, q, &add_assoc(&1, &2, user))
end
end
defp add_where(q, params) do
case params do
nil ->
q
xs ->
Enum.reduce(xs, q, &add_where_field/2)
end
end
defp add_where_field({f, "is.not.null"}, q) do
Ecto.Query.where(q, [x], not is_nil(field(x, ^f)))
end
defp add_where_field({field, value}, q) do
where = Keyword.put([], field, value)
Ecto.Query.where(q, ^where)
end
defp add_assoc({assoc, params}, q, user) do
{_, source} = q.from.source
preload = [{assoc, build_query(ref(source, assoc), params, user)}]
Ecto.Query.preload(q, ^preload)
end
defp ref(source, assoc) do
case source.__schema__(:association, assoc) do
%Ecto.Association.BelongsTo{related: related} -> related
%Ecto.Association.Has{related: related} -> related
%Ecto.Association.HasThrough{owner: owner, through: through} -> ref_through(owner, through)
end
end
defp ref_through(source, [assoc]),
do: ref(source, assoc)
defp ref_through(source, [assoc | rest]) do
ref = ref(source, assoc)
ref_through(ref, rest)
end
end