class Philtre::Filter
Parse the predicates on the end of field names, and round-trip the search fields between incoming params, controller and views. So,
filter_parameters = { birth_year: ['2012', '2011'], title_like: 'sir', order: ['title', 'name_asc', 'birth_year_desc'], } Philtre.new( filter_parameters ).apply( Personage.dataset ).sql
should result in
SELECT * FROM "personages" WHERE (("birth_year" IN ('2012', '2011')) AND ("title" ~* 'bar')) ORDER BY ("title" ASC, "name" ASC, "date" DESC)
TODO pass a predicates: parameter in here to specify a predicates object.
Attributes
Public Class Methods
# File lib/philtre/filter.rb, line 28 def initialize( filter_parameters = nil, &custom_predicate_block ) # This must be a new instance of Hash, because sometimes # HashWithIndifferentAccess is passed in, which breaks things in here. # Don't use symbolize_keys because that creates a dependency on ActiveSupport # have to iterate anyway to convert keys to symbols @filter_parameters = if filter_parameters filter_parameters.each_with_object(Hash.new){|(k,v),ha| ha[k.to_sym] = v} else {} end if block_given? predicates.extend_with &custom_predicate_block end end
# File lib/philtre/filter.rb, line 97 def self.predicates @predicates ||= Predicates.new end
Public Instance Methods
easier access for filter_parameters
return nil for nil and '' and []
# File lib/philtre/filter.rb, line 234 def []( key ) rv = filter_parameters[key] rv unless rv.blank? end
easier access for filter_parameters
# File lib/philtre/filter.rb, line 240 def []=(key, value) filter_parameters[key] = value end
return a modified dataset containing all the predicates
# File lib/philtre/filter.rb, line 51 def call( dataset ) # mainly for Sequel::Model dataset = dataset.dataset if dataset.respond_to? :dataset # clone here so later order! calls don't mess with a Model's default dataset dataset = expressions.inject(dataset.clone) do |dataset, filter_expr| dataset.filter( filter_expr ) end # preserve existing order if we don't have one. if order_clause.empty? dataset else # There might be multiple orderings in the order_clause dataset.order *order_clause end end
# File lib/philtre/filter.rb, line 181 def clone( extra_parameters = {} ) new_filter = super() # and explicitly clone these because they may well be modified new_filter.filter_parameters = filter_parameters.clone new_filter.predicates = predicates.clone extra_parameters.each do |key,value| new_filter[key] = value end new_filter end
# File lib/philtre/filter.rb, line 48 def empty?; filter_parameters.empty? end
turn the expression at predicate into a Sequel
expression with field, having the value for predicate. Will be nil if the predicate has no value in valued_parameters. Will always be a Sequel::SQL::Expression.
# File lib/philtre/filter.rb, line 158 def expr_for( predicate, field = nil ) unless (value = valued_parameters[predicate]).blank? to_expr( predicate, value, field ) end end
hash of keys to expressions, but only where there are values.
# File lib/philtre/filter.rb, line 224 def expr_hash vary = valued_parameters.map do |key, value| [ key, to_expr(key, value) ] end Hash[ vary ] end
The set of expressions from the filter_parameters
with values.
# File lib/philtre/filter.rb, line 91 def expressions valued_parameters.map do |key, value| to_expr(key, value) end end
return a subset of filter parameters/predicates, but leave this object without the matching keys. NOTE does not operate on field names.
# File lib/philtre/filter.rb, line 214 def extract!( *keys, &select_block ) rv = subset( *keys, &select_block ) rv.to_h.keys.each do |key| filter_parameters.delete( key ) end rv end
deallocate any cached lazies
# File lib/philtre/filter.rb, line 173 def initialize_copy( *args ) super @order_expressions = nil @order_hash = nil @order_clause = nil @valued_parameters = nil end
return a possibly empty array of Sequel
order expressions
# File lib/philtre/filter.rb, line 129 def order_clause @order_clause ||= order_expressions.map{|e| e.last} end
# File lib/philtre/filter.rb, line 110 def order_expr( order_predicate ) return if order_predicate.blank? splitter = PredicateSplitter.new( order_predicate, nil ) case when splitter === :asc Sequel.asc splitter.field when splitter === :desc Sequel.desc splitter.field else Sequel.asc splitter.field end end
Associative array (not a Hash
) of names to order expressions TODO this should just be a hash
# File lib/philtre/filter.rb, line 135 def order_expressions @order_expressions ||= [filter_parameters[:order]].flatten.map do |order_predicate| next if order_predicate.blank? expr = order_expr order_predicate [expr.expression, expr] end.compact end
# File lib/philtre/filter.rb, line 124 def order_for( order_field ) order_hash[order_field] end
# File lib/philtre/filter.rb, line 144 def order_hash @order_hash ||= Hash[ order_expressions ] end
Hash
of predicate names to blocks. One way to get custom predicates is to subclass filter and override this.
# File lib/philtre/filter.rb, line 103 def predicates # don't mess with the class' minimal set @predicates ||= self.class.predicates.clone end
return a new filter including only the specified filter parameters/predicates. NOTE predicates are not the same as field names. args to select_block are the same as to filter_parameters
, ie it's a Hash
TODO should use clone
# File lib/philtre/filter.rb, line 199 def subset( *keys, &select_block ) subset_params = if block_given? filter_parameters.select &select_block else filter_parameters.slice( *keys ) end subset = self.class.new( subset_params ) subset.predicates = predicates.clone subset end
turn a filter_parameter key => value into a Sequel::SQL::Expression subclass field will be the field name ultimately used in the expression. Defaults to key.
# File lib/philtre/filter.rb, line 150 def to_expr( key, value, field = nil ) Sequel.expr( predicates[key, value, field] ) end
for use in forms
# File lib/philtre/filter.rb, line 165 def to_h(all=false) filter_parameters.select{|k,v| all || !v.blank?} end
called by valued_parameters
to generate the set of expressions. This returns true for a value that show up in the set of expressions, false otherwise.
Intended to be overridden if necessary.
# File lib/philtre/filter.rb, line 76 def valued_parameter?( key, value ) value.is_a?(Array) || !value.blank? end
Values in the parameter list which are not blank, and not an ordering. That is, parameters which will be used to generate the filter expression.
# File lib/philtre/filter.rb, line 83 def valued_parameters @valued_parameters ||= filter_parameters.select do |key,value| # :order is special, it must always be excluded key.to_sym != :order && valued_parameter?(key,value) end end