Instantly share code, notes, and snippets.

What would you like to do?
# Call scopes directly from your URL params:
# @products = Product.filter(params.slice(:status, :location, :starts_with))
module Filterable
extend ActiveSupport::Concern
module ClassMethods
# Call the class methods with the same name as the keys in <tt>filtering_params</tt>
# with their associated values. Most useful for calling named scopes from
# URL params. Make sure you don't pass stuff directly from the web without
# whitelisting only the params you care about first!
def filter(filtering_params)
results = self.where(nil) # create an anonymous scope
filtering_params.each do |key, value|
results = results.public_send(key, value) if value.present?

This comment has been minimized.

steveyken commented Feb 22, 2014

Thanks this is a really useful pattern. Bookmarked!


This comment has been minimized.

skateinmars commented Feb 25, 2014

This could be seriously dangerous if you don't whitelist the params in the controller:

params = {destroy: 1}


This comment has been minimized.

rafeeqskr commented Sep 17, 2014

@skateinmars If you look at the original blog. we only send that params that were sliced( filtered )


This comment has been minimized.

jameskerr commented Sep 19, 2014

This is a pretty nice way of doing this sort of thing. @justinweiss, what would you do if you needed to do a bit of preprocessing on the params? For example, lets say I want to filter a Post model by Tags. My query string is /posts?tag_ids=10,11,12 Would you put a method in the scope? in the model? in a model concern? What if I need to do this same operation on several models? Interested in hearing your ideas! Thanks!


This comment has been minimized.

thetrung commented Feb 13, 2015

Why don't you make a gem 😄 so we can include in Gemfile ?
Thanks anyway !!!


This comment has been minimized.

nicolas-besnard commented Apr 8, 2015

I think present? isn't the right method to chose. You can't do /hashtags?parent_id=nil.


This comment has been minimized.

maebeale commented Oct 2, 2015

thanks @justinweiss! this is awesome!!! hope all is well w you.


This comment has been minimized.

mbchandar commented Jan 13, 2016

can this be included in the base class? which will then inherit ?


This comment has been minimized.

krokrob commented Feb 16, 2016

Hi @justinweiss! And thanks for the gist 😄
I try it and something went wrong line 16, I can't call the method 'key' (named 'orientation') on my results which is an array of instances of class Product. I do not understand of does that could work.


This comment has been minimized.

asory commented Mar 27, 2016

work in rails 4 ??
sorry how used in view?
i really want used this is simple and useful


This comment has been minimized.

gusridd commented Apr 11, 2016

Thanks for your code it worked for me right from the gist. Nonetheless, I've created a scope with 'joins' so the results could appear more than once. In your opinion, Should I include a 'distinct' in that scope only, or should that behavior be included globally in the filter method as in ?


This comment has been minimized.

cyb-ahmadh commented Jun 9, 2016

That helped a lot. Thanks :) 👍


This comment has been minimized.

guru28 commented Aug 19, 2016

thanks but i have a doubt that how i can create scope for join table


This comment has been minimized.

elentras commented Oct 27, 2016

Thank for this gist, I use it often, but with some extras :

cattr_accessor :filters

def available_filters(*filters)
  self.filters ||= []
  self.filters += scopes

And in method def filter(filtering_params) :

  filtering_params = filtering_params.symbolize_keys.slice(*self.filters)

I use symbolise_keys to ensure it will match with the available_filters.
The slice extract only the authorised params defined in models like this :

class Person < ActiveRecord::Base
  available_scopes :name, :role, :age, :phone_number

And, I use also the role definition on available_filters like this (not handled in my examples) :

  available_scopes :name, :role, :age, :phone_number, as: :admin
  available_scopes :name, :role, as: :user

This comment has been minimized.

Superpencil commented Apr 4, 2017

For Join table (many to many through)

class Item < ApplicationRecord
  has_many :categorizations
  has_many :categories, through: :categorizations

  scope :category, ->(category) { Item.joins(:categories).where category: category }
  scope :other, ->(other) { where other: other }

This comment has been minimized.

zx1986 commented Jul 1, 2017

Thank you so much!


This comment has been minimized.

markhallen commented May 9, 2018

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment