• Stars
    star
    123
  • Rank 290,145 (Top 6 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 11 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

πŸ” Object-oriented query building for ActiveRecord.

⚠️ Warning: This project is no longer maintained.

πŸ“ This project is not compatible with ruby 2.6.6 or rails 6 and it is no longer maintained.

Filterer status coverage codeclimate gem

Filterer lets your users easily filter results from your ActiveRecord models. What does that mean? Let's imagine a page in your application that lists the results of Person.all:

Name              Email           Admin?
----              ----            ----
Adam Becker       [email protected]     true
Barack Obama      [email protected]       false
Joe Biden         [email protected]   true

What if you want to let your users filter the results by name? Or email? Or whether or not the Person is an admin? Where does that logic go?

One answer could be your controller. You could progressively build up a query, like so:

@results = Person.all
@results = @results.where(name: params[:name]) if params[:name].present?
@results = @results.where(email: params[:email]) if params[:email].present?
@results = @results.where(admin: true) if params[:admin].present?

But you can see how that could get ugly fast. Especially when you add in sorting and pagination.

Another answer could be in your models. But passing a bunch of query parameters to a model isn't really a good practice either.

Enter Filterer.

Using Filterer

First, add gem 'filterer' to your Gemfile.

Next, you create a Filterer that looks like this:

# app/filterers/person_filterer.rb

class PersonFilterer < Filterer::Base
  def param_name(x)
    results.where(name: x)
  end

  def param_email(x)
    results.where('LOWER(email) = ?', x)
  end

  def param_admin(x)
    results.where(admin: true)
  end

  # Optional default params
  def defaults
    {
      direction: 'desc'
    }
  end

  # Optional default filters
  def apply_default_filters
    results.where('deleted_at IS NULL')
  end
end

And in your controller:

class PeopleController < ApplicationController
  def index
    @people = Person.filter(params)
  end
end

Now, when a user visits /people, they'll see Adam, Barack, and Joe, all three people. But when they visit /people?name=Adam%20Becker, they'll see only Adam. Or when they visit /people?admin=t, they'll see only Adam and Joe.

Specifying the Filterer class to use

Filterer includes a lightweight ActiveRecord adapter that allows us to call filter on any ActiveRecord::Relation like in the example above. By default, it will look for a class named [ModelName]Filterer. If you wish to override this, you have a couple of options:

You can pass a :filterer_class option to the call to filter:

Person.filter(params, filterer_class: 'AdvancedPersonFilterer')

Or you can bypass the ActiveRecord adapter altogether:

AdvancedPersonFilterer.filter(params, starting_query: Person.all)

Pagination

Filterer relies on either Kaminari or will_paginate for pagination. You must install one of them if you want to paginate your records.

If you have either of the above gems installed, Filterer will automatically paginate your records, fetching the correct page for the ?page=X URL parameter. By default, filterer will display 20 records per page.

Overriding per_page

class PersonFilterer < Filterer::Base
  self.per_page = 30 # defaults to 20
end

Allowing the user to override per_page

class PersonFilterer < Filterer::Base
  self.per_page = 20
  self.allow_per_page_override = true
end

Now you can append ?per_page=50 to the URL.

Note: To prevent abuse, this value will still max-out at 1000 records per page.

Disabling pagination

class NoPaginationFilterer < PersonFilterer
  self.per_page = nil
end

or

Person.filter(params, skip_pagination: true)

Sorting the results

Filterer provides a slightly different DSL for sorting your results. Here's a quick overview of the different ways to use it:

class PersonFilterer < Filterer::Base
  # '?sort=name' will order by LOWER(people.name). If there is no sort parameter,
  # we'll default to this anyway.
  sort_option 'name', 'LOWER(people.name)', default: true

  # '?sort=id' will order by id. This is used as a tiebreaker, so if two records
  # have the same name, the one with the lowest id will come first.
  sort_option 'id', tiebreaker: true

  # '?sort=occupation' will order by occupation, with NULLS LAST.
  sort_option 'occupation', nulls_last: true

  # '?sort=data1', '?sort=data2', etc. will call the following proc, passing the
  # match data. It returns a string that gets passed to the ORDER BY clause.
  sort_option Regexp.new('data([0-9]+)'), -> (matches) {
    "(ratings -> '#{matches[1]}')"
  }
end

Since paginating records without an explicit ORDER BY clause is a no-no, Filterer orders by [table_name].id asc if no sort options are provided.

Disabling the ordering of results

For certain queries, you might want to bypass the ordering of results:

Person.filter(params, skip_ordering: true)

Passing arbitrary data to the Filterer

class OrganizationFilterer < Filterer::Base
  def starting_query
    if opts[:is_admin]
      Organization.all.with_deleted_records
    else
      Organization.all
    end
  end
end

OrganizationFilterer.filter(params, is_admin: current_user.admin?)

License

MIT

More Repositories

1

formbuilder

[Needs Maintainer] Formbuilder is a small graphical interface for letting users build their own webforms.
JavaScript
1,844
star
2

jquery-resizable-columns

Resizable table columns for jQuery.
JavaScript
536
star
3

starrr

1-5 star rating, in jQuery.
JavaScript
220
star
4

openrfps-scrapers

Scraping government contracting opportunities.
CoffeeScript
61
star
5

wintersmith-knowledge-base

A static, developer-friendly, knowledge base built with Wintersmith.
CoffeeScript
55
star
6

formbuilder-rb

[UNMAINTAINED] Rails backend for https://github.com/dobtco/formbuilder
Ruby
53
star
7

procure-io

*NOT CURRENTLY MAINTAINED* Procurement software for the 21st century.
Ruby
41
star
8

dispatch

Dispatch is an application for cities to advertise their contract opportunities.
Ruby
11
star
9

palat

Generate beautiful, accessible color schemes from a single background color.
Ruby
9
star
10

beacon

[Not maintained] Beacon, originally built by CfA Pittsburgh 2015.
Python
4
star
11

writeforhumans.io

A tool that helps you write gooder.
JavaScript
4
star
12

fixing-procurement-ebook

7 Simple Ways to Modernize Enterprise Procurement
CSS
4
star
13

NAICS

Placeholder for NAICS innovation.
3
star
14

autosaver

Wraps your AJAX save logic in a cozy, edge-case-preventing blanket.
CoffeeScript
3
star
15

storage_unit

Soft deletion for Rails 4, done right.
Ruby
2
star
16

simple_form-dropdown_select

A more powerful dropdown input for Simple Form.
Ruby
2
star
17

openrfps-site

Informational website for the OpenRFPs project.
CSS
2
star
18

pretty_id

Add random, unique "pretty" ids to your ActiveRecord models.
Ruby
2
star
19

pretty_file_input

[DEPRECATED] Standardized file inputs for Ruby on Rails.
JavaScript
2
star
20

pdfjs_viewer-rails

fork of https://github.com/senny/pdfjs_viewer-rails
CSS
1
star
21

hubot

our chat bot. thanks github!
CoffeeScript
1
star
22

dvl-core

Base styles for the DOBT View Layer.
Ruby
1
star
23

inline_file_upload

Helper code for handling file uploads via AJAX.
JavaScript
1
star
24

fortitude-caching

Integrates Rails fragment caching with Fortitude.
Ruby
1
star
25

screendoor-project-templates

A shared collection of project templates for Screendoor.
1
star
26

screendoor-nps

Using Screendoor to collect Screendoor's Net Promoter Score.
Ruby
1
star
27

screendoor-appointment-dropdown

Simple integration for adding an "appointment time" dropdown to your Screendoor forms.
Ruby
1
star
28

screendoor-api-docs

Screendoor API Docs
JavaScript
1
star