lib/scoped_search/definition.rb in scoped_search-2.3.7 vs lib/scoped_search/definition.rb in scoped_search-2.4.0

- old
+ new

@@ -13,42 +13,86 @@ # # Instances of this class are created when calling scoped_search in your model # class, so you should not create instances of this class yourself. class Field - attr_reader :definition, :field, :only_explicit, :relation, :key_relation, + attr_reader :definition, :field, :only_explicit, :relation, :key_relation, :full_text_search, :key_field, :complete_value, :offset, :word_size, :ext_method, :operators + # Initializes a Field instance given the definition passed to the + # scoped_search call on the ActiveRecord-based model class. + def initialize(definition, options = {}) + @definition = definition + @definition.profile = options[:profile] if options[:profile] + @definition.default_order ||= default_order(options) + + case options + when Symbol, String + @field = field.to_sym + when Hash + @field = options.delete(:on) + + # Set attributes from options hash + @complete_value = options[:complete_value] + @relation = options[:in] + @key_relation = options[:in_key] + @key_field = options[:on_key] + @offset = options[:offset] + @word_size = options[:word_size] || 1 + @ext_method = options[:ext_method] + @operators = options[:operators] + @only_explicit = !!options[:only_explicit] + @full_text_search = options[:full_text_search] + @default_operator = options[:default_operator] if options.has_key?(:default_operator) + end + + # Store this field is the field array + definition.fields[@field] ||= self unless options[:rename] + definition.fields[options[:rename].to_sym] ||= self if options[:rename] + definition.unique_fields << self + + # Store definition for alias / aliases as well + definition.fields[options[:alias].to_sym] ||= self if options[:alias] + options[:aliases].each { |al| definition.fields[al.to_sym] ||= self } if options[:aliases] + end + # The ActiveRecord-based class that belongs to this field. def klass - if relation + @klass ||= if relation related = definition.klass.reflections[relation] raise ScopedSearch::QueryNotSupported, "relation '#{relation}' not one of #{definition.klass.reflections.keys.join(', ')} " if related.nil? related.klass else definition.klass end end + # The ActiveRecord-based class that belongs the key field in a key-value pair. def key_klass - if key_relation + @key_klass ||= if key_relation definition.klass.reflections[key_relation].klass elsif relation definition.klass.reflections[relation].klass else definition.klass end end # Returns the ActiveRecord column definition that corresponds to this field. def column - klass.columns_hash[field.to_s] + @column ||= begin + if klass.columns_hash.has_key?(field.to_s) + klass.columns_hash[field.to_s] + else + raise ActiveRecord::UnknownAttributeError, "#{klass.inspect} doesn't have column #{field.inspect}." + end + end end # Returns the column type of this field. def type - column.type + @type ||= column.type end # Returns true if this field is a datetime-like column def datetime? [:datetime, :time, :timestamp].include?(type) @@ -93,45 +137,10 @@ field_name = options[:on] unless options[:rename] field_name = options[:rename] if options[:rename] order = (options[:default_order].to_s.downcase.include?('desc')) ? "DESC" : "ASC" return "#{field_name} #{order}" end - # Initializes a Field instance given the definition passed to the - # scoped_search call on the ActiveRecord-based model class. - def initialize(definition, options = {}) - @definition = definition - @definition.profile = options[:profile] if options[:profile] - @definition.default_order ||= default_order(options) - - case options - when Symbol, String - @field = field.to_sym - when Hash - @field = options.delete(:on) - - # Set attributes from options hash - @complete_value = options[:complete_value] - @relation = options[:in] - @key_relation = options[:in_key] - @key_field = options[:on_key] - @offset = options[:offset] - @word_size = options[:word_size] || 1 - @ext_method = options[:ext_method] - @operators = options[:operators] - @only_explicit = !!options[:only_explicit] - @default_operator = options[:default_operator] if options.has_key?(:default_operator) - end - - # Store this field is the field array - definition.fields[@field] ||= self unless options[:rename] - definition.fields[options[:rename].to_sym] ||= self if options[:rename] - definition.unique_fields << self - - # Store definition for alias / aliases as well - definition.fields[options[:alias].to_sym] ||= self if options[:alias] - options[:aliases].each { |al| definition.fields[al.to_sym] ||= self } if options[:aliases] - end end attr_reader :klass # Initializes a ScopedSearch definition instance. @@ -183,17 +192,19 @@ return ['= ', '> ', '< '] if field.temporal? raise ScopedSearch::QueryNotSupported, "could not verify '#{name}' type, this can be a result of a definition error" end NUMERICAL_REGXP = /^\-?\d+(\.\d+)?$/ + INTEGER_REGXP = /^\-?\d+$/ # Returns a list of appropriate fields to search in given a search keyword and operator. def default_fields_for(value, operator = nil) column_types = [] column_types += [:string, :text] if [nil, :like, :unlike, :ne, :eq].include?(operator) - column_types += [:integer, :double, :float, :decimal] if value =~ NUMERICAL_REGXP + 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 @@ -234,28 +245,26 @@ find_options = ScopedSearch::QueryBuilder.build_query(self, args[0], args[1]) search_scope = @klass.scoped 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.order(find_options[:order]) if find_options[:order] - search_scope = search_scope.group(find_options[:group]) if find_options[:group] + search_scope = search_scope.reorder(find_options[:order]) if find_options[:order] search_scope }) else raise "This ActiveRecord version is currently not supported!" end else raise "Currently, only ActiveRecord 2.1 or higher is supported!" end end - end -end -# Registers the complete_for method within the class that is used for searching. - def register_complete_for! # :nodoc -@klass.class_eval do - def self.complete_for (query, options = {}) - search_options = ScopedSearch::AutoCompleteBuilder.auto_complete(@scoped_search , query, options) - search_options + # Registers the complete_for method within the class that is used for searching. + def register_complete_for! # :nodoc + @klass.class_eval do + def self.complete_for(query, options = {}) + ScopedSearch::AutoCompleteBuilder.auto_complete(@scoped_search , query, options) + end + end end end - end +end