class ScopedSearch::Definition

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.

Constants

INTEGER_REGXP
NUMERICAL_REGXP

Attributes

default_order[RW]
klass[R]
profile[RW]

Public Class Methods

new(klass) click to toggle source

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.

# File lib/scoped_search/definition.rb, line 182
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

Public Instance Methods

default_fields() click to toggle source

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.

# File lib/scoped_search/definition.rb, line 275
def default_fields
  unique_fields.reject { |field| field.only_explicit }
end
default_fields_for(value, operator = nil) click to toggle source

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

# File lib/scoped_search/definition.rb, line 246
def default_fields_for(value, operator = nil)

  column_types  = []
  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 += [:datetime, :date, :timestamp]  if (parse_temporal(value))

  default_fields.select { |field| column_types.include?(field.type) && !field.set? }
end
define(*args) click to toggle source

Defines a new search field for this search definition.

# File lib/scoped_search/definition.rb, line 280
def define(*args)
  Field.new(self, *args)
end
define_field(name, field) click to toggle source
# File lib/scoped_search/definition.rb, line 199
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) click to toggle source

this method return definitions::field object from string

# File lib/scoped_search/definition.rb, line 221
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?
  end
  field
end
fields() click to toggle source
# File lib/scoped_search/definition.rb, line 208
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) click to toggle source

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

# File lib/scoped_search/definition.rb, line 231
def operator_by_field_name(name)
  field = field_by_name(name)
  return [] if field.nil?
  return field.operators                                      if field.operators
  return ['= ', '!= ']                                        if field.set?
  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) click to toggle source

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.

# File lib/scoped_search/definition.rb, line 260
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) click to toggle source

Returns a reflection for a given klass and name

# File lib/scoped_search/definition.rb, line 285
def reflection_by_name(klass, name)
  return if name.nil?
  klass.reflections[name.to_sym] || klass.reflections[name.to_s]
end
super_definition() click to toggle source
# File lib/scoped_search/definition.rb, line 195
def super_definition
  klass.superclass.try(:scoped_search_definition)
end
unique_fields() click to toggle source
# File lib/scoped_search/definition.rb, line 214
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

Protected Instance Methods

register_complete_for!() click to toggle source

Registers the complete_for method within the class that is used for searching.

# File lib/scoped_search/definition.rb, line 312
def register_complete_for! # :nodoc
  @klass.extend(ScopedSearch::AutoCompleteClassMethods)
end
register_named_scope!() click to toggle source

Registers the search_for named scope within the class that is used for searching.

# File lib/scoped_search/definition.rb, line 293
def register_named_scope! # :nodoc
  @klass.define_singleton_method(:search_for) do |query = '', options = {}|
    # klass may be different to @klass if the scope is called on a subclass
    klass = self
    definition = klass.scoped_search_definition

    search_scope = klass.all
    find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options)
    search_scope = search_scope.where(find_options[:conditions])   if find_options[:conditions]
    search_scope = search_scope.includes(find_options[:include])   if find_options[:include]
    search_scope = search_scope.joins(find_options[:joins])        if find_options[:joins]
    search_scope = search_scope.reorder(find_options[:order])      if find_options[:order]
    search_scope = search_scope.references(find_options[:include]) if find_options[:include]

    search_scope
  end
end