ActiveAdmin Filters—Unlocking the Power of Ransack

For those who aren't familiar with ActiveAdmin, it provides a nice out-of-the-box admin interface for Rails projects that has a pretty solid DSL. If you've never heard of ActiveAdmin or used it before -- check out the documentation!

ActiveAdmin has some pretty great out-of-the-box filters on resource index pages, which lets you search on a field-by-field basis to scope the collection of records. On each index page, any field you have on a given model will get a corresponding filter based on the underlying column's data type or whether or not it represents an association.

ActiveAdmin takes care of the DSL for defining filters as well as how they look on a page, but all the heavy lifting is done behind the scenes with the Ransack gem. If we want to do sweet, sweet things with filters, one must first learn how to harness the power of Ransack.

Understanding Ransack

Ransack provides a string-based query interface that allows you to search against a given model in a number of ways. To illustrate, let's say we had a User model and we wanted to find all the Users who had emails ending in ''. Using Ransack, here's what that search would look like: '').result

The _end is what Ransack refers to as a predicate. The predicate defines how to search. There are a number of available predicates that you can use to construct Ransack queries.

Ransack also supports searching across multiple fields/columns using or. Continuing with our User example, let's say we want to find Users who have a first name or last name that contain 'bob'. Here's the Ransack query: 'bob').result

Ransack also lets you search across associations. If our User had a polymorphic :has_many association to a Preference model that has a content field, we'd be able to search for Users having particular preference content like so: 'movies').result

Ransack gives you a pretty simple way to search your data and knowing how to construct your own Ransack queries gives you a lot of control over how filters function in ActiveAdmin.

Tying Ransack & ActiveAdmin Filters Together

As we just covered, Ransack is the muscle behind ActiveAdmin filters. ActiveAdmin basically gives us a nice UI on top of Ransack. By default, ActiveAdmin will let you define both the value to search for as well as picking the predicate to use.

That gives us a lot of flexibility, which is pretty great.

Knowing this, we're able to customize the filters to fit our needs. Here's a few examples to show the kinds of things you can do.

Filter Without a Predicate Dropdown

If we want our first_name filter to always search for Users containing a given value -- and thus removing the need for a predicate dropdown, we're able to simply provide a predicate instead of a field name:

ActiveAdmin.describe User do
 filter :first_name_cont, label: 'First Name'

Gives us:

Filter for Multiple Fields

Building on the previous example, if we wanted to search across first name and last name under a 'Name' problem!

ActiveAdmin.describe User do
 filter :first_name_or_last_name_cont, label: 'Name'

Gives us:

Filter Across An Association

Borrowing the User preferences example from the Ransack section, we could easily turn that into a filter:

ActiveAdmin.describe User do
 filter :preferences_content_cont, label: 'Preferences'

Gives us:

Multiple Associations with the Same Source Class

If our User model had an association to other Users called favorite_friend and favorite_family_member and we wanted to find all Users having a given User as a favorite friend or family member, here's how we could set up a filter:

ActiveAdmin.describe User do
 filter :favorite_friend_id_or_favorite_family_member_id,
   collection: -> { User.all },
   label:      'Favorite Person'

Gives us:

Wrapping Up

Hopefully you've picked up a trick or two for the next time you need to do some filtering magic in ActiveAdmin. Whether you're filtering in ActiveAdmin or implementing search in a Rails app, learn and master Ransack for great justice!

Ryan is a developer in Viget's Falls Church, VA, HQ, where he believes in being a liason for both the technical and non-technical. He builds elegant tools for clients such as Bozzuto and Millitello Capital—as well as internal tools that we use at Viget every day.

More posts by Ryan