Advertisement
Guest User

Untitled

a guest
Feb 23rd, 2019
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.67 KB | None | 0 0
  1. defmodule Paginator do
  2.  
  3. @moduledoc """
  4. A module that implements functions for performing simple pagination functionality
  5.  
  6. Invoke it using the module's `call` function that takes a struct as parameter.
  7.  
  8. ## Struct key values
  9.  
  10. - total: The total amount of records (mandatory)
  11. - page: The page currently on (default: 1)
  12. - per_page: The number of records per page (default: 10)
  13. - max_display: The number of pages displayed in the pagination menu (default: 10)
  14. - max_results: Optional argument that limits the total number of records it can paginate
  15.  
  16. ## Examples
  17.  
  18. iex> Paginator.call %Paginator(total: 1000)
  19.  
  20. %Paginator.Output{
  21. first: nil,
  22. last: 100,
  23. next: 2,
  24. page: 1,
  25. pages: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  26. previous: nil
  27. }
  28.  
  29. """
  30.  
  31. # The struct with the pagination info that gets returned
  32. defmodule Output do
  33. defstruct [:page, :first, :last, :previous, :next, :pages]
  34. end
  35.  
  36. @doc """
  37. # Struct that's passed to the module used to calculate the pagination data
  38. """
  39. @enforce_keys [:total]
  40. defstruct page: 1, per_page: 10, total: nil, max_display: 10, max_results: nil
  41.  
  42. @doc """
  43. Invokes the module. Takes a struct with the input and returns a struct with the pagination data
  44. """
  45. def call(data) do
  46.  
  47. data = data
  48. |> Map.put(:max_pages, max_pages(data))
  49. |> Map.put(:half, half(data))
  50.  
  51. %Output{
  52. page: data.page,
  53. first: first(data),
  54. last: last(data),
  55. pages: pages(data),
  56. previous: previous(data),
  57. next: next(data)
  58. }
  59. end
  60.  
  61. """
  62. Returns the maximum pages.
  63. @TODO: this must be a bug. add test that has total less than max_results
  64. """
  65. defp max_pages(data) do
  66. cond do
  67. data.total <= data.per_page ->
  68. 0
  69. data.max_results !== nil and data.max_results < data.total ->
  70. Kernel.trunc(data.max_results / data.per_page)
  71. true ->
  72. Kernel.trunc(data.total / data.per_page)
  73. end
  74. end
  75.  
  76. # Returns the first page
  77. defp first(data) do
  78. if data.total >= data.per_page and data.page !== 1, do: 1, else: nil
  79. end
  80.  
  81. # Returns the last page
  82. defp last(data) do
  83. if data.page < data.max_pages, do: data.max_pages, else: nil
  84. end
  85.  
  86. # Returns the half value of `max_display` rounded down
  87. defp half(data) do
  88. Kernel.trunc(data.max_display / 2)
  89. end
  90.  
  91. # Returns the `pages` list. The number of list items depends on the number specified in `max_display`
  92. defp pages(data) do
  93. if data.total === nil or data.total === 0 or data.max_pages === 0 do
  94. []
  95. else
  96. Enum.to_list begin_pages(data)..end_pages(data)
  97. end
  98. end
  99.  
  100. # Returns the page that the `pages` list starts on
  101. defp begin_pages(data) do
  102. cond do
  103. data.page + data.half >= data.max_pages ->
  104. # when reaching the end
  105. data.max_pages - (data.max_display - 1)
  106. data.page > data.half ->
  107. # odd vs even pages
  108. if rem(data.max_display, 2) === 0 do
  109. data.page - (data.half - 1)
  110. else
  111. data.page - data.half
  112. end
  113. true ->
  114. 1
  115. end
  116. end
  117.  
  118. # Returns the page that the `pages` list ends on
  119. defp end_pages(data) do
  120. end_page = data.page + data.half
  121. cond do
  122. end_page >= data.max_pages ->
  123. # when reaching the end
  124. data.max_pages
  125. data.page <= data.half ->
  126. data.max_display
  127. true ->
  128. end_page
  129. end
  130. end
  131.  
  132. # Returns the page number that is prior than the current page.
  133. # If the current page is 1 it returns nil
  134. defp previous(data) do
  135. if data.page > 1, do: data.page - 1, else: nil
  136. end
  137.  
  138. # Returns the page number that is latter than the current page.
  139. # If the current page is equal to the last it returns nil
  140. defp next(data) do
  141. if data.page < data.max_pages, do: data.page + 1, else: nil
  142. end
  143. end
  144.  
  145. defmodule PaginatorTest do
  146. use ConnCase
  147. alias Paginator
  148.  
  149. describe "Test Paginator" do
  150.  
  151. test "page 1 of 15" do
  152. res = Paginator.call %Paginator{total: 150}
  153.  
  154. assert res.first == nil
  155. assert res.previous == nil
  156. assert res.next == 2
  157. assert res.last == 15
  158. assert res.pages == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  159. end
  160.  
  161. test "page 5 of 15" do
  162. res = Paginator.call %Paginator{page: 5, total: 150}
  163.  
  164. assert res.first == 1
  165. assert res.previous == 4
  166. assert res.next == 6
  167. assert res.last == 15
  168. assert res.pages == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  169. end
  170.  
  171. test "page 6 of 15" do
  172. res = Paginator.call %Paginator{page: 6, total: 150}
  173.  
  174. assert res.first == 1
  175. assert res.previous == 5
  176. assert res.next == 7
  177. assert res.last == 15
  178. assert res.pages == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
  179. end
  180.  
  181. test "page 7 of 15" do
  182. res = Paginator.call %Paginator{page: 7, total: 150}
  183.  
  184. assert res.pages == [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  185. end
  186.  
  187. test "page 13 of 15" do
  188. res = Paginator.call %Paginator{page: 13, total: 150}
  189.  
  190. assert res.pages == [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  191.  
  192. end
  193.  
  194. test "per page 50" do
  195. res = Paginator.call %Paginator{page: 25, per_page: 50, total: 2000}
  196.  
  197. assert res.first == 1
  198. assert res.previous == 24
  199. assert res.next == 26
  200. assert res.last == 40
  201. assert res.pages == [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
  202.  
  203. end
  204.  
  205. test "last page" do
  206. res = Paginator.call %Paginator{page: 50, total: 500}
  207.  
  208. assert res.first == 1
  209. assert res.previous == 49
  210. assert res.next == nil
  211. assert res.last == nil
  212. end
  213.  
  214. test "max display" do
  215. res = Paginator.call %Paginator{page: 8, max_display: 5, total: 2000}
  216.  
  217. assert res.first == 1
  218. assert res.previous == 7
  219. assert res.next == 9
  220. assert res.pages == [6, 7, 8, 9, 10]
  221.  
  222. res = Paginator.call %Paginator{page: 9, max_display: 5, total: 2000}
  223. assert res.pages == [7, 8, 9, 10, 11]
  224.  
  225. end
  226.  
  227. test "max results - total more than max" do
  228. res = Paginator.call %Paginator{page: 96, total: 2000, max_results: 1000}
  229.  
  230. assert res.last == 100
  231. assert res.pages == [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
  232.  
  233. end
  234.  
  235. test "max results - max more than total" do
  236. res = Paginator.call %Paginator{page: 96, total: 2000, max_results: 1000}
  237.  
  238. assert res.last == 100
  239. assert res.pages == [91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
  240. end
  241.  
  242. test "no pages - zero total" do
  243. res = Paginator.call %Paginator{total: 0}
  244.  
  245. assert res.first == nil
  246. assert res.previous == nil
  247. assert res.next == nil
  248. assert res.pages == []
  249. end
  250.  
  251. test "no pages - low total" do
  252. res = Paginator.call %Paginator{total: 5}
  253.  
  254. assert res.first == nil
  255. assert res.previous == nil
  256. assert res.next == nil
  257. assert res.pages == []
  258. end
  259. end
  260. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement