Alembic v3.4.0 Alembic.Pagination.Page View Source

A page using paged pagination where the size of pages is fixed.

Link to this section Summary

Types

t()
  • number - the 1-based page number
  • size - the size of this page and all pages

Functions

The number of pages of size when there are total_size resources to be paginated

t for Alembic.Pagination.t first

Parses t out of params

Extracts number from query "page[number]" and and size from query "page[size]"

Extracts number from uri query "page[number]" and size from uri query "page[size]"

t for Alembic.Pagination.t last

t for Alembic.Pagination.t next

t for Alembic.Pagination.t previous

Alembic.Pagination.t for pages around page

Converts the page back to params

Converts the page back to query portion of URI

Link to this section Types

Link to this type t() View Source
t() :: %Alembic.Pagination.Page{number: pos_integer, size: pos_integer}
  • number - the 1-based page number
  • size - the size of this page and all pages

Link to this section Functions

Link to this function count(map) View Source
count(%{size: pos_integer, total_size: non_neg_integer}) :: pos_integer

The number of pages of size when there are total_size resources to be paginated.

If there are no resources (total_size is 0), then there will still be 1 page

iex> Alembic.Pagination.Page.count(%{size: 10, total_size: 0})
1

The number of pages is always rounded up since a partial page still needs to be returned

iex> Alembic.Pagination.Page.count(%{size: 10, total_size: 10})
1
iex> Alembic.Pagination.Page.count(%{size: 10, total_size: 1})
1
iex> Alembic.Pagination.Page.count(%{size: 10, total_size: 11})
2
Link to this function first(page, _) View Source
first(t, %{optional(:count) => pos_integer}) :: t

t for Alembic.Pagination.t first

There is always a first t with number 1.

iex> Alembic.Pagination.Page.first(
...>   %Alembic.Pagination.Page{
...>     number: 2,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
%Alembic.Pagination.Page{
  number: 1,
  size: 10
}
Link to this function from_params(params) View Source
from_params(map) ::
  {:ok, t} |
  {:ok, :all} |
  {:ok, nil} |
  {:error, Alembic.Document.t}

Parses t out of params

No pagination

If there is no "page" key, then there is no pagination.

iex> Alembic.Pagination.Page.from_params(%{})
{:ok, nil}

Pagination

If there is a “page” key with "number" and "size" children, then there is pagination.

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "number" => 1,
...>       "size" => 2
...>     }
...>   }
...> )
{:ok, %Alembic.Pagination.Page{number: 1, size: 2}}

In addition to be decoded JSON, the params can also be the raw %{String.t => String.t} and the quoted integers will be decoded.

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "number" => "1",
...>       "size" => "2"
...>     }
...>   }
...> )
{:ok, %Alembic.Pagination.Page{number: 1, size: 2}}

Opt-out

Opting out of default pagination can be indicated with a nil page

iex> Alembic.Pagination.Page.from_params(
...>   %{"page" => nil}
...> )
{:ok, :all}

Errors

A page number can’t be given as the "page" parameter alone because no default page size is assumed.

iex> Alembic.Pagination.Page.from_params(%{"page" => 1})
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "`/page` type is not object",
        meta: %{
          "type" => "object"
        },
        source: %Alembic.Source{
          pointer: "/page"
        },
        status: "422",
        title: "Type is wrong"
      }
    ]
  }
}

Required parameters

Likewise, the "page" map can’t have only a "number" parameter because no default page size is assumed.

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "number" => 1
...>     }
...>   }
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "`/page/size` is missing",
        meta: %{
          "child" => "size"
        },
        source: %Alembic.Source{
          pointer: "/page"
        },
        status: "422",
        title: "Child missing"
      }
    ]
  }
}

The page number is not assumed to be 1 when not given.

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "size" => 10
...>     }
...>   }
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "`/page/number` is missing",
        meta: %{
          "child" => "number"
        },
        source: %Alembic.Source{
          pointer: "/page"
        },
        status: "422",
        title: "Child missing"
      }
    ]
  }
}

Number format

"page" "number" must be a positive integer. It is 1-based. The first page is "1"

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "number" => 0,
...>       "size" => 10
...>     }
...>   }
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "`/page/number` type is not positive integer",
        meta: %{
          "type" => "positive integer"
        },
        source: %Alembic.Source{
          pointer: "/page/number"
        },
        status: "422",
        title: "Type is wrong"
      }
    ]
  }
}

Size format

"page" "size" must be a positive integer.

iex> Alembic.Pagination.Page.from_params(
...>   %{
...>     "page" => %{
...>       "number" => 1,
...>       "size" => 0
...>     }
...>   }
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "`/page/size` type is not positive integer",
        meta: %{
          "type" => "positive integer"
        },
        source: %Alembic.Source{
          pointer: "/page/size"
        },
        status: "422",
        title: "Type is wrong"
      }
    ]
  }
}
Link to this function from_query(query) View Source
from_query(String.t) :: t

Extracts number from query "page[number]" and and size from query "page[size]".

iex> Alembic.Pagination.Page.from_query("page%5Bnumber%5D=2&page%5Bsize%5D=10")
%Alembic.Pagination.Page{number: 2, size: 10}

If query does not have both "page[number]" and "page[size]" then nil is returned

iex> Alembic.Pagination.Page.from_query("page%5Bnumber%5D=2")
nil
iex> Alembic.Pagination.Page.from_query("page%5Bsize%5D=10")
nil
iex> Alembic.Pagination.Page.from_query("")
nil
Link to this function from_uri(uri) View Source
from_uri(URI.t) :: t | nil

Extracts number from uri query "page[number]" and size from uri query "page[size]".

iex> Alembic.Pagination.Page.from_uri(%URI{query: "page%5Bnumber%5D=2&page%5Bsize%5D=10"})
%Alembic.Pagination.Page{number: 2, size: 10}

If a URI.t query does not have both "page[number]" and "page[size]" then nil is returned

iex> Alembic.Pagination.Page.from_uri(%URI{query: "page%5Bnumber%5D=2"})
nil
iex> Alembic.Pagination.Page.from_uri(%URI{query: "page%5Bsize%5D=10"})
nil
iex> Alembic.Pagination.Page.from_uri(%URI{query: ""})
nil

If a URI.t does not have a query then nil is returned

iex> Alembic.Pagination.Page.from_uri(%URI{query: nil})
nil
Link to this function last(arg, map) View Source
last(t, %{count: pos_integer}) :: t

t for Alembic.Pagination.t last

There is always last Alembic.Pagination.Page.t with number count.

iex> Alembic.Pagination.Page.last(
...>   %Alembic.Pagination.Page{
...>     number: 1,
...>     size: 10
...>   },
...>   %{count: 2}
...> )
%Alembic.Pagination.Page{
  number: 2,
  size: 10
}
Link to this function next(arg1, arg2) View Source
next(t, %{count: pos_integer}) :: t | nil

t for Alembic.Pagination.t next

Single page

When there is only one page, the first page is also the last page, so next is nil.

iex> Alembic.Pagination.Page.next(
...>   %Alembic.Pagination.Page{
...>     number: 1,
...>     size: 10
...>   },
...>   %{count: 1}
...> )
nil

Multiple Pages

When there are multiple pages, the first page has the second page, with number 2, as its next page.

iex> Alembic.Pagination.Page.next(
...>   %Alembic.Pagination.Page{
...>     number: 1,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
%Alembic.Pagination.Page{
  number: 2,
  size: 10
}

Any middle page will also have a next page with number + 1

iex> Alembic.Pagination.Page.next(
...>   %Alembic.Pagination.Page{
...>     number: 2,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
%Alembic.Pagination.Page{
  number: 3,
  size: 10
}

The last page is the only page that will have a next page.

iex> Alembic.Pagination.Page.next(
...>   %Alembic.Pagination.Page{
...>     number: 3,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
nil
Link to this function previous(arg1, _) View Source
previous(t, %{optional(:count) => pos_integer}) :: t | nil

t for Alembic.Pagination.t previous.

Single page

When there is only one page, the last page is also the first page, so previous page is nil.

iex> Alembic.Pagination.Page.previous(
...>   %Alembic.Pagination.Page{
...>     number: 1,
...>     size: 10
...>   },
...>   %{count: 1}
...> )
nil

Multiple Pages

When there are multiple pages, the first page has no previous page, so it is nil.

iex> Alembic.Pagination.Page.previous(
...>   %Alembic.Pagination.Page{
...>     number: 1,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
nil

Any middle page will have the previous page with number - 1.

iex> Alembic.Pagination.Page.previous(
...>   %Alembic.Pagination.Page{
...>     number: 2,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
%Alembic.Pagination.Page{
  number: 1,
  size: 10
}

The last page will also have previous page the same as a middle page.

iex> Alembic.Pagination.Page.previous(
...>   %Alembic.Pagination.Page{
...>     number: 3,
...>     size: 10
...>   },
...>   %{count: 3}
...> )
%Alembic.Pagination.Page{
  number: 2,
  size: 10
}
Link to this function to_pagination(page, map) View Source
to_pagination(t, %{total_size: non_neg_integer}) ::
  {:ok, Alembic.Pagination.t} |
  {:error, Alembic.Document.t}

Alembic.Pagination.t for pages around page.

Single Page

When there is only one page, first and last will be set to a t, but next or previous will be nil.

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 1, size: 10},
...>   %{total_size: 5}
...> )
{
  :ok,
  %Alembic.Pagination{
    first: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    last: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    total_size: 5
  }
}

No entries

If total_size is 0, then there will still be 1 page, but it will be empty

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 1, size: 10},
...>   %{total_size: 0}
...> )
{
  :ok,
  %Alembic.Pagination{
    first: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    last: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    total_size: 0
  }
}

Multiple Pages

When there are multiple pages, every page will have first and last set to a t.

On the first page, the next field will set, but not the previous field.

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 1, size: 10},
...>   %{total_size: 25}
...> )
{
  :ok,
  %Alembic.Pagination{
    first: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    last: %Alembic.Pagination.Page{
      number: 3,
      size: 10
    },
    next: %Alembic.Pagination.Page{
      number: 2,
      size: 10
    },
    total_size: 25
  }
}

On any middle page, both the next and previous fields will be set.

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 2, size: 10},
...>   %{total_size: 25}
...> )
{
  :ok,
  %Alembic.Pagination{
    first: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    last: %Alembic.Pagination.Page{
      number: 3,
      size: 10
    },
    next: %Alembic.Pagination.Page{
      number: 3,
      size: 10
    },
    previous: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    total_size: 25
  }
}

On the last page, the previous field will be set, but not the next field.

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 3, size: 10},
...>   %{total_size: 25}
...> )
{
  :ok,
  %Alembic.Pagination{
    first: %Alembic.Pagination.Page{
      number: 1,
      size: 10
    },
    last: %Alembic.Pagination.Page{
      number: 3,
      size: 10
    },
    previous: %Alembic.Pagination.Page{
      number: 2,
      size: 10
    },
    total_size: 25
  }
}

Out-of-range

If a page number is too high an error will be returned.

iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 2, size: 10},
...>   %{total_size: 0}
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "Page number (2) must be between 1 and the page count (1)",
        meta: %{
          "count" => 1,
          "number" => 2
        },
        source: %Alembic.Source{
          pointer: "/page/number"
        },
        status: "422",
        title: "Page number must be between 1 and the page count"
      }
    ]
  }
}
iex> Alembic.Pagination.Page.to_pagination(
...>   %Alembic.Pagination.Page{number: 4, size: 10},
...>   %{total_size: 15}
...> )
{
  :error,
  %Alembic.Document{
    errors: [
      %Alembic.Error{
        detail: "Page number (4) must be between 1 and the page count (2)",
        meta: %{
          "count" => 2,
          "number" => 4
        },
        source: %Alembic.Source{
          pointer: "/page/number"
        },
        status: "422",
        title: "Page number must be between 1 and the page count"
      }
    ]
  }
}
Link to this function to_params(page) View Source
to_params(t | :all | nil) :: map

Converts the page back to params.

An Alembic.Pagination.Page.t is converted to JSON.

iex> Alembic.Pagination.Page.to_params(%Alembic.Pagination.Page{number: 2, size: 10})
%{
  "page" => %{
    "number" => 2,
    "size" => 10
  }
}

The special value :all, which indicates that pagination should be disabled and “all” pages should be returned at once goes back to a nil "page"

iex> Alembic.Pagination.Page.to_params(:all)
%{
  "page" => nil
}

A nil page on the other hand, has no params at all

iex> Alembic.Pagination.Page.to_params(nil)
%{}

Converts the page back to query portion of URI

iex> Alembic.Pagination.Page.to_query(%Alembic.Pagination.Page{number: 2, size: 10})
"page%5Bnumber%5D=2&page%5Bsize%5D=10"