Module: APIHelper::Filterable
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/api_helper/filterable.rb
Overview
Helper To Make Resource APIs Filterable
A filterable resource API supports requests to filter resources according to specific criteria, using the filter query parameter.
For example, the following is a request for all products that has a particular color:
GET /products?filter[color]=red
With this approach, multiple filters can be applied to a single request:
GET /products?filter[color]=red&filter[status]=in-stock
Multiple filters are applied with the AND condition.
OR conditions of a single value can be represented as:
GET /products?filter[color]=red,blue,yellow
A few functions: not, greater_then, less_then, greater_then_or_equal, less_then_or_equal, between and like can be used while filtering the data, for example:
GET /products?filter[color]=not(red)
GET /products?filter[price]=greater_then(1000)
GET /products?filter[price]=less_then_or_equal(2000)
GET /products?filter[price]=between(1000,2000)
GET /products?filter[name]=like(%lovely%)
Usage
Include this Concern in your Action Controller:
SamplesController < ApplicationController
include APIHelpers::Filterable
end
or in your Grape API class:
class SampleAPI < Grape::API
include APIHelper::Filterable
end
then use the filter method like this:
resources :products do
get do
@products = filter(Post, filterable_fields: [:name, :price, :color])
# ...
end
end
The filter method will return the scoped model, based directly from the requested URL.
Class Method Summary collapse
-
.filter_param_desc(for_field: nil) ⇒ Object
Return the ‘fields’ param description.
Instance Method Summary collapse
-
#filter(resource, filterable_fields: []) ⇒ Object
Filter resources of a collection from the request parameter.
Class Method Details
.filter_param_desc(for_field: nil) ⇒ Object
Return the ‘fields’ param description
121 122 123 124 125 126 127 |
# File 'lib/api_helper/filterable.rb', line 121 def self.filter_param_desc(for_field: nil) if for_field.present? "Filter data base on the '#{for_field}' field." else "Filter the data." end end |
Instance Method Details
#filter(resource, filterable_fields: []) ⇒ Object
Filter resources of a collection from the request parameter
Params:
resource-
ActiveRecord::BaseorActiveRecord::Relationresource collection to filter data from filterable_fields-
Arrayof Symbols fields that are allowed to be filtered, default to all
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/api_helper/filterable.rb', line 72 def filter(resource, filterable_fields: []) # parse the request parameter if params[:filter].is_a? Hash @filter = params[:filter] filterable_fields = filterable_fields.map(&:to_s) @filter.each_pair do |field, condition| next if filterable_fields.present? && !filterable_fields.include?(field) field = resource.connection.quote_string(field) next if resource.columns_hash[field].blank? field_type = resource.columns_hash[field].type # if a function is used if func = condition.match(/(?<function>[^\(\)]+)\((?<param>.*)\)/) case func[:function] when 'not' values = func[:param].split(',') values.map!(&:to_bool) if field_type == :boolean resource = resource.where.not(field => values) when 'greater_then' resource = resource.where("\"#{resource.table_name}\".\"#{field}\" > ?", func[:param]) when 'less_then' resource = resource.where("\"#{resource.table_name}\".\"#{field}\" < ?", func[:param]) when 'greater_then_or_equal' resource = resource.where("\"#{resource.table_name}\".\"#{field}\" >= ?", func[:param]) when 'less_then_or_equal' resource = resource.where("\"#{resource.table_name}\".\"#{field}\" <= ?", func[:param]) when 'between' param = func[:param].split(',') resource = resource.where("\"#{resource.table_name}\".\"#{field}\" BETWEEN ? AND ?", param.first, param.last) when 'like' resource = resource.where("\"#{resource.table_name}\".\"#{field}\" LIKE ?", func[:param]) when 'null' resource = resource.where(field => nil) end # if not function else values = condition.split(',') values.map!(&:to_bool) if field_type == :boolean resource = resource.where(field => values) end end end return resource end |