Instantly share code, notes, and snippets.

Embed
What would you like to do?
Filterable
# 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?
end
results
end
end
end
@steveyken

This comment has been minimized.

steveyken commented Feb 22, 2014

Thanks this is a really useful pattern. Bookmarked!

@skateinmars

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}

Product.filter(params)
@rafeeqskr

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 )
http://www.justinweiss.com/blog/2014/02/17/search-and-filter-rails-models-without-bloating-your-controller/

@jameskerr

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!

@thetrung

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 !!!

@nicolas-besnard

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.

@maebeale

This comment has been minimized.

maebeale commented Oct 2, 2015

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

@mbchandar

This comment has been minimized.

mbchandar commented Jan 13, 2016

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

@krokrob

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.

@asory

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

@gusridd

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 https://gist.github.com/gusridd/9e80f763bae56a425aff310eeef0ae0f ?

@cyb-ahmadh

This comment has been minimized.

cyb-ahmadh commented Jun 9, 2016

That helped a lot. Thanks :) 👍

@guru28

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

@elentras

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
  self.filters.uniq!
end

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
[...]
end

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
@Superpencil

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 }
end
@zx1986

This comment has been minimized.

zx1986 commented Jul 1, 2017

Thank you so much!

@markhallen

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