Class: ScopedSearch::Definition

Inherits:
Object
  • Object
show all
Defined in:
lib/scoped_search/definition.rb

Overview

The ScopedSearch definition class defines on what fields should be search in the model in what cases

A definition can be created by calling the scoped_search method on an ActiveRecord-based class, so you should not create an instance of this class yourself.

Defined Under Namespace

Classes: Field

Constant Summary collapse

NUMERICAL_REGXP =
/^\-?\d+(\.\d+)?$/
INTEGER_REGXP =
/^\-?\d+$/
UUID_REGXP =
/^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass) ⇒ Definition

Initializes a ScopedSearch definition instance. This method will also setup a database adapter and create the :search_for named scope if it does not yet exist.


197
198
199
200
201
202
203
204
205
206
# File 'lib/scoped_search/definition.rb', line 197

def initialize(klass)
  @klass                 = klass
  @fields                = {}
  @unique_fields         = []
  @profile_fields        = {:default => {}}
  @profile_unique_fields = {:default => []}

  register_named_scope! unless klass.respond_to?(:search_for)
  register_complete_for! unless klass.respond_to?(:complete_for)
end

Instance Attribute Details

#default_orderObject

Returns the value of attribute default_order


208
209
210
# File 'lib/scoped_search/definition.rb', line 208

def default_order
  @default_order
end

#klassObject (readonly)

Returns the value of attribute klass


192
193
194
# File 'lib/scoped_search/definition.rb', line 192

def klass
  @klass
end

#profileObject

Returns the value of attribute profile


208
209
210
# File 'lib/scoped_search/definition.rb', line 208

def profile
  @profile
end

Instance Method Details

#default_fieldsObject

Returns a list of fields that should be searched on by default.

Every field will show up in this method's result, except for fields for which the only_explicit parameter is set to true.


296
297
298
# File 'lib/scoped_search/definition.rb', line 296

def default_fields
  unique_fields.reject { |field| field.only_explicit }
end

#default_fields_for(value, operator = nil) ⇒ Object

Returns a list of appropriate fields to search in given a search keyword and operator.


266
267
268
269
270
271
272
273
274
275
276
# File 'lib/scoped_search/definition.rb', line 266

def default_fields_for(value, operator = nil)

  column_types  = [:virtual]
  column_types += [:string, :text]                if [nil, :like, :unlike, :ne, :eq].include?(operator)
  column_types += [:double, :float, :decimal]     if value =~ NUMERICAL_REGXP
  column_types += [:integer]                      if value =~ INTEGER_REGXP
  column_types += [:uuid]                         if value =~ UUID_REGXP
  column_types += [:datetime, :date, :timestamp]  if (parse_temporal(value))

  default_fields.select { |field| !field.set? && column_types.include?(field.type) }
end

#define(*args) ⇒ Object

Defines a new search field for this search definition.


301
302
303
# File 'lib/scoped_search/definition.rb', line 301

def define(*args)
  Field.new(self, *args)
end

#define_field(name, field) ⇒ Object


214
215
216
217
218
219
220
221
# File 'lib/scoped_search/definition.rb', line 214

def define_field(name, field)
  @profile ||= :default
  @profile_fields[@profile] ||= {}
  @profile_fields[@profile][name.to_sym] ||= field
  @profile_unique_fields[@profile] ||= []
  @profile_unique_fields[@profile] = (@profile_unique_fields[@profile] + [field]).uniq
  field
end

#field_by_name(name) ⇒ Object

this method return definitions::field object from string


236
237
238
239
240
241
242
243
244
245
246
# File 'lib/scoped_search/definition.rb', line 236

def field_by_name(name)
  field = fields[name.to_sym] unless name.blank?
  if field.nil?
    dotted = name.to_s.split('.')[0]
    field = fields[dotted.to_sym] unless dotted.blank?
    if field && field.key_relation.nil?
      return nil
    end
  end
  field
end

#fieldsObject


223
224
225
226
227
# File 'lib/scoped_search/definition.rb', line 223

def fields
  @profile ||= :default
  @profile_fields[@profile] ||= {}
  super_definition ? super_definition.fields.merge(@profile_fields[@profile]) : @profile_fields[@profile]
end

#operator_by_field_name(name) ⇒ Object

this method is used by the syntax auto completer to suggest operators.


249
250
251
252
253
254
255
256
257
258
259
# File 'lib/scoped_search/definition.rb', line 249

def operator_by_field_name(name)
  field = field_by_name(name)
  return [] if field.nil?
  return field.operators                                          if field.operators
  return ['=', '!=', '>', '<', '<=', '>=', '~', '!~', '^', '!^']  if field.virtual?
  return ['=', '!=']                                              if field.set? || field.uuid?
  return ['=', '>', '<', '<=', '>=', '!=', '^', '!^']             if field.numerical?
  return ['=', '!=', '~', '!~', '^', '!^']                        if field.textual?
  return ['=', '>', '<']                                          if field.temporal?
  raise ScopedSearch::QueryNotSupported, "Unsupported type '#{field.type.inspect}')' for field '#{name}'. This can be a result of a search definition problem."
end

#parse_temporal(value) ⇒ Object

Try to parse a string as a datetime. Supported formats are Today, Yesterday, Sunday, '1 day ago', '2 hours ago', '3 months ago', '4 weeks from now', 'Jan 23, 2004' And many more formats that are documented in Ruby DateTime API Doc.


281
282
283
284
285
286
287
288
289
290
# File 'lib/scoped_search/definition.rb', line 281

def parse_temporal(value)
  return Date.current if value =~ /\btoday\b/i
  return 1.day.ago.to_date if value =~ /\byesterday\b/i
  return 1.day.from_now.to_date if value =~ /\btomorrow\b/i
  return (eval($1.strip.gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bago)\b\s*\z/i
  return (eval($1.strip.gsub(/\s+/,'.').downcase)).to_date     if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bago)\b\s*\z/i
  return (eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bfrom\s+now)\b\s*\z/i
  return (eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)).to_date     if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bfrom\s+now)\b\s*\z/i
  DateTime.parse(value, true) rescue nil
end

#reflection_by_name(klass, name) ⇒ Object

Returns a reflection for a given klass and name


306
307
308
309
# File 'lib/scoped_search/definition.rb', line 306

def reflection_by_name(klass, name)
  return if name.nil?
  klass.reflections[name.to_sym] || klass.reflections[name.to_s]
end

#super_definitionObject


210
211
212
# File 'lib/scoped_search/definition.rb', line 210

def super_definition
  klass.superclass.try(:scoped_search_definition)
end

#unique_fieldsObject


229
230
231
232
233
# File 'lib/scoped_search/definition.rb', line 229

def unique_fields
  @profile ||= :default
  @profile_unique_fields[@profile] ||= []
  super_definition ? (super_definition.unique_fields + @profile_unique_fields[@profile]).uniq : @profile_unique_fields[@profile]
end