search_scope 0.1.9 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Manifest CHANGED
@@ -4,3 +4,4 @@ lib/search_scope.rb
4
4
  Manifest
5
5
  Rakefile
6
6
  README
7
+ search_scope.tmproj
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('search_scope', '0.1.9') do |p|
5
+ Echoe.new('search_scope', '0.2.1') do |p|
6
6
  p.description = "Simplify searching a model by defining custom named_scopes."
7
7
  p.project = 'search-scope'
8
8
  p.url = "http://rubyforge.org/projects/search-scope"
data/lib/search_scope.rb CHANGED
@@ -7,6 +7,102 @@ module SearchScope
7
7
  end
8
8
  end
9
9
 
10
+
11
+ # filter_scope :author_gender, lambda { |value| { :conditions => ["authors.gender = ?", value], :include => {:authorships => :author} } } do
12
+ # filter_option :male, :label => 'Male Authors'
13
+ # filter_option :female, :label => 'Female Authors'
14
+ # filter_option :unspecified, :scope => :unspecified_author_gender
15
+ # end
16
+
17
+
18
+ class FilterScopeOption
19
+ #options can supply a value to fill in for their filter_scope, or a separate scope (symbol for a named_scope) or conditions/include to use instead.
20
+ attr_reader :key, :value, :label, :scope
21
+ def initialize(key, value, label, scope=nil)
22
+ label ||= key.to_s.titleize
23
+ value ||= key
24
+ value = value.to_s if value.is_a? Symbol
25
+ @key, @value, @label, @scope = key, value, label, scope
26
+ end
27
+ end
28
+
29
+ class FilterScope
30
+ attr_reader :name, :label, :filter_options, :filter_option_keys
31
+ def initialize(name, label, options={}, &block)
32
+ label ||= name.to_s.titleize
33
+ @name, @label = name, label
34
+ @filter_options = HashWithIndifferentAccess.new
35
+ @filter_option_keys = []
36
+ @omit_from_ui = true if options.delete(:omit_from_ui)
37
+ #eval the block so that filter options can be initialized. Should only have calls to filter_option
38
+ instance_eval(&block)
39
+ end
40
+
41
+ def omit_from_ui?
42
+ @omit_from_ui ? true : false
43
+ end
44
+
45
+ def filter_option(name, options={})
46
+ name = name.to_s
47
+ raise "Already defined a filter_option (#{name.inspect}) for filter_scope (#{self.name})}" if filter_option_keys.include? name
48
+ @filter_options_ordered = nil
49
+ filter_option_keys << name
50
+ filter_options[name] = FilterScopeOption.new(name, options[:value], options[:label], options[:scope])
51
+ end
52
+
53
+ def filter_options_ordered
54
+ return @filter_options_ordered if @filter_options_ordered
55
+ @filter_options_ordered = []
56
+ filter_option_keys.each do |key|
57
+ @filter_options_ordered << filter_options[key]
58
+ end
59
+ @filter_options_ordered
60
+ end
61
+
62
+ def key
63
+ @key ||= "filter_#{name}".intern
64
+ end
65
+
66
+ def params_key
67
+ @params_key ||= key.to_s
68
+ end
69
+
70
+ def selected_option_for(params)
71
+ selected_option_key = params[self.params_key]
72
+ self.filter_options[selected_option_key]
73
+ end
74
+
75
+ end
76
+
77
+ #filter scope blocks are not passed through to named_scope. Instead they are eval'd for filter_options.
78
+ def filter_scope(name, proc, options={}, &block)
79
+ proc, options = nil, proc if proc.is_a?(Hash)
80
+ #default the search to a LIKE search if nothing is given
81
+ label = options.delete(:label)
82
+ #setup standard filters
83
+ if proc
84
+ #do nothing, it's custom
85
+ elsif options.blank?
86
+ proc = lambda { |term| { :conditions => ["#{table_name}.#{name} LIKE ?", "%#{term}%"] } }
87
+ elsif options.is_a?(Hash) && options[:search_type]
88
+ case options[:search_type]
89
+ when :exact_match
90
+ proc = lambda { |term| { :conditions => ["#{table_name}.#{name} = ?", term] } }
91
+ else
92
+ raise "unknown search_type for filter_scope: (#{name} - #{options[:search_type]})"
93
+ end
94
+ end
95
+ raise "Already defined a filter_scope with name: #{name.inspect}" if filter_scope_keys.include? name
96
+ @filter_scopes_ordered = nil
97
+
98
+ scope = FilterScope.new name, label, :omit_from_ui => options.delete(:omit_from_ui), &block
99
+
100
+ filter_scope_keys << scope.key
101
+ filter_scopes[scope.key] = scope
102
+
103
+ named_scope(scope.key, proc || options)
104
+ end
105
+
10
106
  def sort_search_by(name, options={})
11
107
  options[:label] ||= name.to_s.titleize
12
108
  if options[:order].blank?
@@ -22,8 +118,6 @@ module SearchScope
22
118
  end
23
119
  options[:order], options[:reverse] = options[:reverse], options[:order] if options[:reverse_orders]
24
120
 
25
- # puts "***(#{self.name}).sort_search_by :#{name}, #{options.inspect}"
26
-
27
121
  raise "you must supply a Symbol for a name to new_sortable_by (#{name.inspect})" unless name.is_a? Symbol
28
122
  return if sort_choices_hash.keys.include? name.to_s
29
123
  #the first sort added becomes the default sorting for all searches
@@ -34,7 +128,7 @@ module SearchScope
34
128
  sort_choices << sort_choices_hash[name]
35
129
  end
36
130
 
37
- def search_scope(name, options = {}, &block)
131
+ def search_scope(name, options={}, &block)
38
132
  #default the search to a LIKE search if nothing is given
39
133
  if options.blank?
40
134
  options = lambda { |term| { :conditions => ["#{table_name}.#{name} LIKE ?", "%#{term}%"] } }
@@ -56,7 +150,11 @@ module SearchScope
56
150
  def search_scope_keys
57
151
  @search_scope_keys ||= []
58
152
  end
59
-
153
+
154
+ def filter_scope_keys
155
+ @filter_scope_keys ||= []
156
+ end
157
+
60
158
  def default_sort_choice
61
159
  @default_sort_choice
62
160
  end
@@ -74,6 +172,49 @@ module SearchScope
74
172
  @quick_search_scopes ||= []
75
173
  end
76
174
 
175
+ def filter_scopes
176
+ @filter_scopes ||= HashWithIndifferentAccess.new
177
+ end
178
+
179
+ def filter_scopes_ordered
180
+ return @filter_scopes_ordered if @filter_scopes_ordered
181
+ @filter_scopes_ordered = []
182
+ filter_scope_keys.each do |key|
183
+ @filter_scopes_ordered << filter_scopes[key]
184
+ end
185
+ @filter_scopes_ordered
186
+ end
187
+
188
+ def active_filter_scopes_for(params, options={}, &block)
189
+ active_filter_scopes = [] unless block
190
+ filter_scopes_ordered.each do |filter_scope|
191
+ selected_option = filter_scope.selected_option_for(params)
192
+ if selected_option
193
+ if block
194
+ yield filter_scope, selected_option
195
+ else
196
+ active_filter_scopes << [filter_scope, selected_option]
197
+ end
198
+ end
199
+ end
200
+ block ? nil : active_filter_scopes
201
+ end
202
+
203
+ def inactive_filter_scopes_for(params, options={}, &block)
204
+ inactive_filter_scopes = [] unless block
205
+ filter_scopes_ordered.each do |filter_scope|
206
+ selected_option = filter_scope.selected_option_for(params)
207
+ unless selected_option
208
+ if block
209
+ yield filter_scope
210
+ else
211
+ inactive_filter_scopes << filter_scope
212
+ end
213
+ end
214
+ end
215
+ block ? nil : inactive_filter_scopes
216
+ end
217
+
77
218
  def sort_search_by_options(sort_by, reverse=nil)
78
219
  if reverse.nil? || reverse.empty?
79
220
  reverse = false
@@ -110,15 +251,15 @@ module SearchScope
110
251
  end
111
252
  scopes
112
253
  end
113
-
114
- def get_named_scope_from_object(object, scope, *args)
115
- scope = object.send(scope, *args)
116
- end
117
254
 
118
- def get_search_scope_from_object(object, scope, *args)
119
- scope_name = "search_#{scope}".intern
255
+ def get_named_scope_from_object(object, scope_name, *args)
120
256
  scope = object.send(scope_name, *args)
121
257
  end
258
+
259
+ def get_search_scope_from_object(object, scope_name, *args)
260
+ scope_name = "search_#{scope_name}".intern
261
+ get_named_scope_from_object object, scope_name, *args
262
+ end
122
263
 
123
264
  def quick_search_scope_options(quick_search_terms, split_terms)
124
265
  conditions = []
@@ -151,11 +292,26 @@ module SearchScope
151
292
  {:conditions => conditions_sql, :include => includes}
152
293
  end
153
294
 
295
+ def extract_filter_params(params)
296
+ filter_params = {}
297
+ params.keys.each do |key|
298
+ key = key.to_s#.intern
299
+ filter_params[key] = params.delete key if filter_scope_keys.include? key.intern
300
+ end
301
+ filter_params
302
+ end
303
+
154
304
  #this searches by chaining all of the named_scopes (search_scopes) that were included in the params
155
305
  def search(params={})
306
+ params = params.clone
307
+ #this lets you do a quick_search like: Book.search('foo') instead of Book.search(:quick_search => 'foo')
308
+ params = {:quick_search => params} unless params.kind_of? Hash
156
309
  params.reverse_merge! :split_terms => true
157
310
  paginate = params.delete :paginate
158
311
  aggregate_scope = self
312
+
313
+ filter_params = extract_filter_params params
314
+
159
315
  search_scopes(params).each do |scope|
160
316
  if scope.is_a? Symbol
161
317
  # aggregate_scope = aggregate_scope.send(scope)
@@ -167,6 +323,24 @@ module SearchScope
167
323
  raise "unsupported type for search scope: #{scope.inspect}"
168
324
  end
169
325
  end
326
+
327
+ filter_params.each do |filter_scope_key, filter_option_key|
328
+ filter_scope_key = filter_scope_key.intern unless filter_scope_key.is_a? Symbol
329
+ filter_option_key = filter_option_key.intern unless filter_option_key.blank? || filter_option_key.is_a?(Symbol)
330
+ next if filter_option_key.blank?
331
+
332
+ filter_scope = filter_scopes[filter_scope_key]
333
+ filter_option = filter_scope.filter_options[filter_option_key]
334
+
335
+ raise "search_scope: No filter option found with key: #{filter_option_key.inspect}" unless filter_option
336
+
337
+ if filter_option.scope
338
+ aggregate_scope = get_named_scope_from_object(aggregate_scope, filter_option.scope)
339
+ else
340
+ aggregate_scope = get_named_scope_from_object(aggregate_scope, filter_scope_key, filter_option.value)
341
+ end
342
+ end
343
+
170
344
  #TODO add filter_scopes here as well
171
345
  aggregate_scope = aggregate_named_scope(aggregate_scope, params)
172
346
 
data/search_scope.gemspec CHANGED
@@ -2,26 +2,25 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{search_scope}
5
- s.version = "0.1.9"
5
+ s.version = "0.2.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Ryan Owens"]
9
- s.date = %q{2009-01-23}
9
+ s.date = %q{2009-07-24}
10
10
  s.description = %q{Simplify searching a model by defining custom named_scopes.}
11
11
  s.email = %q{ryan@infoether.com}
12
12
  s.extra_rdoc_files = ["CHANGELOG", "lib/search_scope.rb", "README"]
13
- s.files = ["CHANGELOG", "init.rb", "lib/search_scope.rb", "Manifest", "Rakefile", "README", "search_scope.gemspec"]
14
- s.has_rdoc = true
13
+ s.files = ["CHANGELOG", "init.rb", "lib/search_scope.rb", "Manifest", "Rakefile", "README", "search_scope.tmproj", "search_scope.gemspec"]
15
14
  s.homepage = %q{http://rubyforge.org/projects/search-scope}
16
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Search_scope", "--main", "README"]
17
16
  s.require_paths = ["lib"]
18
17
  s.rubyforge_project = %q{search-scope}
19
- s.rubygems_version = %q{1.3.1}
18
+ s.rubygems_version = %q{1.3.3}
20
19
  s.summary = %q{Simplify searching a model by defining custom named_scopes.}
21
20
 
22
21
  if s.respond_to? :specification_version then
23
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
- s.specification_version = 2
23
+ s.specification_version = 3
25
24
 
26
25
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
26
  else
@@ -0,0 +1,145 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>currentDocument</key>
6
+ <string>search_scope_notes.txt</string>
7
+ <key>documents</key>
8
+ <array>
9
+ <dict>
10
+ <key>expanded</key>
11
+ <true/>
12
+ <key>name</key>
13
+ <string>search_scope</string>
14
+ <key>regexFolderFilter</key>
15
+ <string>!.*/(\coverage|\.svn|\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
16
+ <key>sourceDirectory</key>
17
+ <string></string>
18
+ </dict>
19
+ </array>
20
+ <key>fileHierarchyDrawerWidth</key>
21
+ <integer>370</integer>
22
+ <key>metaData</key>
23
+ <dict>
24
+ <key>README</key>
25
+ <dict>
26
+ <key>caret</key>
27
+ <dict>
28
+ <key>column</key>
29
+ <integer>0</integer>
30
+ <key>line</key>
31
+ <integer>0</integer>
32
+ </dict>
33
+ <key>firstVisibleColumn</key>
34
+ <integer>0</integer>
35
+ <key>firstVisibleLine</key>
36
+ <integer>0</integer>
37
+ </dict>
38
+ <key>Rakefile</key>
39
+ <dict>
40
+ <key>caret</key>
41
+ <dict>
42
+ <key>column</key>
43
+ <integer>0</integer>
44
+ <key>line</key>
45
+ <integer>15</integer>
46
+ </dict>
47
+ <key>columnSelection</key>
48
+ <false/>
49
+ <key>firstVisibleColumn</key>
50
+ <integer>0</integer>
51
+ <key>firstVisibleLine</key>
52
+ <integer>0</integer>
53
+ <key>selectFrom</key>
54
+ <dict>
55
+ <key>column</key>
56
+ <integer>0</integer>
57
+ <key>line</key>
58
+ <integer>0</integer>
59
+ </dict>
60
+ <key>selectTo</key>
61
+ <dict>
62
+ <key>column</key>
63
+ <integer>0</integer>
64
+ <key>line</key>
65
+ <integer>15</integer>
66
+ </dict>
67
+ </dict>
68
+ <key>lib/search_scope.rb</key>
69
+ <dict>
70
+ <key>caret</key>
71
+ <dict>
72
+ <key>column</key>
73
+ <integer>0</integer>
74
+ <key>line</key>
75
+ <integer>378</integer>
76
+ </dict>
77
+ <key>columnSelection</key>
78
+ <false/>
79
+ <key>firstVisibleColumn</key>
80
+ <integer>0</integer>
81
+ <key>firstVisibleLine</key>
82
+ <integer>211</integer>
83
+ <key>selectFrom</key>
84
+ <dict>
85
+ <key>column</key>
86
+ <integer>0</integer>
87
+ <key>line</key>
88
+ <integer>384</integer>
89
+ </dict>
90
+ <key>selectTo</key>
91
+ <dict>
92
+ <key>column</key>
93
+ <integer>0</integer>
94
+ <key>line</key>
95
+ <integer>378</integer>
96
+ </dict>
97
+ </dict>
98
+ <key>search_scope_notes.txt</key>
99
+ <dict>
100
+ <key>caret</key>
101
+ <dict>
102
+ <key>column</key>
103
+ <integer>0</integer>
104
+ <key>line</key>
105
+ <integer>47</integer>
106
+ </dict>
107
+ <key>firstVisibleColumn</key>
108
+ <integer>0</integer>
109
+ <key>firstVisibleLine</key>
110
+ <integer>36</integer>
111
+ </dict>
112
+ </dict>
113
+ <key>openDocuments</key>
114
+ <array>
115
+ <string>lib/search_scope.rb</string>
116
+ <string>search_scope_notes.txt</string>
117
+ <string>README</string>
118
+ <string>Rakefile</string>
119
+ </array>
120
+ <key>showFileHierarchyDrawer</key>
121
+ <false/>
122
+ <key>showFileHierarchyPanel</key>
123
+ <true/>
124
+ <key>treeState</key>
125
+ <dict>
126
+ <key>search_scope</key>
127
+ <dict>
128
+ <key>isExpanded</key>
129
+ <true/>
130
+ <key>subItems</key>
131
+ <dict>
132
+ <key>lib</key>
133
+ <dict>
134
+ <key>isExpanded</key>
135
+ <true/>
136
+ <key>subItems</key>
137
+ <dict/>
138
+ </dict>
139
+ </dict>
140
+ </dict>
141
+ </dict>
142
+ <key>windowFrame</key>
143
+ <string>{{-1920, -58}, {1920, 1200}}</string>
144
+ </dict>
145
+ </plist>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_scope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Owens
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-23 00:00:00 -05:00
12
+ date: 2009-07-24 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,9 +30,12 @@ files:
30
30
  - Manifest
31
31
  - Rakefile
32
32
  - README
33
+ - search_scope.tmproj
33
34
  - search_scope.gemspec
34
35
  has_rdoc: true
35
36
  homepage: http://rubyforge.org/projects/search-scope
37
+ licenses: []
38
+
36
39
  post_install_message:
37
40
  rdoc_options:
38
41
  - --line-numbers
@@ -58,9 +61,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
61
  requirements: []
59
62
 
60
63
  rubyforge_project: search-scope
61
- rubygems_version: 1.3.1
64
+ rubygems_version: 1.3.3
62
65
  signing_key:
63
- specification_version: 2
66
+ specification_version: 3
64
67
  summary: Simplify searching a model by defining custom named_scopes.
65
68
  test_files: []
66
69