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
Public Class Methods
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
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
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
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
# 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
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
# 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
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
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
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
# File lib/scoped_search/definition.rb, line 195 def super_definition klass.superclass.try(:scoped_search_definition) end
# 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
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
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