module JSONAPI::Utils::Support::Pagination

Constants

RecordCountError

Public Instance Methods

apply_pagination(records, options = {}) click to toggle source

Apply proper pagination to the records.

@param records [ActiveRecord::Relation, Array] collection of records

e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }]

@param options [Hash] JSONAPI::Utils' options

e.g.: { resource: V2::UserResource, count: 100 }

@return [ActiveRecord::Relation, Array]

@api public

# File lib/jsonapi/utils/support/pagination.rb, line 36
def apply_pagination(records, options = {})
  if !apply_pagination?(options) then records
  elsif records.is_a?(Array)     then records[paginate_with(:range)]
  else paginate_with(:paginator).apply(records, nil)
  end
end
include_page_count?() click to toggle source

Check whether pagination's page count should be included on the “meta” key.

@api public @return [Boolean]

# File lib/jsonapi/utils/support/pagination.rb, line 21
def include_page_count?
  JSONAPI.configuration.top_level_meta_include_page_count
end
pagination_params(records, options) click to toggle source

Mount pagination params for JSONAPI::ResourcesOperationResult. It can also be used anywhere else as a helper method.

@param records [ActiveRecord::Relation, Array] collection of records

e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }]

@param options [Hash] JU's options

e.g.: { resource: V2::UserResource, count: 100 }

@return [Hash]

e.g.: {"first"=>{"number"=>1, "size"=>2}, "next"=>{"number"=>2, "size"=>2}, "last"=>{"number"=>2, "size"=>2}}

@api public

# File lib/jsonapi/utils/support/pagination.rb, line 57
def pagination_params(records, options)
  return {} unless include_pagination_links?
  paginator.links_page_params(record_count: record_count_for(records, options))
end
record_count_for(records, options) click to toggle source

Apply memoization to the record count result avoiding duplicate counts.

@param records [ActiveRecord::Relation, Array] collection of records

e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }]

@param options [Hash] JU's options

e.g.: { resource: V2::UserResource, count: 100 }

@return [Integer]

e.g.: 42

@api public

# File lib/jsonapi/utils/support/pagination.rb, line 74
def record_count_for(records, options)
  @record_count ||= count_records(records, options)
end

Private Instance Methods

apply_pagination?(options) click to toggle source

Check whether pagination should be applied to the response.

@return [Boolean]

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 103
def apply_pagination?(options)
  JSONAPI.configuration.default_paginator != :none &&
    (options[:paginate].nil? || options[:paginate])
end
count_records(records, options) click to toggle source

Count records in order to build a proper pagination and to fill up the “record_count” response's member.

@param records [ActiveRecord::Relation, Array] collection of records

e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }]

@param options [Hash] JU's options

e.g.: { resource: V2::UserResource, count: 100 }

@return [Integer]

e.g.: 42

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 171
def count_records(records, options)
  return options[:count].to_i if options[:count].is_a?(Numeric)

  case records
  when ActiveRecord::Relation then count_records_from_database(records, options)
  when Array                  then records.length
  else raise RecordCountError, "Can't count records with the given options"
  end
end
count_records_from_database(records, options) click to toggle source

Count records from the datatase applying the given request filters and skipping things like eager loading, grouping and sorting.

@param records [ActiveRecord::Relation, Array] collection of records

e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }]

@param options [Hash] JU's options

e.g.: { resource: V2::UserResource, count: 100 }

@return [Integer]

e.g.: 42

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 211
def count_records_from_database(records, options)
  records = apply_filter(records, options) if params[:filter].present?
  count   = -> (records, except:) do
    records.except(*except).count(distinct_count_sql(records))
  end
  count.(records, except: %i(includes group order))
rescue ActiveRecord::StatementInvalid
  count.(records, except: %i(group order))
end
distinct_count_sql(records) click to toggle source

Build the SQL distinct count with some reflection on the “records” object.

@param records [ActiveRecord::Relation] collection of records

e.g.: User.all

@return [String]

e.g.: "DISTINCT users.id"

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 230
def distinct_count_sql(records)
  "DISTINCT #{records.table_name}.#{records.primary_key}"
end
page_count_for(record_count) click to toggle source

Count pages in order to build a proper pagination and to fill up the “page_count” response's member.

@param record_count [Integer] number of records

e.g.: 42

@return [Integer]

e.g 5

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 190
def page_count_for(record_count)
  return 0 if record_count.to_i < 1

  size = (page_params['size'] || page_params['limit']).to_i
  size = JSONAPI.configuration.default_page_size unless size.nonzero?
  (record_count.to_f / size).ceil
end
page_params() click to toggle source

Creates an instance of ActionController::Parameters for page params.

@return [ActionController::Parameters]

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 113
def page_params
  @page_params ||= begin
    page = @request.params.to_unsafe_hash['page'] || {}
    ActionController::Parameters.new(page)
  end
end
paginate_with(kind) click to toggle source

Define the paginator or range according to the pagination strategy.

@param kind [Symbol] pagination object's kind

e.g.: :paginator or :range

@return [PagedPaginator, OffsetPaginator, Range]

e.g.: #<PagedPaginator:0x00561ed06dc5a0 @number=1, @size=2>
      0..9

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 130
def paginate_with(kind)
  @pagination ||=
    case kind
    when :paginator then paginator
    when :range     then pagination_range
    end
end
pagination_range() click to toggle source

Define a pagination range for objects which quack like Arrays.

@return [Range]

e.g.: 0..9

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 144
def pagination_range
  case JSONAPI.configuration.default_paginator
  when :paged
    number = page_params['number'].to_i.nonzero? || 1
    size   = page_params['size'].to_i.nonzero?   || JSONAPI.configuration.default_page_size
    (number - 1) * size..number * size - 1
  when :offset
    offset = page_params['offset'].to_i.nonzero? || 0
    limit  = page_params['limit'].to_i.nonzero?  || JSONAPI.configuration.default_page_size
    offset..offset + limit - 1
  else
    paginator.pagination_range(page_params)
  end
end
paginator() click to toggle source

Define the paginator object to be used in the response's pagination.

@return [PagedPaginator, OffsetPaginator]

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 85
def paginator
  @paginator ||= paginator_klass.new(page_params)
end
paginator_klass() click to toggle source

Return the paginator class to be used in the response's pagination.

@return [Paginator]

@api private

# File lib/jsonapi/utils/support/pagination.rb, line 94
def paginator_klass
  "#{JSONAPI.configuration.default_paginator}_paginator".classify.constantize
end