Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- defmodule Paginator do
- @moduledoc """
- A module that implements functions for performing simple pagination functionality
- Invoke it using the module's `call` function that takes a struct as parameter.
- ## Struct key values
- - total: The total amount of records (mandatory)
- - page: The page currently on (default: 1)
- - per_page: The number of records per page (default: 10)
- - max_display: The number of pages displayed in the pagination menu (default: 10)
- - max_results: Optional argument that limits the total number of records it can paginate
- ## Examples
- iex> Paginator.call %Paginator(total: 1000)
- %Paginator.Output{
- first: nil,
- last: 100,
- next: 2,
- page: 1,
- pages: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
- previous: nil
- }
- """
- # The struct with the pagination info that gets returned
- defmodule Output do
- defstruct [:page, :first, :last, :previous, :next, :pages]
- end
- @doc """
- # Struct that's passed to the module used to calculate the pagination data
- """
- @enforce_keys [:total]
- defstruct page: 1, per_page: 10, total: nil, max_display: 10, max_results: nil
- @doc """
- Invokes the module. Takes a struct with the input and returns a struct with the pagination data
- """
- def call(data) do
- data = data
- |> Map.put(:max_pages, max_pages(data))
- |> Map.put(:half, half(data))
- %Output{
- page: data.page,
- first: first(data),
- last: last(data),
- pages: pages(data),
- previous: previous(data),
- next: next(data)
- }
- end
- """
- Returns the maximum pages.
- @TODO: this must be a bug. add test that has total less than max_results
- """
- defp max_pages(data) do
- cond do
- data.total <= data.per_page ->
- 0
- data.max_results !== nil and data.max_results < data.total ->
- Kernel.trunc(data.max_results / data.per_page)
- true ->
- Kernel.trunc(data.total / data.per_page)
- end
- end
- # Returns the first page
- defp first(data) do
- if data.total >= data.per_page and data.page !== 1, do: 1, else: nil
- end
- # Returns the last page
- defp last(data) do
- if data.page < data.max_pages, do: data.max_pages, else: nil
- end
- # Returns the half value of `max_display` rounded down
- defp half(data) do
- Kernel.trunc(data.max_display / 2)
- end
- # Returns the `pages` list. The number of list items depends on the number specified in `max_display`
- defp pages(data) do
- if data.total === nil or data.total === 0 or data.max_pages === 0 do
- []
- else
- Enum.to_list begin_pages(data)..end_pages(data)
- end
- end
- # Returns the page that the `pages` list starts on
- defp begin_pages(data) do
- cond do
- data.page + data.half >= data.max_pages ->
- # when reaching the end
- data.max_pages - (data.max_display - 1)
- data.page > data.half ->
- # odd vs even pages
- if rem(data.max_display, 2) === 0 do
- data.page - (data.half - 1)
- else
- data.page - data.half
- end
- true ->
- 1
- end
- end
- # Returns the page that the `pages` list ends on
- defp end_pages(data) do
- end_page = data.page + data.half
- cond do
- end_page >= data.max_pages ->
- # when reaching the end
- data.max_pages
- data.page <= data.half ->
- data.max_display
- true ->
- end_page
- end
- end
- # Returns the page number that is prior than the current page.
- # If the current page is 1 it returns nil
- defp previous(data) do
- if data.page > 1, do: data.page - 1, else: nil
- end
- # Returns the page number that is latter than the current page.
- # If the current page is equal to the last it returns nil
- defp next(data) do
- if data.page < data.max_pages, do: data.page + 1, else: nil
- end
- end
- defmodule PaginatorTest do
- use ConnCase
- alias Paginator
- describe "Test Paginator" do
- test "page 1 of 15" do
- res = Paginator.call %Paginator{total: 150}
- assert res.first == nil
- assert res.previous == nil
- assert res.next == 2
- assert res.last == 15
- assert res.pages == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- end
- test "page 5 of 15" do
- res = Paginator.call %Paginator{page: 5, total: 150}
- assert res.first == 1
- assert res.previous == 4
- assert res.next == 6
- assert res.last == 15
- assert res.pages == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- end
- test "page 6 of 15" do
- res = Paginator.call %Paginator{page: 6, total: 150}
- assert res.first == 1
- assert res.previous == 5
- assert res.next == 7
- assert res.last == 15
- assert res.pages == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
- end
- test "page 7 of 15" do
- res = Paginator.call %Paginator{page: 7, total: 150}
- assert res.pages == [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
- end
- test "page 13 of 15" do
- res = Paginator.call %Paginator{page: 13, total: 150}
- assert res.pages == [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
- end
- test "per page 50" do
- res = Paginator.call %Paginator{page: 25, per_page: 50, total: 2000}
- assert res.first == 1
- assert res.previous == 24
- assert res.next == 26
- assert res.last == 40
- assert res.pages == [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
- end
- test "last page" do
- res = Paginator.call %Paginator{page: 50, total: 500}
- assert res.first == 1
- assert res.previous == 49
- assert res.next == nil
- assert res.last == nil
- end
- test "max display" do
- res = Paginator.call %Paginator{page: 8, max_display: 5, total: 2000}
- assert res.first == 1
- assert res.previous == 7
- assert res.next == 9
- assert res.pages == [6, 7, 8, 9, 10]
- res = Paginator.call %Paginator{page: 9, max_display: 5, total: 2000}
- assert res.pages == [7, 8, 9, 10, 11]
- end
- test "max results - total more than max" do
- res = Paginator.call %Paginator{page: 96, total: 2000, max_results: 1000}
- assert res.last == 100
- assert res.pages == [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
- end
- test "max results - max more than total" do
- res = Paginator.call %Paginator{page: 96, total: 2000, max_results: 1000}
- assert res.last == 100
- assert res.pages == [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
- end
- test "no pages - zero total" do
- res = Paginator.call %Paginator{total: 0}
- assert res.first == nil
- assert res.previous == nil
- assert res.next == nil
- assert res.pages == []
- end
- test "no pages - low total" do
- res = Paginator.call %Paginator{total: 5}
- assert res.first == nil
- assert res.previous == nil
- assert res.next == nil
- assert res.pages == []
- end
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement