Module: Warped::Controllers::Filterable

Extended by:
ActiveSupport::Concern
Included in:
Ui, Tabulatable
Defined in:
lib/warped/controllers/filterable.rb,
lib/warped/controllers/filterable/ui.rb

Overview

Provides functionality for filtering records from an ActiveRecord::Relation in a controller.

Example usage:

class UsersController < ApplicationController
  include Filterable

  filterable_by :name, :created_at, 'accounts.kind'

  def index
    scope = filter(User.joins(:account))
    render json: scope
  end
end

Example requests:

GET /users?name=John
GET /users?created_at=2020-01-01
GET /users?accounts.kind=premium
GET /users?accounts.kind=premium&accounts.kind.rel=neq

Filters can be combined:

GET /users?name=John&created_at=2020-01-01

Renaming filter keys:

In some cases, you may not want to expose the actual column names to the client. In such cases, you can rename the filter keys by passing a hash to the filterable_by method.

Example:

class UsersController < ApplicationController
  include Filterable

  filterable_by :name, :created_at, 'accounts.kind' => { alias_name: 'kind' }

  def index
    scope = filter(User.joins(:account))
    render json: scope
  end
end

Example requests:

GET /users?kind=premium

Using relations:

In some cases, you may want to filter records based on a relation. For example, you may want to filter users based on operands like:

  • greater than

  • less than

  • not equal

To see the full list of operands, check the Warped::Queries::Filter::RELATIONS constant.

To use the operands, you must pass a parameter appended with ‘.rel`, and the value of a valid operand.

Example requests:

GET /users?created_at=2020-01-01&created_at.rel=gt
GET /users?created_at=2020-01-01&created_at.rel=lt
GET /users?created_at=2020-01-01&created_at.rel=neq

When the operand relation requires multiple values, like in, not_in, or between, you can pass an array of values.

Example requests:

GET /users?created_at[]=2020-01-01&created_at[]=2020-01-03&created_at.rel=in
GET /users?created_at[]=2020-01-01&created_at[]=2020-01-03&created_at.rel=between

Setting types and casting: By default, the filter values are cast to strings. If you want to cast the values to a specific type, and validate that the values are of the correct type, you can pass the kind of the filter to the filterable_by method.

Example:

class UsersController < ApplicationController
  include Filterable

  filterable_by :name, :created_at, 'accounts.active' => { kind: :integer, alias_name: 'active' }

  def index
    scope = filter(User.joins(:account))
    render json: scope
  end
end

Example requests:

GET /users?active=1

The kind parameter will be cast to an integer. If the value is not an integer, an error will be raised, and the response will be a 400 Bad Request.

In order to change the error message, you can rescue from the Filter::ValueError exception, or override the render_invalid_filter_value method.

def render_invalid_filter_value(exception)
  render action_name, status: :bad_request
end

Defined Under Namespace

Modules: Ui

Instance Method Summary collapse

Instance Method Details

#current_action_filter_valuesArray<Warped::Filter::Base::Value>

Returns:



173
174
175
# File 'lib/warped/controllers/filterable.rb', line 173

def current_action_filter_values
  @current_action_filter_values ||= []
end

#current_action_filtersArray<Warped::Filter::Base>

Returns:



168
169
170
# File 'lib/warped/controllers/filterable.rb', line 168

def current_action_filters
  @current_action_filters ||= []
end

#filter(scope, filter_conditions: nil) ⇒ ActiveRecord::Relation

Parameters:

  • scope (ActiveRecord::Relation)
  • filter_conditions (Array<Warped::Filter::Base>|nil) (defaults to: nil)

Returns:

  • (ActiveRecord::Relation)


145
146
147
148
149
150
151
# File 'lib/warped/controllers/filterable.rb', line 145

def filter(scope, filter_conditions: nil)
  action_filters = filter_conditions.presence || filters
  @current_action_filters = action_filters
  @current_action_filter_values = parse_filter_params

  Warped::Queries::Filter.call(scope, filter_conditions: current_action_filter_values.map(&:to_h))
end

#parse_filter_paramsArray<Hash>

Returns:

  • (Array<Hash>)


154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/warped/controllers/filterable.rb', line 154

def parse_filter_params
  current_action_filters.filter_map do |filter|
    raw_value = params[filter.parameter_name]
    raw_relation = params["#{filter.parameter_name}.rel"]

    filter_value = filter.class::Value.new(filter, raw_relation.presence || "eq", raw_value.presence)

    next if filter_value.empty?

    filter_value
  end
end