Current section
Files
Jump to
Current section
Files
lib/plucto.ex
defmodule Plucto do
import Ecto.Query, only: [from: 2]
alias Plucto.Page
alias Plug.Conn
@moduledoc """
Plucto is a light wieght unobtrusive pagination helper for elixir/phoenix web applications.
Plucto does not "integrate" with existing schemas, it is simply library for asbtracting common pagination functions.
Plucto is intended to work with Plug and Ecto. It requires a conn struct, ecto query, and ecto repo.
You might be familiar with generated context functions such as this:
%User{} |> Repo.all()
To write this using Plucto you would do:
from(u in User) |> Plucto.flip(conn, Repo)
Why the conn struct? Plucto at this time is a configuration free library. It gets all the informaion it needs using the url.
The only two parameter that matter are page and limit. Neither are required to initial a paginated response. Plucto will default to page 1 and a limit of 25.
Consider a page that list users, we will use the url www.officeadmin.com/users
www.officeadmin.com/users is a paginatable route due to the defaults.
www.officeadmin.com/users is the same as this www.officeadmin.com/users?page=1
"""
def flip(query, %Conn{} = conn, repo) do
conn = Plug.Conn.fetch_query_params(conn)
%Page{
params: conn.params,
path_info: conn.path_info,
repo: repo
}
|> current_page()
|> limit()
|> offset()
|> paged_query(query)
|> data()
|> total(query)
|> get_from()
|> get_to()
|> last_page()
end
defp total(%Page{repo: repo} = page, query) do
%{page | total: repo.aggregate(query, :count, :id)}
end
defp data(%Page{query: query, repo: repo} = page) do
%{page | data: repo.all(query)}
end
defp paged_query(%Page{limit: limit, offset: offset} = page, %Ecto.Query{} = query) do
%{page | query: from(q in query, limit: ^limit, offset: ^offset)}
end
defp get_from(%Page{total: total} = page) when total == 0, do: page
defp get_from(%Page{offset: offset} = page), do: %{page | from: offset + 1}
defp get_to(%Page{total: total} = page) when total == 0, do: page
defp get_to(%Page{offset: offset, data: data} = page),
do: %{page | to: offset + Enum.count(data)}
defp last_page(%Page{total: total, limit: limit} = page) when total < limit, do: page
defp last_page(%Page{total: total, limit: limit} = page) do
last_page = (total / limit) |> Float.ceil() |> round
%{page | last_page: last_page}
end
defp offset(%Page{current_page: current_page, limit: limit} = page),
do: %{page | offset: current_page * limit - limit}
defp limit(%Page{params: params} = page) do
case params["limit"] do
nil ->
page
val ->
case Integer.parse(val) do
{limit, ""} -> %{page | limit: limit}
_ -> %{page | limit: 1}
end
end
end
# get the page
defp current_page(%Page{params: params} = page) do
case params["page"] do
nil ->
%{page | current_page: 1}
val ->
case Integer.parse(val) do
{current_page, ""} -> %{page | current_page: current_page}
_ -> %{page | current_page: 1}
end
end
end
end