lib/scoped_search/definition.rb in scoped_search-3.3.0 vs lib/scoped_search/definition.rb in scoped_search-4.0.0

- old
+ new

@@ -14,48 +14,80 @@ # 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, :full_text_search, - :key_field, :complete_value, :complete_enabled, :offset, :word_size, :ext_method, :operators + :key_field, :complete_value, :complete_enabled, :offset, :word_size, :ext_method, :operators, + :validator # 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) + # + # Field name may be given in positional 'field' argument or 'on' named + # argument. + def initialize(definition, + field = nil, + aliases: [], + complete_enabled: true, + complete_value: nil, + default_operator: nil, + default_order: nil, + ext_method: nil, + full_text_search: nil, + in_key: nil, + offset: nil, + on: field, + on_key: nil, + only_explicit: nil, + operators: nil, + profile: nil, + relation: nil, + rename: nil, + validator: nil, + word_size: 1, + **kwargs) - case options - when Symbol, String - @field = field.to_sym - when Hash - @field = options.delete(:on) + # Prefer 'on' kw arg if given, defaults to the 'field' positional to allow either syntax + raise ArgumentError, "Missing field or 'on' keyword argument" if on.nil? + @field = on.to_sym - # 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) - @complete_enabled = options[:complete_enabled].nil? ? true : options[:complete_enabled] + # Reserved Ruby keywords so access via kwargs instead, but deprecate them for future versions + if kwargs.key?(:in) + relation = kwargs.delete(:in) + ActiveSupport::Deprecation.warn("'in' argument deprecated, prefer 'relation' since scoped_search 4.0.0", caller(6)) end + if kwargs.key?(:alias) + aliases += [kwargs.delete(:alias)] + ActiveSupport::Deprecation.warn("'alias' argument deprecated, prefer aliases: [..] since scoped_search 4.0.0", caller(6)) + end + raise ArgumentError, "Unknown arguments to scoped_search: #{kwargs.keys.join(', ')}" unless kwargs.empty? - # 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 + @definition = definition + @definition.profile = profile if profile + @definition.default_order ||= generate_default_order(default_order, rename || @field) if default_order - # 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] + # Set attributes from keyword arguments + @complete_enabled = complete_enabled + @complete_value = complete_value + @default_operator = default_operator + @ext_method = ext_method + @full_text_search = full_text_search + @key_field = on_key + @key_relation = in_key + @offset = offset + @only_explicit = !!only_explicit + @operators = operators + @relation = relation + @validator = validator + @word_size = word_size + + # Store this field in the field array + definition.fields[rename ? rename.to_sym : @field] ||= self + definition.unique_fields << self + + # Store definition for aliases as well + aliases.each { |al| definition.fields[al.to_sym] ||= self } end # The ActiveRecord-based class that belongs to this field. def klass @klass ||= if relation @@ -80,15 +112,11 @@ def column @column ||= begin if klass.columns_hash.has_key?(field.to_s) klass.columns_hash[field.to_s] else - if "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}".to_f < 4.1 - raise ActiveRecord::UnknownAttributeError, "#{klass.inspect} doesn't have column #{field.inspect}." - else - raise ActiveRecord::UnknownAttributeError.new(klass, field) - end + raise ActiveRecord::UnknownAttributeError.new(klass, field) end end end # Returns the column type of this field. @@ -133,15 +161,13 @@ when :string, :text then :like else :eq end end - def default_order(options) - return nil if options[:default_order].nil? - field_name = options[:rename].nil? ? options[:on] : options[:rename] - order = (options[:default_order].to_s.downcase.include?('desc')) ? "DESC" : "ASC" - return "#{field_name} #{order}" + def generate_default_order(default_order, field) + order = (default_order.to_s.downcase.include?('desc')) ? "DESC" : "ASC" + return "#{field} #{order}" end # Return 'table'.'column' with the correct database quotes def quoted_field c = klass.connection @@ -232,12 +258,12 @@ def default_fields unique_fields.reject { |field| field.only_explicit } end # Defines a new search field for this search definition. - def define(options) - Field.new(self, options) + def define(*args) + Field.new(self, *args) end # Returns a reflection for a given klass and name def reflection_by_name(klass, name) return if name.nil? @@ -250,27 +276,16 @@ 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 - + 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] && ActiveRecord::VERSION::MAJOR >= 4 + search_scope = search_scope.references(find_options[:include]) if find_options[:include] search_scope }) end