Module: Eluvia::PaginationHandler

Extended by:
ActiveSupport::Concern
Defined in:
lib/eluvia/handlers/pagination_handler.rb

Constant Summary collapse

NOT_SET =

Sentinel distinguishing an unset max_limit: kwarg (defer to pagination_max_limit) from an explicit max_limit: nil (no ceiling for this call only, regardless of the config/method).

Object.new.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.order_by_param_name ⇒ Object

Resolves the query parameter name carrying the order_by input according to the configured API case. Exposed so callers outside this concern (e.g. request-level validators run in a before_action ahead of set_pagination) parse the exact same parameter, instead of duplicating this ternary and risking it drifting out of sync with Config.api_case.



9
10
11
# File 'lib/eluvia/handlers/pagination_handler.rb', line 9

def self.order_by_param_name
  Eluvia::Base::Config.api_case == 'camel_case' ? :orderBy : :order_by
end

Instance Method Details

#pagination_max_limit ⇒ Object

Ceiling applied to limit for this controller. Override to customize (e.g. a per-resource value pulled from a resource definition). Defaults to the library-wide config.



19
20
21
# File 'lib/eluvia/handlers/pagination_handler.rb', line 19

def pagination_max_limit
  Eluvia::Base::Config.max_page_limit
end

#set_pagination(default_limit: 20, default_offset: 0, default_order_by: 'created_at:asc', fallback_order_by: 'created_at:asc', force_order_by: false, max_limit: NOT_SET, disable_errors: nil, allow_meta_only_limit: true) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/eluvia/handlers/pagination_handler.rb', line 23

def set_pagination(default_limit: 20, default_offset: 0, default_order_by: 'created_at:asc',
                   fallback_order_by: 'created_at:asc', force_order_by: false, max_limit: NOT_SET,
                   disable_errors: nil, allow_meta_only_limit: true)

  # Pagination
  @limit = resolve_pagination_limit(default_limit: default_limit, max_limit: max_limit,
    disable_errors: disable_errors, allow_meta_only_limit: allow_meta_only_limit)

  @offset = params[:offset] ? params[:offset].to_i : default_offset
  if @limit.zero?
    # Meta-only request (see `allow_meta_only_limit:`) - no records to page through.
    @page = 1
    @padding = 0
  else
    @page = (@offset / @limit) + 1
    @padding = @offset % @limit
  end

  # Order by
  order_by_param = Eluvia::PaginationHandler.order_by_param_name
  user_order_by = params[order_by_param].to_s.strip
  # A blank `order_by` is treated as "not supplied" (falls through to defaults/fallback)
  # rather than as a literal empty ordering, since a Kaminari scope always needs some order.
  order_by = !force_order_by && user_order_by.present? ? user_order_by : default_order_by
  order_by = "#{order_by},#{fallback_order_by}"

  @order_by = {}
  order_by.split(',').each do |entry|
    entry = entry.strip
    # `-1` keeps a trailing empty segment (e.g. `name:`) instead of Ruby's default of
    # dropping it, so it is caught below as an invalid direction rather than as a bare field.
    parts = entry.split(':', -1)
    field_name, direction = parts

    if parts.length != 2 || field_name.blank?
      raise Eluvia::Errors::UnprocessableEntity.new({
        'order_by' => I18n.t('eluvia.errors.ordering.invalid_format',
          default: 'Invalid format, expected "field_name:asc" or "field_name:desc".')
      })
    end

    unless %w[asc desc].include?(direction)
      raise Eluvia::Errors::UnprocessableEntity.new({
        "order_by__#{field_name}" => I18n.t('eluvia.errors.ordering.invalid_direction',
          default: 'Invalid order direction. Allowed values are `asc` and `desc`.')
      })
    end

    @order_by[field_name] = direction.to_sym
  end

end