Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
145 lines (106 sloc) 4.41 KB

Simplificator Filter

An Extension to ActiveRecord in order to manage filtering and ordering of collections.

Install

  gem install simplificator-filter

Usage

You can define filters and orders in your models and then using this definitions to retrieve the data.
See the examples below.

Filter

Definition

Assume you have a product model with name:string, price:integer and available_as_of:date as attributes. Then you could define the following filter definition in your product class.


filter_definition do |filter|
filter.fuzzy_name :strategy => :like, :attribute => ‘name’
filter.price_range :strategy => :between, :attribute => ‘price’
filter.available_as_of :strategy => :equal
end

You can use different strategies according to the attributes type. For string you can define :like, :begins_with, :ends_with or :equal.
The strategy option is not mandatory, if not set then the default strategy for that attributes type is used. For a string type this would be the :like strategy.
(See filter strategies for more information)

If you just want a filter with the same name as the attribute and its default strategy, you can use:

  default_filter_for_attribute :name
  default_filters_for_attributes :name, :price

or just define for all your attributes a default filter:

  default_filters_for_all_attributes

filter_by

Keeping the first definition as example, you can now use the filter_by class method:

  Product.filter_by(:fuzzy_name => 'carpet')
  Product.filter_by(:fuzzy_name => 'carpet', :price_range => '15 - 45')
  Product.filter_by(:fuzzy_name => 'carpet', :price_range => '15 - 45', :available_as_of => 1.day.ago.to_date)

filter_by returns a scope, therefore you can use it with your other scopes or with will_paginate.

  Product.cheap.filter_by(:fuzzy_name => 'carpet').red
  Product.cheap.filter_by(:fuzzy_name => 'carpet').red.paginate(:page => 2)

Associations

You can even filter associated attributes. Let’s add an order model with following associations and filter definition.

  belongs_to :product
  belongs_to :customer

  filter_definition do |filter|
    filter.customer_name :strategy => :like, :attribute => 'customer.name'
    filter.product_name :strategy => :like, :attribute => 'product.name'
    filter.purchased_at :strategy => :equal
  end

The filter_by works still the same way, all necessary tables are included automatically.

  Product.filter_by(:customer_name => 'foo', :product_name => 'magic carpet')

Filter Forms

  <% form_for @products.filter |f| %>
    <%= f.input_field f.fuzzy_name %>
    <%= f.input_field f.price_range %>
    <%= f.input_field f.purchased_at %>
    <%= f.sumbit_tag 'Filter' %>
  <% end %>

In your controller you can just use:


@products = Product.filter_by params[:filter]

View Helpers

Filter Strategies

Type strategies default
string :like, :begins_with, :ends_with, :like
text :like, :begins_with, :ends_with, :like
integer :equal, :between :equal
float :equal, :between :equal
time :equal, :between :equal
date :equal, :between :equal
datetime :equal, :between :equal
binary :equal, :equal
boolean :equal, :equal

Patterns

If you define more than one strategy for an attribute or leave it completly.

  filter_definition do |filter|
    filter.name :strategy => [:begins_with, :ends_with]
    filter.price
  end

In this cases filter_by tries to match a pattern against the passed value to detect the right strategy.
If name matches “* carpet” a :ends_with strategy would be applied, if it would match "magic* " a :begins_with would be used.
If no pattern matches, then default strategy is used.
The only difference between the first and the second filter definition is: If a pattern could be found but it is not included in the array passed, an ArgumentError will be raised.

Patterns

pattern strategy
/^\(.?)\*$/ :like
/^\(.?)$/, :ends_with
/^(.?)\$/, :begins_with
/^(-?\d+)\s?-\s?(-?\d+)$/ :between

Order