|
| 1 | +module JsonApiClient |
| 2 | + module Paginating |
| 3 | + # An alternate, more consistent Paginator that always wraps |
| 4 | + # pagination query string params in a top-level wrapper_name, |
| 5 | + # e.g. page[offset]=2, page[limit]=10. |
| 6 | + class NestedParamPaginator |
| 7 | + DEFAULT_WRAPPER_NAME = "page".freeze |
| 8 | + DEFAULT_PAGE_PARAM = "page".freeze |
| 9 | + DEFAULT_PER_PAGE_PARAM = "per_page".freeze |
| 10 | + |
| 11 | + # Define class accessors as methods to enforce standard way |
| 12 | + # of defining pagination related query string params. |
| 13 | + class << self |
| 14 | + |
| 15 | + def wrapper_name |
| 16 | + @_wrapper_name ||= DEFAULT_WRAPPER_NAME |
| 17 | + end |
| 18 | + |
| 19 | + def wrapper_name=(param = DEFAULT_WRAPPER_NAME) |
| 20 | + raise ArgumentError, "don't wrap wrapper_name" unless valid_param?(param) |
| 21 | + |
| 22 | + @_wrapper_name = param.to_s |
| 23 | + end |
| 24 | + |
| 25 | + def page_param |
| 26 | + @_page_param ||= DEFAULT_PAGE_PARAM |
| 27 | + "#{wrapper_name}[#{@_page_param}]" |
| 28 | + end |
| 29 | + |
| 30 | + def page_param=(param = DEFAULT_PAGE_PARAM) |
| 31 | + raise ArgumentError, "don't wrap page_param" unless valid_param?(param) |
| 32 | + |
| 33 | + @_page_param = param.to_s |
| 34 | + end |
| 35 | + |
| 36 | + def per_page_param |
| 37 | + @_per_page_param ||= DEFAULT_PER_PAGE_PARAM |
| 38 | + "#{wrapper_name}[#{@_per_page_param}]" |
| 39 | + end |
| 40 | + |
| 41 | + def per_page_param=(param = DEFAULT_PER_PAGE_PARAM) |
| 42 | + raise ArgumentError, "don't wrap per_page_param" unless valid_param?(param) |
| 43 | + |
| 44 | + @_per_page_param = param |
| 45 | + end |
| 46 | + |
| 47 | + private |
| 48 | + |
| 49 | + def valid_param?(param) |
| 50 | + !(param.nil? || param.to_s.include?("[") || param.to_s.include?("]")) |
| 51 | + end |
| 52 | + |
| 53 | + end |
| 54 | + |
| 55 | + attr_reader :params, :result_set, :links |
| 56 | + |
| 57 | + def initialize(result_set, data) |
| 58 | + @params = params_for_uri(result_set.uri) |
| 59 | + @result_set = result_set |
| 60 | + @links = data["links"] |
| 61 | + end |
| 62 | + |
| 63 | + def next |
| 64 | + result_set.links.fetch_link("next") |
| 65 | + end |
| 66 | + |
| 67 | + def prev |
| 68 | + result_set.links.fetch_link("prev") |
| 69 | + end |
| 70 | + |
| 71 | + def first |
| 72 | + result_set.links.fetch_link("first") |
| 73 | + end |
| 74 | + |
| 75 | + def last |
| 76 | + result_set.links.fetch_link("last") |
| 77 | + end |
| 78 | + |
| 79 | + def total_pages |
| 80 | + if links["last"] |
| 81 | + uri = result_set.links.link_url_for("last") |
| 82 | + last_params = params_for_uri(uri) |
| 83 | + last_params.fetch(page_param, &method(:current_page)).to_i |
| 84 | + else |
| 85 | + current_page |
| 86 | + end |
| 87 | + end |
| 88 | + |
| 89 | + # this is an estimate, not necessarily an exact count |
| 90 | + def total_entries |
| 91 | + per_page * total_pages |
| 92 | + end |
| 93 | + def total_count; total_entries; end |
| 94 | + |
| 95 | + def offset |
| 96 | + per_page * (current_page - 1) |
| 97 | + end |
| 98 | + |
| 99 | + def per_page |
| 100 | + params.fetch(per_page_param) do |
| 101 | + result_set.length |
| 102 | + end.to_i |
| 103 | + end |
| 104 | + |
| 105 | + def current_page |
| 106 | + params.fetch(page_param, 1).to_i |
| 107 | + end |
| 108 | + |
| 109 | + def out_of_bounds? |
| 110 | + current_page > total_pages |
| 111 | + end |
| 112 | + |
| 113 | + def previous_page |
| 114 | + current_page > 1 ? (current_page - 1) : nil |
| 115 | + end |
| 116 | + |
| 117 | + def next_page |
| 118 | + current_page < total_pages ? (current_page + 1) : nil |
| 119 | + end |
| 120 | + |
| 121 | + def page_param |
| 122 | + self.class.page_param |
| 123 | + end |
| 124 | + |
| 125 | + def per_page_param |
| 126 | + self.class.per_page_param |
| 127 | + end |
| 128 | + |
| 129 | + alias limit_value per_page |
| 130 | + |
| 131 | + protected |
| 132 | + |
| 133 | + def params_for_uri(uri) |
| 134 | + return {} unless uri |
| 135 | + uri = Addressable::URI.parse(uri) |
| 136 | + ( uri.query_values || {} ).with_indifferent_access |
| 137 | + end |
| 138 | + end |
| 139 | + end |
| 140 | +end |
0 commit comments