zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

group_by.ex (3794B)


      1 import Kernel, except: [apply: 2]
      2 
      3 defmodule Ecto.Query.Builder.GroupBy do
      4   @moduledoc false
      5 
      6   alias Ecto.Query.Builder
      7 
      8   @doc """
      9   Escapes a list of quoted expressions.
     10 
     11   See `Ecto.Builder.escape/2`.
     12 
     13       iex> escape(:group_by, quote do [x.x, 13] end, {[], %{}}, [x: 0], __ENV__)
     14       {[{:{}, [], [{:{}, [], [:., [], [{:{}, [], [:&, [], [0]]}, :x]]}, [], []]},
     15         13],
     16        {[], %{}}}
     17   """
     18   @spec escape(:group_by | :partition_by, Macro.t, {list, term}, Keyword.t, Macro.Env.t) ::
     19           {Macro.t, {list, term}}
     20   def escape(kind, expr, params_acc, vars, env) do
     21     expr
     22     |> List.wrap
     23     |> Enum.map_reduce(params_acc, &do_escape(&1, &2, kind, vars, env))
     24   end
     25 
     26   defp do_escape({:^, _, [expr]}, params_acc, kind, _vars, _env) do
     27     {quote(do: Ecto.Query.Builder.GroupBy.field!(unquote(kind), unquote(expr))), params_acc}
     28   end
     29 
     30   defp do_escape(field, params_acc, _kind, _vars, _env) when is_atom(field) do
     31     {Macro.escape(to_field(field)), params_acc}
     32   end
     33 
     34   defp do_escape(expr, params_acc, _kind, vars, env) do
     35     Builder.escape(expr, :any, params_acc, vars, env)
     36   end
     37 
     38   @doc """
     39   Called at runtime to verify a field.
     40   """
     41   def field!(_kind, field) when is_atom(field),
     42     do: to_field(field)
     43   def field!(kind, other) do
     44     raise ArgumentError,
     45       "expected a field as an atom in `#{kind}`, got: `#{inspect other}`"
     46   end
     47 
     48   @doc """
     49   Shared between group_by and partition_by.
     50   """
     51   def group_or_partition_by!(kind, query, exprs, params) do
     52     {expr, {params, _}} =
     53       Enum.map_reduce(List.wrap(exprs), {params, length(params)}, fn
     54         field, params_count when is_atom(field) ->
     55           {to_field(field), params_count}
     56 
     57         %Ecto.Query.DynamicExpr{} = dynamic, {params, count} ->
     58           {expr, params, count} = Builder.Dynamic.partially_expand(kind, query, dynamic, params, count)
     59           {expr, {params, count}}
     60 
     61         other, _params_count ->
     62           raise ArgumentError,
     63                 "expected a list of fields and dynamics in `#{kind}`, got: `#{inspect other}`"
     64       end)
     65 
     66     {expr, params}
     67   end
     68 
     69   defp to_field(field), do: {{:., [], [{:&, [], [0]}, field]}, [], []}
     70 
     71   @doc """
     72   Called at runtime to assemble group_by.
     73   """
     74   def group_by!(query, group_by, file, line) do
     75     {expr, params} = group_or_partition_by!(:group_by, query, group_by, [])
     76     expr = %Ecto.Query.QueryExpr{expr: expr, params: Enum.reverse(params), line: line, file: file}
     77     apply(query, expr)
     78   end
     79 
     80   @doc """
     81   Builds a quoted expression.
     82 
     83   The quoted expression should evaluate to a query at runtime.
     84   If possible, it does all calculations at compile time to avoid
     85   runtime work.
     86   """
     87   @spec build(Macro.t, [Macro.t], Macro.t, Macro.Env.t) :: Macro.t
     88   def build(query, _binding, {:^, _, [var]}, env) do
     89     quote do
     90       Ecto.Query.Builder.GroupBy.group_by!(unquote(query), unquote(var), unquote(env.file), unquote(env.line))
     91     end
     92   end
     93 
     94   def build(query, binding, expr, env) do
     95     {query, binding} = Builder.escape_binding(query, binding, env)
     96     {expr, {params, _acc}} = escape(:group_by, expr, {[], %{}}, binding, env)
     97     params = Builder.escape_params(params)
     98 
     99     group_by = quote do: %Ecto.Query.QueryExpr{
    100                            expr: unquote(expr),
    101                            params: unquote(params),
    102                            file: unquote(env.file),
    103                            line: unquote(env.line)}
    104     Builder.apply_query(query, __MODULE__, [group_by], env)
    105   end
    106 
    107   @doc """
    108   The callback applied by `build/4` to build the query.
    109   """
    110   @spec apply(Ecto.Queryable.t, term) :: Ecto.Query.t
    111   def apply(%Ecto.Query{group_bys: group_bys} = query, expr) do
    112     %{query | group_bys: group_bys ++ [expr]}
    113   end
    114   def apply(query, expr) do
    115     apply(Ecto.Queryable.to_query(query), expr)
    116   end
    117 end