Current section
Files
Jump to
Current section
Files
lib/ecto/paginate.ex
defmodule Phutilx.Ecto.Paginate do
defstruct items: nil, page: 1, size: 10, total: 0
@type t :: %__MODULE__{
items: list(),
page: integer(),
size: integer(),
total: integer()
}
@moduledoc """
Helps with creating filters on resources in the database by creating a helper macros and function and by
consuming the module with use, it will also import the ecto modules needed to create filter schemas and
importing ecto query functions.
"""
@doc """
A pagination macro that adds the fields `:size` and `:page` to the embedded schema. The default
values for these fields can be configured via options.
## Options
* `:default_page` - The default page number (default: `1`)
* `:default_size` - The default page size (default: `25`)
* `:max_size` - The maximum page size allowed (default: `100`)
"""
defmacro paginate(opts \\ []) do
default_page = Keyword.get(opts, :default_page, 1)
default_size = Keyword.get(opts, :default_size, 25)
max_size = Keyword.get(opts, :max_size, 100)
quote do
field(:page, :integer, default: unquote(default_page))
field(:size, :integer, default: unquote(default_size))
@pagination_options %{
page: unquote(default_page),
size: unquote(default_size),
max_size: unquote(max_size)
}
end
end
defmacro __using__(_opts) do
quote do
import Phutilx.Ecto.Paginate
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
Module.register_attribute(__MODULE__, :pagination_options, accumulate: false)
@before_compile Phutilx.Ecto.Paginate
end
end
defmacro __before_compile__(_env) do
quote do
@doc """
Validates the pagination fields `:page` and `:size` in the given changeset. The function uses the
pagination options defined in the module attribute `@pagination_options` to validate the fields.
You do not need to pass the `:page` and `:size` fields to the changeset, they are extracted from the `attrs`,
so you also do not have to pass them to the `cast/3` function in your changeset function.
## Examples
iex> validate_pagination(%Ecto.Changeset{}, %{"page" => 2, "size" => 50})
#Ecto.Changeset<...>
iex> validate_pagination(%Ecto.Changeset{}, %{"page" => 0, "size" => 150})
#Ecto.Changeset<...>
"""
def validate_pagination(changeset, attrs) do
%__MODULE__{}
|> cast(attrs, [:page, :size])
|> validate_number(:page, greater_than: 0)
|> validate_number(:size,
greater_than: 0,
less_than_or_equal_to: @pagination_options[:max_size]
)
|> merge(changeset)
end
end
end
@doc """
Updates the `:values` field of the given pagination struct using the provided update function.
## Examples
iex> pagination = %Phutilx.Ecto.Paginate{values: [1, 2, 3], page: 1, size: 3, total: 3}
iex> Phutilx.Ecto.Paginate.update_items(pagination, fn items -> Enum.map(items, &(&1 * 2)) end)
%Phutilx.Ecto.Paginate{items: [2, 4, 6], page: 1, size: 3, total: 3}
"""
def update_items(%__MODULE__{} = pagination, update_fn) do
update_in(pagination, [Access.key!(:items)], update_fn)
end
end