Class: Philtre::Filter

Inherits:
Object
  • Object
show all
Defined in:
lib/philtre/filter.rb

Overview

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.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filter_parameters = nil, &custom_predicate_block) ⇒ Filter

Returns a new instance of Filter.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 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
  @filter_parameters =
  if filter_parameters
    # preserve 2.0 compatibility
    filter_parameters.inject({}){|ha,(k,v)| ha[k.to_sym] = v; ha}
  else
    {}
  end

  if block_given?
    predicates.extend_with &custom_predicate_block
  end
end

Instance Attribute Details

#filter_parametersObject

Returns the value of attribute filter_parameters.



45
46
47
# File 'lib/philtre/filter.rb', line 45

def filter_parameters
  @filter_parameters
end

#predicatesObject

Hash of predicate names to blocks. One way to get custom predicates is to subclass filter and override this.



92
93
94
95
# File 'lib/philtre/filter.rb', line 92

def predicates
  # don't mess with the class' minimal set
  @predicates ||= self.class.predicates.clone
end

Class Method Details

.predicatesObject



86
87
88
# File 'lib/philtre/filter.rb', line 86

def self.predicates
  @predicates ||= Predicates.new
end

Instance Method Details

#[](key) ⇒ Object

easier access for filter_parameters return nil for nil and ” and []



222
223
224
225
# File 'lib/philtre/filter.rb', line 222

def []( key )
  rv = filter_parameters[key]
  rv unless rv.blank?
end

#[]=(key, value) ⇒ Object

easier access for filter_parameters



228
229
230
# File 'lib/philtre/filter.rb', line 228

def []=(key, value)
  filter_parameters[key] = value
end

#call(dataset) ⇒ Object Also known as: apply

return a modified dataset containing all the predicates



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/philtre/filter.rb', line 50

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

#clone(extra_parameters = {}) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/philtre/filter.rb', line 169

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

#empty?Boolean

Returns:

  • (Boolean)


47
# File 'lib/philtre/filter.rb', line 47

def empty?; filter_parameters.empty? end

#expr_for(predicate, field = nil) ⇒ Object

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.



147
148
149
150
151
# File 'lib/philtre/filter.rb', line 147

def expr_for( predicate, field = nil )
  unless (value = valued_parameters[predicate]).blank?
    to_expr( predicate, value, field )
  end
end

#expr_hashObject

hash of keys to expressions, but only where there are values.



212
213
214
215
216
217
218
# File 'lib/philtre/filter.rb', line 212

def expr_hash
  vary = valued_parameters.map do |key, value|
    [ key, to_expr(key, value) ]
  end

  Hash[ vary ]
end

#expressionsObject

The set of expressions from the filter_parameters with values.



80
81
82
83
84
# File 'lib/philtre/filter.rb', line 80

def expressions
  valued_parameters.map do |key, value|
    to_expr(key, value)
  end
end

#extract!(*keys, &select_block) ⇒ Object

return a subset of filter parameters/predicates, but leave this object without the matching keys. NOTE does not operate on field names.



202
203
204
205
206
207
208
# File 'lib/philtre/filter.rb', line 202

def extract!( *keys, &select_block )
  rv = subset( *keys, &select_block )
  rv.to_h.keys.each do |key|
    filter_parameters.delete( key )
  end
  rv
end

#initialize_copy(*args) ⇒ Object

deallocate any cached lazies



162
163
164
165
166
167
# File 'lib/philtre/filter.rb', line 162

def initialize_copy( *args )
  super
  @order_expressions = nil
  @order_hash = nil
  @order_clause = nil
end

#order_clauseObject

return a possibly empty array of Sequel order expressions



118
119
120
# File 'lib/philtre/filter.rb', line 118

def order_clause
  @order_clause ||= order_expressions.map{|e| e.last}
end

#order_expr(order_predicate) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/philtre/filter.rb', line 99

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

#order_expressionsObject

Associative array (not a Hash) of names to order expressions TODO this should just be a hash



124
125
126
127
128
129
130
131
# File 'lib/philtre/filter.rb', line 124

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

#order_for(order_field) ⇒ Object



113
114
115
# File 'lib/philtre/filter.rb', line 113

def order_for( order_field )
  order_hash[order_field]
end

#order_hashObject



133
134
135
# File 'lib/philtre/filter.rb', line 133

def order_hash
  @order_hash ||= Hash[ order_expressions ]
end

#subset(*keys, &select_block) ⇒ Object

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



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/philtre/filter.rb', line 187

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

#to_expr(key, value, field = nil) ⇒ Object

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.



139
140
141
# File 'lib/philtre/filter.rb', line 139

def to_expr( key, value, field = nil )
  Sequel.expr( predicates[key, value, field] )
end

#to_h(all = false) ⇒ Object

for use in forms



154
155
156
# File 'lib/philtre/filter.rb', line 154

def to_h(all=false)
  filter_parameters.select{|k,v| all || !v.blank?}
end

#valued_parametersObject

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.



73
74
75
76
77
# File 'lib/philtre/filter.rb', line 73

def valued_parameters
  filter_parameters.select do |key,value|
    key.to_sym != :order && (value.is_a?(Array) || !value.blank?)
  end
end