0.0
The project is in a healthy, maintained state
Enable filter parameters to be declared in query classes or controllers.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.16
~> 1.5.4
~> 0.18
~> 1.60.2
~> 2.23.1
~> 2.5.0

Runtime

>= 6.1
 Project Readme

Gem Version RuboCop RSpec Maintainability

Filterameter

Declarative filter parameters provide clean and clear filters for queries.

Usage

Declare filters in query classes or controllers to increase readability and reduce boilerplate code. Filters can be declared for attributes, scopes, or attributes from singular associations (belongs_to or has_one). Validations can also be assigned.

  filter :color
  filter :size, validates: { inclusion: { in: %w[Small Medium Large], allow_multiple_values: true } }
  filter :brand_name, association: :brand, name: :name
  filter :on_sale, association: :price, validates: [{ numericality: { greater_than: 0 } },
                                                    { numericality: { less_than: 100 } }]

Filtering Options

The following options can be specified for each filter.

name

If the name of the parameter is different than the name of the attribute or scope, then use the name parameter to specify the name of the attribute or scope. For example, if the attribute name is current_status but the filter is exposed simply as status use the following:

filter :status, name: :current_status

association

If the attribute or scope is nested, it can be referenced by naming the association. Only singular associations are valid. For example, if the manager_id attribute lives on an employee's department record, use the following:

filter :manager_id, association: :department

validates

If the filter value should be validated, use the validates option along with ActiveModel validations. Here's an example of the inclusion validator being used to restrict sizes:

filter :size, validates: { inclusion: { in: %w[Small Medium Large] } }

The inclusion validator has been overridden to provide the additional option allow_multiple_values. When true, the value can be an array and each entry in the array will be validated. Use this when the filter can specify one or more values.

filter :size, validates: { inclusion: { in: %w[Small Medium Large], allow_multiple_values: true } }

partial

Specify the partial option if the filter should do a partial search (SQL's LIKE). The partial option accepts a hash to specify the search behavior. Here are the available options:

  • match: anywhere (default), from_start, dynamic
  • case_sensitive: true, false (default)

There are two shortcuts: : the partial option can be declared with true, which just uses the defaults; or the partial option can be declared with the match option directly, such as partial: :from_start.

filter :description, partial: true
filter :department_name, partial: :from_start
filter :reason, partial: { match: :dynamic, case_sensitive: true } 

The match options defines where you are searching (which then controls where the wildcard(s) appear):

  • anywhere: adds wildcards at the start and end, for example '%blue%'
  • from_start: adds a wildcard at the end, for example 'blue%'
  • dynamic: adds no wildcards; this enables the client to fully control the search string

range

Specify the range option to enable searches by ranges, minimum values, or maximum values. (All of these are inclusive. A search for a minimum value of $10.00 would include all items priced at $10.00.)

Here are the available options:

  • true: enable ranges, minimum values, and/or maximum values
  • min_only: enables minimum values
  • max_only: enables maximum values

Using the range option means that in addition to the attribute filter minimum and maximum query parameters may also be specified. The parameter names are the attribute name plus the suffix _min or _max.

filter :price, range: true
filter :approved_at, range: :min_only
filter :sale_price, range: :max_only

In the first example, query parameters could include price, price_min, and price_max.

Query Classes

Include module Filterameter::DeclarativeFilters in the query class. The model must be declared using model, and a default query can optionally be declared using default_query. If no default query is provided, then the default is .all.

Example

Here's what a query class for the Widgets model with filters on size and color might look like:

class WidgetQuery
  include Filterameter::DeclarativeFilters

  model Widget
  filter :size
  filter :color
end

Build the query using class method build_query. The method takes two parameters:

  • filter: the hash of filter parameters
  • starting_query: any scope to build on (if not provided, the default query is the starting point)

Here's how the query might be invoked:

filters = { size: 'large', color: 'blue' }
widgets = WidgetQuery.build_query(filters, Widget.limit(10))

Controllers

Include module Filterameter::DeclarativeControllerFilters in the controller. Add before action callback build_filtered_query for controller actions that should build the query.

Rails conventions are used to determine the controller's model as well as the name of the instance variable to apply the filters to. For example, the PhotosController will use the variable @photos to store a query against the Photo model. If the conventions do not provide the correct info, they can be overridden with the following two methods:

filter_model

Provide the name of the model. This method also allows the variable name to be optionally provided as the second parameter.

filter_model 'Picture'

filter_query_var_name

Provide the name of the instance variable. For example, if the query is stored as @data, use the following:

filter_query_var_name :data

Example

In the happy path, the WidgetsController serves Widgets and can filter on size and color. Here's what the controller might look like:

class WidgetController < ApplicationController
  include Filterameter::DeclarativeControllerFilters
  before_action :build_filtered_query, only: :index

  filter :size
  filter :color

  def index
    render json: @widgets
  end
end

Installation

Add this line to your application's Gemfile:

gem 'filterameter'

And then execute:

$ bundle

Or install it yourself as:

$ gem install filterameter

Forms and Query Parameters

The controller mixin will look for filter parameters nested under the filter key. For example, here's what the query parameters might look like for size and color:

?filter[size]=large&filter[color]=blue

On a generic search form, the form_with form helper takes the option scope that allows parameters to be grouped:

<%= form_with url: "/search", scope: :filter, method: :get do |form| %>
  <%= form.label :size, "Size:" %>
  <%= form.text_field :size %>
  <%= form.label :color, "Color:" %>
  <%= form.text_field :color %>
  <%= form.submit "Search" %>
<% end %>

Running Tests

Tests are written in RSpec and the dummy app uses a docker database. The script bin/start_db.sh starts and prepares the test database. It is a one-time step before running the tests.

bin/start_db.rb
bundle exec rspec

The tests can also be run across all the ruby and Rails combinations using appraisal. The install is also a one-time step.

bundle exec appraisal install
bundle exec appraisal rspec

License

The gem is available as open source under the terms of the MIT License.