Filtering
Filters are usually one-liners, with the logic delegated to an Adapter.
allow_filter :titleYou can view allow_filter like a whitelist. We wouldn’t want to
automatically support filters - otherwise sneaky users might filter our
Employees to only those making a certain salary. Hence the whitelist.
To customize a filter:
allow_filter :title do |scope, value|
scope.where(title: value)
endA real-life example might be a prefix query:
allow_filter do |scope, value|
scope.where(["title LIKE ?", "#{value}%"])
endFiltering Relationships
Prefix the filter parameter with the relevant JSONAPI Type like so:
/blogs?include=posts&filter[posts][title]=fooDefault Filters
You may want your scope to be filtered any time it is accessed - Perhaps
you only want to show active posts by default:
default_filter :active do |scope|
scope.where(active: true)
endDefault filters can be overridden if there is a corresponding
allow_filter. Given a Resource:
allow_filter :active
default_filter :active do |scope|
scope.where(active: true)
endAnd the following requests:
/posts
/posts?filter[active]=falseThe first will display only active posts, the second will display only inactive posts.
Filter Conventions
There are some common naming conventions for supporting more complex filters:
# greater than
allow_filter :id_gt
# greater than or equal to
allow_filter :id_gte
# less than
allow_filter :id_lt
# less than or equal to
allow_filter :id_lte
# prefix queries
allow_filter :title_prefix
# OR queries
allow_filter :active_or # true or falseNOTE: AND queries are supported by default - just pass a comma-delimited list of values.
Filter Guards
You can conditionally allow filters based on runtime context. Let’s say only managers should be allowed to filter employees by salary:
allow_filter :salary, if: :manager?
def manager?
current_user.role == 'manager'
endFilter Aliases
Aliases mostly come into play when supporting backwards
compatibility. Let’s say we originally called the filter fname then
later wanted the more-expressive first_name. An alias allows is to
keep a one-liner with the correct naming, while still responding correctly
to fname:
allow_filter :first_name, aliases: [:fname]Accessing Runtime Context
allow_filter can access runtime context (in Rails, the controller) as
the last argument:
allow_filter :my_siblings do |scope, value, context|
if value == true
scope.where(family_id: context.current_user.family_id)
else
scope
end
end