class JsonApiServer::Sort

Description:

Implements sort parameters per JSON API Spec: jsonapi.org/format/#fetching-sorting.

From the spec: β€œThe sort order for each sort field MUST be ascending unless it is prefixed with a minus (U+002D HYPHEN-MINUS, ”-β€œ, in which case it MUST be descending.”

This class (1) whitelists sort params, (2) optionally specifies a default order, and (3) generates a sub-query based on these params.

Usage:

A sort request will look like:

/topics?sort=-created,title

This gets converted to an array. Sort order is ASC by default. Minus = DESC.

['-created', 'title']

Sort attributes are configured like so. :permitted are whitelisted attributes. :default specifies the default sort order. If a user specifies sort params other than those in :permitted, a JsonApiServer::BadRequest exception is raised which renders a 400 error.

{
  permitted: [:id, :title, { created: { col_name: :created_at}],
  default: { id: :desc }
}

In this example, id, title and created (alias for created_at column) are permitted sort params.

Example:

# create sort options
sort_options = {
  permitted: [:id, :title, { created: { col_name: :created_at}],
  default: { id: :desc }
}

# create instance
sort = JsonApiServer::Sort.new(request, Topic, sort_options)

# merge into master query
recent_topics = Topic.recent.merge(sort.query)

# see sort params
puts sort.sort

Note:

JsonApiServer::Builder class provides an easier way to use this class.

Attributes

model[R]

Model passed in constructor.

options[R]

(Hash) Sort options. Specify the attributes that can be used in ActiveRecord query method 'sort'. No sort attributes can be used except for those specified here.

  • permitted - array of model attribute names

  • default - used if no sort params are specified (or rejected)

i.e.,

Allows sorting on model attributes :id, :title, :created. If no sort params are specified in the request, it sorts by 'id' desc.

{
  permitted: [:id, :title, { created: { col_name: :created_at}],
  default: { id: :desc }
}
params[R]

Request query params (request.query_parameters).

request[R]

Controller request object.

Public Class Methods

new(request, model, options = {}) click to toggle source

Params:

- request - instance of request object
- options - sort options. See #options documentation.
# File lib/json_api_server/sort.rb, line 92
def initialize(request, model, options = {})
  @request = request
  @model = model
  @options = options
  @params = request.query_parameters
end

Public Instance Methods

configs() click to toggle source

Instance of JsonApiServer::SortConfigs based on options.

# File lib/json_api_server/sort.rb, line 120
def configs
  @configs ||= JsonApiServer::SortConfigs.new(options)
end
query()
Alias for: relation
relation() click to toggle source

Returns an ActiveRecord::Relation if sort_params are present. Otherwise returns nil. Instance is a query fragment intended to be merged into another query.

Example:

sort = JsonApiServer::Sort.new(request, Comment, options)
Comment.recent.merge!(sort.query)
# File lib/json_api_server/sort.rb, line 108
def relation
  @relation ||= model.order(sort_params) if sort_params.present?
end
Also aliased as: query
sort() click to toggle source

Sort query parameter params.

# File lib/json_api_server/sort.rb, line 115
def sort
  @sort ||= params[:sort].to_s
end
sort_params() click to toggle source

Calculated ActiveRecord 'order' parameters. Use in queries.

# File lib/json_api_server/sort.rb, line 125
def sort_params
  @sort_params ||= begin
    attrs = sort.split(',')
    sort_params = convert(attrs)
    sort_params.empty? ? configs.default_order : sort_params
  end
end

Protected Instance Methods

convert(attrs) click to toggle source

Converts to ActiveRecord query order parameters; whitelists based on configs. Raises JsonApiServer::BadRequest with descriptive message if attribute is not whitelisted.

# File lib/json_api_server/sort.rb, line 138
def convert(attrs)
  whitelisted = []

  attrs.each do |attr|
    attr.strip!
    order = attr.start_with?('-') ? :desc : :asc
    attr_name = order == :desc ? attr.slice(1..attr.length) : attr
    config = configs.config_for(attr_name)
    if config.nil?
      msg = I18n.t('json_api_server.render_400.sort', param: attr_name)
      raise JsonApiServer::BadRequest, msg
    end
    whitelisted << { (config[:col_name] || config[:attr]) => order }
  end

  whitelisted
end