Module: SearchScope

Included in:
ActiveRecord::Base
Defined in:
lib/search_scope.rb

Defined Under Namespace

Classes: SortScope

Instance Method Summary collapse

Instance Method Details

#get_search_scope_from_object(object, scope, *args) ⇒ Object



84
85
86
87
# File 'lib/search_scope.rb', line 84

def get_search_scope_from_object(object, scope, *args)
  scope_name = "search_#{scope}".intern 
  scope = object.send(scope_name, *args)
end

#paginate_search(params = {}) ⇒ Object

this is for use with will_paginate



148
149
150
151
# File 'lib/search_scope.rb', line 148

def paginate_search(params={})
  params[:paginate] = true
  search params
end

#quick_search_scope_options(quick_search_terms) ⇒ Object

this gets all of the options from the quick_search named scopes and builds a quick_search from them the quick_search is one that matches any of the named scopes, not all of them. TODO look into better ways of doing this, and figure out the proper name.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/search_scope.rb', line 92

def quick_search_scope_options(quick_search_terms)
  conditions = []
  includes = []
  aggregate_scope = self
  
  quick_search_scopes.each do |scope|
    term_conditions = []
    terms = quick_search_terms.split.compact
    terms.each_with_index do |term,index|
      # quick_search_scope = self.send(scope, term)
      quick_search_scope = get_search_scope_from_object(self, scope, term)
      query_options = quick_search_scope.proxy_options
      term_conditions << self.sanitize_sql_for_conditions(query_options[:conditions])
      #only do this once, the first time
      if query_options[:include] && index == 0
        includes << query_options[:include] unless includes.include? query_options[:include]
      end
      extra_options = query_options.keys - [:conditions, :include]
      raise "search_scope with quick_search does not support the #{extra_options.first.inspect} option at this time (#{scope.inspect})" if extra_options.first
    end
    conditions << term_conditions.collect{|c|"(#{c})"}.join(' OR ')     #ORing makes sure any of the terms exist somewhere in any of the fields. I think this is what we actually need, plus "relevance" (does that mean sphinx?)
    # conditions << term_conditions.collect{|c|"(#{c})"}.join(' AND ')  #ANDing this will make it so that all the terms MUST appear in one field, eg author first and last name
  end
  conditions_sql = conditions.collect{|c|"(#{c})"}.join(' OR ')
  {:conditions => conditions_sql, :include => includes}
end

#quick_search_scopesObject

for now, we’ll say that by definition, if a search_scope defines a lambda with one term, then it is a quick_search_scope



54
55
56
# File 'lib/search_scope.rb', line 54

def quick_search_scopes
  @quick_search_scopes ||= []
end

#search(params = {}) ⇒ Object

this searches by chaining all of the named_scopes (search_scopes) that were included in the params



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/search_scope.rb', line 120

def search(params={})
  paginate = params.delete :paginate
  aggregate_scope = self
  search_scopes(params).each do |scope|
    if scope.is_a? Symbol
      # aggregate_scope = aggregate_scope.send(scope)
      aggregate_scope = get_search_scope_from_object(aggregate_scope, scope)
    elsif scope.is_a? Array
      # aggregate_scope = aggregate_scope.send(*scope)
      aggregate_scope = get_search_scope_from_object(aggregate_scope, *scope)
    else
      raise "unsupported type for search scope: #{scope.inspect}"
    end
  end
  unless params[:quick_search].blank?
    aggregate_scope = aggregate_scope.scoped quick_search_scope_options(params[:quick_search])
  end
  if params[:sort_by]
    aggregate_scope = aggregate_scope.scoped sort_search_by_options(params[:sort_by])
  end
  if paginate
    aggregate_scope.paginate(:all, :page => params[:page])
  else
    aggregate_scope.find(:all)
  end
end

#search_scope(name, options = {}, &block) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/search_scope.rb', line 21

def search_scope(name, options = {}, &block)
  puts "***search_scope: #{name.inspect} - #{options.inspect}"
  #default the search to a LIKE search if nothing is given
  if options.blank?
    options = lambda { |term| { :conditions => ["#{table_name}.#{name} LIKE ?", "%#{term}%"] } }
  elsif options.is_a?(Hash) && options[:search_type]
    case options[:search_type]
    when :exact_match
      options = lambda { |term| { :conditions => ["#{table_name}.#{name} = ?", term] } }
    else
      raise "unknown search_type for search_scope: (#{name} - #{options[:search_type]})"
    end
  end
  search_scope_keys << name unless search_scope_keys.include? name
  if options.is_a? Proc
    quick_search_scopes << name if options.arity == 1
  end
  named_scope("search_#{name}".intern, options, &block)
end

#search_scope_keysObject



41
42
43
# File 'lib/search_scope.rb', line 41

def search_scope_keys
  @search_scope_keys ||= []
end

#search_scopes(options = {}) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/search_scope.rb', line 68

def search_scopes(options={})
  scopes = []
  search_scope_keys.each do |key|
    
    #if the scope key isn't in the params, don't include it
    next unless options[key]
    # add the scope once for each term passed in (space delimited). this allows a search for 'star wars' to return only items where both terms match
    terms = options[key].split.compact
    terms.each do |term|
      scopes << [key, term]
    end
  end
  # scopes << [:sort_search_scope, options[:sort_by]] if options[:sort_by]
  scopes
end

#sort_choicesObject



45
46
47
# File 'lib/search_scope.rb', line 45

def sort_choices
  @sort_choices ||= []
end

#sort_choices_hashObject



49
50
51
# File 'lib/search_scope.rb', line 49

def sort_choices_hash
  @sort_choices_hash ||= HashWithIndifferentAccess.new
end

#sort_search_by(name, options = {}) ⇒ Object



10
11
12
13
14
15
16
17
18
19
# File 'lib/search_scope.rb', line 10

def sort_search_by(name, options={})
  options[:label] ||= name.to_s.titleize
  options[:order] ||= name.to_s
  raise "you must supply a Symbol for a name to new_sortable_by (#{name.inspect})" unless name.is_a? Symbol
  return if sort_choices_hash.keys.include? name.to_s
  #TODO put this back and get rid of the return above once I figure out how to reset the class vars when the class is reloaded
  # raise "there is already a sortable defined for the name (#{name.inspect}" if sort_choices_hash.keys.include? name.to_s
  sort_choices_hash[name] = SortScope.new(name, options[:label], options[:order], options[:include])
  sort_choices << sort_choices_hash[name]
end

#sort_search_by_options(sort_by) ⇒ Object



58
59
60
61
62
63
64
65
66
# File 'lib/search_scope.rb', line 58

def sort_search_by_options(sort_by)
  choices_hash = sort_choices_hash[sort_by]
  return {} unless choices_hash
  hash = { 
    :order    => choices_hash.order, 
    :include  => choices_hash.include_model,
  }
  hash
end