module JSONAPI::Filtering

Public Class Methods

extract_attributes_and_predicates(requested_field) click to toggle source

Parses and returns the attribute and the predicate of a ransack field

@param requested_field [String] the field to parse @return [Array] with the fields and the predicate

# File lib/jsonapi/filtering.rb, line 11
def self.extract_attributes_and_predicates(requested_field)
  predicates = []
  field_name = requested_field.to_s.dup

  while Ransack::Predicate.detect_from_string(field_name).present? do
    predicate = Ransack::Predicate
      .detect_and_strip_from_string!(field_name)
    predicates << Ransack::Predicate.named(predicate)
  end

  [field_name.split(/_and_|_or_/), predicates.reverse]
end

Private Instance Methods

jsonapi_filter(resources, allowed_fields, options = {}) { |resources| ... } click to toggle source

Applies filtering and sorting to a set of resources if requested

The fields follow [Ransack] specifications. See: github.com/activerecord-hackery/ransack#search-matchers

Ex.: `GET /resource?filter=Lisb%&sort=-created_at,id`

@param allowed_fields [Array] a list of allowed fields to be filtered @param options [Hash] extra flags to enable/disable features @return [ActiveRecord::Base] a collection of resources

# File lib/jsonapi/filtering.rb, line 35
def jsonapi_filter(resources, allowed_fields, options = {})
  allowed_fields = allowed_fields.map(&:to_s)
  extracted_params = jsonapi_filter_params(allowed_fields)
  extracted_params[:sorts] = jsonapi_sort_params(allowed_fields, options)
  resources = resources.ransack(extracted_params)
  block_given? ? yield(resources) : resources
end
jsonapi_filter_params(allowed_fields) click to toggle source

Extracts and whitelists allowed fields to be filtered

The fields follow [Ransack] specifications. See: github.com/activerecord-hackery/ransack#search-matchers

@param allowed_fields [Array] a list of allowed fields to be filtered @return [Hash] to be passed to [ActiveRecord::Base#order]

# File lib/jsonapi/filtering.rb, line 50
def jsonapi_filter_params(allowed_fields)
  filtered = {}
  requested = params[:filter] || {}
  allowed_fields = allowed_fields.map(&:to_s)

  requested.each_pair do |requested_field, to_filter|
    field_names, predicates = JSONAPI::Filtering
      .extract_attributes_and_predicates(requested_field)

    wants_array = predicates.any? && predicates.map(&:wants_array).any?

    if to_filter.is_a?(String) && wants_array
      to_filter = to_filter.split(',')
    end

    if predicates.any? && (field_names - allowed_fields).empty?
      filtered[requested_field] = to_filter
    end
  end

  filtered
end
jsonapi_sort_params(allowed_fields, options = {}) click to toggle source

Extracts and whitelists allowed fields (or expressions) to be sorted

@param allowed_fields [Array] a list of allowed fields to be sorted @param options [Hash] extra options to enable/disable features @return [Hash] to be passed to [ActiveRecord::Base#order]

# File lib/jsonapi/filtering.rb, line 78
def jsonapi_sort_params(allowed_fields, options = {})
  filtered = []
  requested = params[:sort].to_s.split(',')

  requested.each do |requested_field|
    if requested_field.to_s.start_with?('-')
      dir = 'desc'
      requested_field = requested_field[1..-1]
    else
      dir = 'asc'
    end

    field_names, predicates = JSONAPI::Filtering
      .extract_attributes_and_predicates(requested_field)

    next unless (field_names - allowed_fields).empty?
    next if !options[:sort_with_expressions] && predicates.any?

    # Convert to strings instead of hashes to allow joined table columns.
    filtered << [requested_field, dir].join(' ')
  end

  filtered
end