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 182 def initialize(klass) 183 @klass = klass 184 @fields = {} 185 @unique_fields = [] 186 @profile_fields = {:default => {}} 187 @profile_unique_fields = {:default => []} 188 189 register_named_scope! unless klass.respond_to?(:search_for) 190 register_complete_for! unless klass.respond_to?(:complete_for) 191 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 275 def default_fields 276 unique_fields.reject { |field| field.only_explicit } 277 end
Returns a list of appropriate fields to search in given a search keyword and operator.
# File lib/scoped_search/definition.rb 246 def default_fields_for(value, operator = nil) 247 248 column_types = [] 249 column_types += [:string, :text] if [nil, :like, :unlike, :ne, :eq].include?(operator) 250 column_types += [:double, :float, :decimal] if value =~ NUMERICAL_REGXP 251 column_types += [:integer] if value =~ INTEGER_REGXP 252 column_types += [:datetime, :date, :timestamp] if (parse_temporal(value)) 253 254 default_fields.select { |field| column_types.include?(field.type) && !field.set? } 255 end
Defines a new search field for this search definition.
# File lib/scoped_search/definition.rb 280 def define(*args) 281 Field.new(self, *args) 282 end
# File lib/scoped_search/definition.rb 199 def define_field(name, field) 200 @profile ||= :default 201 @profile_fields[@profile] ||= {} 202 @profile_fields[@profile][name.to_sym] ||= field 203 @profile_unique_fields[@profile] ||= [] 204 @profile_unique_fields[@profile] = (@profile_unique_fields[@profile] + [field]).uniq 205 field 206 end
this method return definitions::field object from string
# File lib/scoped_search/definition.rb 221 def field_by_name(name) 222 field = fields[name.to_sym] unless name.blank? 223 if field.nil? 224 dotted = name.to_s.split('.')[0] 225 field = fields[dotted.to_sym] unless dotted.blank? 226 end 227 field 228 end
# File lib/scoped_search/definition.rb 208 def fields 209 @profile ||= :default 210 @profile_fields[@profile] ||= {} 211 super_definition ? super_definition.fields.merge(@profile_fields[@profile]) : @profile_fields[@profile] 212 end
this method is used by the syntax auto completer to suggest operators.
# File lib/scoped_search/definition.rb 231 def operator_by_field_name(name) 232 field = field_by_name(name) 233 return [] if field.nil? 234 return field.operators if field.operators 235 return ['= ', '!= '] if field.set? 236 return ['= ', '> ', '< ', '<= ', '>= ','!= ', '^ ', '!^ '] if field.numerical? 237 return ['= ', '!= ', '~ ', '!~ ', '^ ', '!^ '] if field.textual? 238 return ['= ', '> ', '< '] if field.temporal? 239 raise ScopedSearch::QueryNotSupported, "Unsupported type '#{field.type.inspect}')' for field '#{name}'. This can be a result of a search definition problem." 240 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 260 def parse_temporal(value) 261 return Date.current if value =~ /\btoday\b/i 262 return 1.day.ago.to_date if value =~ /\byesterday\b/i 263 return 1.day.from_now.to_date if value =~ /\btomorrow\b/i 264 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 265 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 266 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 267 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 268 DateTime.parse(value, true) rescue nil 269 end
Returns a reflection for a given klass and name
# File lib/scoped_search/definition.rb 285 def reflection_by_name(klass, name) 286 return if name.nil? 287 klass.reflections[name.to_sym] || klass.reflections[name.to_s] 288 end
# File lib/scoped_search/definition.rb 195 def super_definition 196 klass.superclass.try(:scoped_search_definition) 197 end
# File lib/scoped_search/definition.rb 214 def unique_fields 215 @profile ||= :default 216 @profile_unique_fields[@profile] ||= [] 217 super_definition ? (super_definition.unique_fields + @profile_unique_fields[@profile]).uniq : @profile_unique_fields[@profile] 218 end
Protected Instance Methods
Registers the complete_for method within the class that is used for searching.
# File lib/scoped_search/definition.rb 312 def register_complete_for! # :nodoc 313 @klass.extend(ScopedSearch::AutoCompleteClassMethods) 314 end
Registers the search_for named scope within the class that is used for searching.
# File lib/scoped_search/definition.rb 293 def register_named_scope! # :nodoc 294 @klass.define_singleton_method(:search_for) do |query = '', options = {}| 295 # klass may be different to @klass if the scope is called on a subclass 296 klass = self 297 definition = klass.scoped_search_definition 298 299 search_scope = klass.all 300 find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options) 301 search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions] 302 search_scope = search_scope.includes(find_options[:include]) if find_options[:include] 303 search_scope = search_scope.joins(find_options[:joins]) if find_options[:joins] 304 search_scope = search_scope.reorder(find_options[:order]) if find_options[:order] 305 search_scope = search_scope.references(find_options[:include]) if find_options[:include] 306 307 search_scope 308 end 309 end