Class: MetaSearch::Where

Inherits:
Object
  • Object
show all
Defined in:
lib/meta_search/where.rb

Overview

Wheres are how MetaSearch does its magic. Wheres have a name (and possible aliases) which are appended to your model and association attributes. When you instantiate a MetaSearch::Builder against a model (manually or by calling your model's search method) the builder responds to methods named for your model's attributes and associations, suffixed by the name of the Where.

These are the default Wheres, broken down by the types of ActiveRecord columns they can search against:

All data types

  • equals (alias: eq) - Just as it sounds.

  • does_not_equal (aliases: ne, noteq) - The opposite of equals, oddly enough.

  • in - Takes an array, matches on equality with any of the items in the array.

  • not_in (aliases: ni, notin) - Like above, but negated.

  • is_null - The column has an SQL NULL value.

  • is_not_null - The column contains anything but NULL.

Strings

  • contains (aliases: like, matches) - Substring match.

  • does_not_contain (aliases: nlike, nmatches) - Negative substring match.

  • starts_with (alias: sw) - Match strings beginning with the entered term.

  • does_not_start_with (alias: dnsw) - The opposite of above.

  • ends_with (alias: ew) - Match strings ending with the entered term.

  • does_not_end_with (alias: dnew) - Negative of above.

Numbers, dates, and times

  • greater_than (alias: gt) - Greater than.

  • greater_than_or_equal_to (aliases: gte, gteq) - Greater than or equal to.

  • less_than (alias: lt) - Less than.

  • less_than_or_equal_to (aliases: lte, lteq) - Less than or equal to.

Booleans

  • is_true - Is true. Useful for a checkbox like “only show admin users”.

  • is_false - The complement of is_true.

Non-boolean data types

  • is_present - As with is_true, useful with a checkbox. Not NULL or the empty string.

  • is_blank - Returns records with a value of NULL or the empty string in the column.

So, given a model like this…

class Article < ActiveRecord::Base
  belongs_to :author
  has_many :comments
  has_many :moderations, :through => :comments
end

…you might end up with attributes like title_contains, comments_title_starts_with, moderations_value_less_than, author_name_equals, and so on.

Additionally, all of the above predicate types also have an _any and _all version, which expects an array of the corresponding parameter type, and requires any or all of the parameters to be a match, respectively. So:

Article.search :author_name_starts_with_any => ['Jim', 'Bob', 'Fred']

will match articles authored by Jimmy, Bobby, or Freddy, but not Winifred.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(where) ⇒ Where

Returns a new instance of Where


68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/meta_search/where.rb', line 68

def initialize(where)
  if [String,Symbol].include?(where.class)
    where = Where.get(where) or raise ArgumentError("A where could not be instantiated for the argument #{where}")
  end
  @name = where[:name]
  @aliases = where[:aliases]
  @types = where[:types]
  @cast = where[:cast]
  @predicate = where[:predicate]
  @validator = where[:validator]
  @formatter = where[:formatter]
  @splat_param = where[:splat_param]
  @skip_compounds = where[:skip_compounds]
end

Instance Attribute Details

#aliasesObject (readonly)

Returns the value of attribute aliases


67
68
69
# File 'lib/meta_search/where.rb', line 67

def aliases
  @aliases
end

#castObject (readonly)

Returns the value of attribute cast


67
68
69
# File 'lib/meta_search/where.rb', line 67

def cast
  @cast
end

#formatterObject (readonly)

Returns the value of attribute formatter


67
68
69
# File 'lib/meta_search/where.rb', line 67

def formatter
  @formatter
end

#nameObject (readonly)

Returns the value of attribute name


67
68
69
# File 'lib/meta_search/where.rb', line 67

def name
  @name
end

#predicateObject (readonly)

Returns the value of attribute predicate


67
68
69
# File 'lib/meta_search/where.rb', line 67

def predicate
  @predicate
end

#typesObject (readonly)

Returns the value of attribute types


67
68
69
# File 'lib/meta_search/where.rb', line 67

def types
  @types
end

#validatorObject (readonly)

Returns the value of attribute validator


67
68
69
# File 'lib/meta_search/where.rb', line 67

def validator
  @validator
end

Class Method Details

.add(*args) ⇒ Object

At application initialization, you can add additional custom Wheres to the mix. in your application's config/initializers/meta_search.rb, place lines like this:

MetaSearch::Where.add :between, :btw,
  :predicate => :in,
  :types => [:integer, :float, :decimal, :date, :datetime, :timestamp, :time],
  :formatter => Proc.new {|param| Range.new(param.first, param.last)},
  :validator => Proc.new {|param|
    param.is_a?(Array) && !(param[0].blank? || param[1].blank?)
  }

The first options are all names for the where. Well, the first is a name, the rest are aliases, really. They will determine the suffix you will use to access your Where.

types is an array of types the comparison is valid for. The where will not be available against columns that are not one of these types. Default is ALL_TYPES, Which is one of several MetaSearch constants available for type assignment (the others being DATES, TIIMES, STRINGS, and NUMBERS).

predicate is the Arel::Attribute predication (read: conditional operator) used for the comparison. Default is :eq, or equality.

formatter is the Proc that will do any formatting to the variables to be substituted. The default proc is {|param| param}, which doesn't really do anything. If you pass a string, it will be evaled in the context of this Proc.

For example, this is the definition of the “contains” Where:

['contains', 'like', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}]

Be sure to single-quote the string, so that variables aren't interpolated until later. If in doubt, just use a Proc.

validator is the Proc that will be used to check whether a parameter supplied to the Where is valid. If it is not valid, it won't be used in the query. The default is {|param| !param.blank?}, so that empty parameters aren't added to the search, but you can get more complex if you desire, like the one in the between example, above.

splat_param, if true, will cause the parameters sent to the predicate in question to be splatted (converted to an argument list). This is not normally useful and defaults to false, but is used when automatically creating compound Wheres (*_any, *_all) so that the Arel attribute method gets the correct parameter list.

skip_compounds will prevent creation of compound condition methods (ending in any or all) for situations where they wouldn't make sense, such as the built-in conditions is_true and is_false.

cast will override the normal cast of the parameter when using this Where condition. Normally, the value supplied to a condition is cast to the type of the column it's being compared against. In cases where this isn't desirable, because the value you intend to accept isn't the same kind of data you'll be comparing against, you can override that cast here, using one of the standard DB type symbols such as :integer, :string, :boolean and so on.


167
168
169
170
# File 'lib/meta_search/where.rb', line 167

def add(*args)
  where = create_where_from_args(*args)
  create_where_compounds_for(where) unless where.skip_compounds?
end

.allObject

Returns the complete array of Wheres


173
174
175
# File 'lib/meta_search/where.rb', line 173

def all
  @@wheres
end

.get(method_id_or_predicate) ⇒ Object

Get the where matching a method or predicate.


178
179
180
181
182
183
184
185
# File 'lib/meta_search/where.rb', line 178

def get(method_id_or_predicate)
  return nil unless where_key = @@wheres.keys.
    sort {|a,b| b.length <=> a.length}.
    detect {|n| method_id_or_predicate.to_s.match(/#{n}=?$/)}
  where = @@wheres[where_key]
  where = @@wheres[where] if where.is_a?(String)
  where
end

.initialize_wheresObject

Set the wheres to their default values, removing any customized settings.


188
189
190
191
192
193
# File 'lib/meta_search/where.rb', line 188

def initialize_wheres
  @@wheres = {}
  DEFAULT_WHERES.each do |where|
    add(*where)
  end
end

Instance Method Details

#evaluate(relation, attributes, param) ⇒ Object

Evaluate the Where for the given relation, attribute, and parameter(s)


102
103
104
105
106
107
108
109
110
# File 'lib/meta_search/where.rb', line 102

def evaluate(relation, attributes, param)
  if splat_param?
    conditions = attributes.map {|a| a.send(predicate, *format_param(param))}
  else
    conditions = attributes.map {|a| a.send(predicate, format_param(param))}
  end

  relation.where(conditions.inject(nil) {|memo, c| memo ? memo.or(c) : c})
end

#format_param(param) ⇒ Object

Format a parameter for searching using the Where's defined formatter.


92
93
94
# File 'lib/meta_search/where.rb', line 92

def format_param(param)
  formatter.call(param)
end

#skip_compounds?Boolean

Returns:

  • (Boolean)

87
88
89
# File 'lib/meta_search/where.rb', line 87

def skip_compounds?
  !!@skip_compounds
end

#splat_param?Boolean

Returns:

  • (Boolean)

83
84
85
# File 'lib/meta_search/where.rb', line 83

def splat_param?
  !!@splat_param
end

#validate(param) ⇒ Object

Validate the parameter for use in a search using the Where's defined validator.


97
98
99
# File 'lib/meta_search/where.rb', line 97

def validate(param)
  validator.call(param)
end