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 157
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 232
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 206
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(options) click to toggle source

Defines a new search field for this search definition.

# File lib/scoped_search/definition.rb, line 237
def define(options)
  Field.new(self, options)
end
field_by_name(name) click to toggle source

this method return definitions::field object from string

# File lib/scoped_search/definition.rb, line 181
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 170
def fields
  @profile ||= :default
  @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 191
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','Jan 23, 2004' And many more formats that are documented in Ruby DateTime API Doc.

# File lib/scoped_search/definition.rb, line 220
def parse_temporal(value)
  return Date.current if value =~ /\btoday\b/i
  return 1.day.ago.to_date if value =~ /\byesterday\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
  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 242
def reflection_by_name(klass, name)
  return if name.nil?
  klass.reflections[name.to_sym] || klass.reflections[name.to_s]
end
unique_fields() click to toggle source
# File lib/scoped_search/definition.rb, line 175
def unique_fields
  @profile ||= :default
  @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 279
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 250
def register_named_scope! # :nodoc
  definition = self
  @klass.scope(:search_for, proc { |query, options|
    klass = definition.klass

    search_scope = case ActiveRecord::VERSION::MAJOR
      when 3
        klass.scoped
      when 4
        (ActiveRecord::VERSION::MINOR < 1) ? klass.where(nil) : klass.all
      when 5
        klass.all
      else
        raise ScopedSearch::DefinitionError, 'version '                "#{ActiveRecord::VERSION::MAJOR} of activerecord is not supported"
      end

    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] && ActiveRecord::VERSION::MAJOR >= 4

    search_scope
  })
end