DynamicSifter is a safe way to simplify your search-and-sieve (search-and-filter) type searches in your ActiveRecord classes.
DynamicSifter takes advantage of named_scopes and uses them as filters for your searches.
Key features:
- Takes the same arguments as any ActiveRecord#find call
- Allows you to chain filters together
- Can now use filters that take lambda methods
- Update README.textile to be more correct
You can add DynamicSifter to any application that uses ActiveRecord >= 2.1.0. You can add it to a Rails application by doing the following:
git submodule add git://github.com/jkatz/dynamic_sifter.git vendor/plugins/dynamic_sifterTo illustrate how to use DynamicSifter, let us use this example class:
class Book < ActiveRecord::Base named_scope :in_stock, :conditions => [‘quantity > 0’] named_scope :history, :conditions => { :genre => ‘history’ } named_scope :paperback, :conditions => { :paperback => true } named_scope :author => lambda { |author_name| :conditions => [‘author = ?’, author_name] }- …
end
Let us say we have a form on our application that allows us to search for books with various attributes. We want to be able to get all the results in one-line of code, without worrying about writing complex boolean statements to apply the correct filters. To make a hard-coded, concrete example, to find all the history books that are in_stock, all we have to do is:
DynamicSifter.search(Book, :filters => [:history, :in_stock])And DynamicSifter will returns our results just like any ActiveRecord::#find query!
To get more complex, let’s say I want to search for all the books by Yukio Mishima that are in stock and are in paperback. It would be silly to have a named scope for every author in our database! With DynamicSifter, we would do the following:
DynamicSifter.search(Book, :conditions => { :author => ‘Yukio Mishima’ }, :filters => [:in_stock, :paperback])Let’s say we store our publisher contact information in a Publisher model. If we wanted to find the all the publishers for the books by Yukio Mishima that are paperback, in-stock, and have the list of publishers by in alphabetical order by name, we could do:
DynamicSifter.search(Book, :joins => :publisher, :conditions => { :author => ‘Yukio Mishima’ }, :select => ‘DISTINCT publishers.name’, :order => ‘publishers.name’, :filters => [:in_stock, :paper_back] )Finally, we got smart and decided to put in a named_scope that let’s us filter by author. We want to filter by author and paperback, we can do:
DynamicSifter.search(Book, :filters => { :author => [‘Yukio Mishima’], :paperback => [] })Easy!
DynamicSifter grew out of a need to find an easy way to chain search filters triggered by user inputs. An example of this is maintaining a list of contacts and sorting them by various information, e.g. breaking it down by name, particular attributes about the contacts, etc. Because I wanted to limit the amount of code in my Rails controllers for handling this, I wanted a dynamic, safe way to update my information without too much hassle. In one use of this, I actually have the users trigger the scopes, but the code in DynamicSifter ensures that the scope actually exists and that the user is not trying to do anything too funky.
- Specs, specs, specs!
- Better support for scopes on includes
- Support for other ORMs?