Filtering
Filters are usually one-liners, with the logic delegated to an Adapter.
allow_filter :title
You can view allow_filter
like a whitelist. We wouldn’t want to
automatically support filters - otherwise sneaky users might filter our
Employee
s to only those making a certain salary. Hence the whitelist.
To customize a filter:
allow_filter :title do |scope, value|
scope.where(title: value)
end
A real-life example might be a prefix query:
allow_filter do |scope, value|
scope.where(["title LIKE ?", "#{value}%"])
end
Filtering Relationships
Prefix the filter parameter with the relevant JSONAPI Type like so:
/blogs?include=posts&filter[posts][title]=foo
Default 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)
end
Default 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)
end
And the following requests:
/posts
/posts?filter[active]=false
The 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 false
NOTE: 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'
end
Filter 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