Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

selected as variable #392

Merged
merged 3 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions lib/flop/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,4 @@ defmodule Flop.Adapter do
@callback list(queryable, opts) :: [any]

@callback get_field(any, atom, Flop.FieldInfo.t()) :: any

@doc """
Returns a quoted function to be compiled in the schema protocol
implementation.

Takes the schema options (with the nested adapter options) as argument.

This is a hacky workaround that is necessary because
`Ecto.Query.API.selected_as/1` does not accept variables, which means that we
need to compile a function that builds the `order_by` clause for configured
alias fields.

This callback will be removed as soon as a better solution is found or made
possible.
"""
@callback custom_func_builder(opts) :: Macro.t() when opts: keyword
end
103 changes: 41 additions & 62 deletions lib/flop/adapter/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,51 @@ defmodule Flop.Adapter.Ecto do
module ->
struct = struct(module)

Enum.reduce(directions, query, fn expr, acc_query ->
Flop.Schema.custom(struct, {:apply_order_by, acc_query, expr})
Enum.reduce(directions, query, fn {_, field} = expr, acc_query ->
field_info = Flop.Schema.field_info(struct, field)
apply_order_by_field(acc_query, expr, field_info, struct)
end)
end
end

defp apply_order_by_field(
q,
{direction, _},
%FieldInfo{
extra: %{type: :join, binding: binding, field: field}
},
_
) do
order_by(q, [{^binding, r}], [{^direction, field(r, ^field)}])
end

defp apply_order_by_field(
q,
{direction, _},
%FieldInfo{
extra: %{type: :compound, fields: fields}
},
struct
) do
Enum.reduce(fields, q, fn field, acc_query ->
field_info = Flop.Schema.field_info(struct, field)
apply_order_by_field(acc_query, {direction, field}, field_info, struct)
end)
end

defp apply_order_by_field(
q,
{direction, field},
%FieldInfo{extra: %{type: :alias}},
_
) do
order_by(q, [{^direction, selected_as(^field)}])
end

defp apply_order_by_field(q, order_expr, _, _) do
order_by(q, ^order_expr)
end

@impl Flop.Adapter
def apply_limit_offset(query, limit, offset, _opts) do
query
Expand Down Expand Up @@ -460,66 +499,6 @@ defmodule Flop.Adapter.Ecto do
Keyword.merge(default_opts, Keyword.get(opts, :query_opts, []))
end

## Compile time shenanigans

@impl Flop.Adapter
def custom_func_builder(opts) do
adapter_opts = Keyword.fetch!(opts, :adapter_opts)
compound_fields = Map.fetch!(adapter_opts, :compound_fields)
join_fields = Map.fetch!(adapter_opts, :join_fields)
alias_fields = Map.fetch!(adapter_opts, :alias_fields)

compound_field_funcs =
for {name, fields} <- compound_fields do
quote do
def custom(struct, {:apply_order_by, q, {direction, unquote(name)}}) do
Enum.reduce(unquote(fields), q, fn field, acc_q ->
Flop.Schema.custom(
struct,
{:apply_order_by, acc_q, {direction, field}}
)
end)
end
end
end

join_field_funcs =
for {join_field, %{binding: binding, field: field}} <- join_fields do
quote do
def custom(_, {:apply_order_by, q, {direction, unquote(join_field)}}) do
order_by(
q,
[{^unquote(binding), r}],
[{^direction, field(r, unquote(field))}]
)
end
end
end

alias_field_func =
for name <- alias_fields do
quote do
def custom(_, {:apply_order_by, q, {direction, unquote(name)}}) do
order_by(q, [{^direction, selected_as(unquote(name))}])
end
end
end

schema_field_func =
quote do
def custom(_, {:apply_order_by, q, direction}) do
order_by(q, ^direction)
end
end

[
compound_field_funcs,
join_field_funcs,
alias_field_func,
schema_field_func
]
end

## Filter query builder

for op <- [:like_and, :like_or, :ilike_and, :ilike_or] do
Expand Down
7 changes: 0 additions & 7 deletions lib/flop/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -844,10 +844,6 @@ defprotocol Flop.Schema do
@doc since: "0.2.0"
@spec max_limit(any) :: pos_integer | nil
def max_limit(data)

@doc false
@spec custom(any, any) :: any
def custom(data, arg)
end

defimpl Flop.Schema, for: Any do
Expand Down Expand Up @@ -926,7 +922,6 @@ defimpl Flop.Schema, for: Any do

field_info_func = build_field_info_func(adapter, adapter_opts, struct)
get_field_func = build_get_field_func(struct, adapter, adapter_opts)
custom_func = adapter.custom_func_builder(options)

quote do
defimpl Flop.Schema, for: unquote(module) do
Expand Down Expand Up @@ -965,8 +960,6 @@ defimpl Flop.Schema, for: Any do
def sortable(_) do
unquote(sortable_fields)
end

unquote(custom_func)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ defmodule Flop.MixProject do
[
{:credo, "~> 1.7.0", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4.0", only: [:dev, :test], runtime: false},
{:ecto, "~> 3.10.3"},
{:ecto_sql, "~> 3.9", only: :test},
{:ecto, "~> 3.11"},
{:ecto_sql, "~> 3.11", only: :test},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:ex_machina, "~> 2.4", only: :test},
{:makeup_diff, "~> 0.1.0", only: :dev, runtime: false},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"earmark_parser": {:hex, :earmark_parser, "1.4.38", "b42252eddf63bda05554ba8be93a1262dc0920c721f1aaf989f5de0f73a2e367", [:mix], [], "hexpm", "2cd0907795aaef0c7e8442e376633c5b3bd6edc8dbbdc539b22f095501c1cdb6"},
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
"ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"ecto_sql": {:hex, :ecto_sql, "3.11.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
Expand Down
Loading