lib/scoped_search.rb in wvanbergen-scoped_search-1.2.0 vs lib/scoped_search.rb in wvanbergen-scoped_search-2.0.0

- old
+ new

@@ -1,108 +1,58 @@ module ScopedSearch - + module ClassMethods - def self.extended(base) # :nodoc: - require 'scoped_search/reg_tokens' - require 'scoped_search/query_language_parser' - require 'scoped_search/query_conditions_builder' + # Export the scoped_search method fo defining the search options. + # This method will create a definition instance for the class if it does not yet exist, + # and use the object as block argument and retun value. + def scoped_search(*definitions) + @scoped_search ||= ScopedSearch::Definition.new(self) + definitions.each do |definition| + if definition[:on].kind_of?(Array) + definition[:on].each { |field| @scoped_search.define(definition.merge(:on => field)) } + else + @scoped_search.define(definition) + end + end + return @scoped_search end + + end - # Creates a named scope in the class it was called upon. - # - # fields:: The fields to search on. + module BackwardsCompatibility + # Defines fields to search on using a syntax compatible with scoped_search 1.2 def searchable_on(*fields) - # Make sure that the table to be searched actually exists - if self.table_exists? - - # Get a collection of fields to be searched on. - if fields.first.class.to_s == 'Hash' - if fields.first.has_key?(:only) - # only search on these fields. - fields = fields.first[:only] - elsif fields.first.has_key?(:except) - # Get all the fields and remove any that are in the -except- list. - fields = self.column_names.collect { |column| fields.first[:except].include?(column.to_sym) ? nil : column.to_sym }.compact - end + + options = fields.last.kind_of?(Hash) ? fields.pop : {} + # TODO: handle options? + + fields.each do |field| + if relation = self.reflections.keys.detect { |relation| field.to_s =~ Regexp.new("^#{relation}_(\\w+)$") } + scoped_search(:in => relation, :on => $1.to_sym) + else + scoped_search(:on => field) end - - # Get an array of associate modules. - assoc_models = self.reflections.collect { |key,value| key } - - # Subtract out the fields to be searched on that are part of *this* model. - # Any thing left will be associate module fields to be searched on. - assoc_fields = fields - self.column_names.collect { |column| column.to_sym } - - # Subtraced out the associated fields from the fields so that you are only left - # with fields in *this* model. - fields -= assoc_fields - - # Loop through each of the associate models and group accordingly each - # associate model field to search. Assuming the following relations: - # has_many :clients - # has_many :notes, - # belongs_to :user_type - # assoc_groupings will look like - # assoc_groupings = {:clients => [:first_name, :last_name], - # :notes => [:descr], - # :user_type => [:identifier]} - assoc_groupings = {} - assoc_models.each do |assoc_model| - assoc_groupings[assoc_model] = [] - assoc_fields.each do |assoc_field| - unless assoc_field.to_s.match(/^#{assoc_model.to_s}_/).nil? - assoc_groupings[assoc_model] << assoc_field.to_s.sub(/^#{assoc_model.to_s}_/, '').to_sym - end - end - end - - # If a grouping does not contain any fields to be searched on then remove it. - assoc_groupings = assoc_groupings.delete_if {|group, field_group| field_group.empty?} - - # Set the appropriate class attributes. - self.cattr_accessor :scoped_search_fields, :scoped_search_assoc_groupings - self.scoped_search_fields = fields - self.scoped_search_assoc_groupings = assoc_groupings - self.named_scope :search_for, lambda { |keywords| self.build_scoped_search_conditions(keywords) } end - end + end + end - # Build a hash that is used for the named_scope search_for. - # This function will split the search_string into keywords, and search for all the keywords - # in the fields that were provided to searchable_on. - # - # search_string:: The search string to parse. - def build_scoped_search_conditions(search_string) - if search_string.nil? || search_string.strip.blank? - return {:conditions => nil} - else - query_fields = {} - self.scoped_search_fields.each do |field| - field_name = connection.quote_table_name(table_name) + "." + connection.quote_column_name(field) - query_fields[field_name] = self.columns_hash[field.to_s].type - end - - assoc_model_indx = 0 - assoc_fields_indx = 1 - assoc_models_to_include = [] - self.scoped_search_assoc_groupings.each do |group| - assoc_models_to_include << group[assoc_model_indx] - group[assoc_fields_indx].each do |group_field| - field_name = connection.quote_table_name(group[assoc_model_indx].to_s.pluralize) + "." + connection.quote_column_name(group_field) - query_fields[field_name] = self.reflections[group[assoc_model_indx]].klass.columns_hash[group_field.to_s].type - end - end - - search_conditions = QueryLanguageParser.parse(search_string) - conditions = QueryConditionsBuilder.build_query(search_conditions, query_fields) - - retVal = {:conditions => conditions} - retVal[:include] = assoc_models_to_include unless assoc_models_to_include.empty? + class Exception < StandardError + end - return retVal - end - end + class DefinitionError < ScopedSearch::Exception end + + class QueryNotSupported < ScopedSearch::Exception + end + end -ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods) \ No newline at end of file +# Load all lib files +require 'scoped_search/definition' +require 'scoped_search/adapters' +require 'scoped_search/query_language' +require 'scoped_search/query_builder' + +# Import the search_on method in the ActiveReocrd::Base class +ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods) +ActiveRecord::Base.send(:extend, ScopedSearch::BackwardsCompatibility)