meta_search 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/Gemfile +2 -8
- data/README.rdoc +15 -1
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/lib/meta_search/builder.rb +119 -129
- data/lib/meta_search/helpers/form_helper.rb +14 -9
- data/lib/meta_search/helpers/url_helper.rb +2 -2
- data/lib/meta_search/join_dependency.rb +11 -7
- data/lib/meta_search/model_compatibility.rb +23 -20
- data/lib/meta_search/searches/active_record.rb +72 -70
- data/lib/meta_search/utility.rb +14 -0
- data/lib/meta_search/where.rb +0 -1
- data/lib/meta_search.rb +5 -5
- data/meta_search.gemspec +16 -14
- data/test/fixtures/developer.rb +4 -1
- data/test/helper.rb +2 -0
- data/test/locales/en.yml +36 -0
- data/test/locales/es.yml +5 -0
- data/test/test_search.rb +11 -0
- data/test/test_view_helpers.rb +74 -0
- metadata +14 -12
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
Changes since 0.9.8 (2010-10-20):
|
2
|
+
* ARel 2.x and Rails 3.0.2 compatability
|
3
|
+
* sort_link uses search_key from builder. Search_key defaults to "search"
|
4
|
+
* sort_link will localize attribute names.
|
5
|
+
* You can now create two scopes on your model named sort_by_something_asc
|
6
|
+
and sort_by_something_desc, and sort_link will then allow you to specify
|
7
|
+
:something as a parameter, then use your scope to perform custom sorting.
|
8
|
+
|
1
9
|
Changes since 0.9.7 (2010-10-12):
|
2
10
|
* Play nicely regardless of MetaWhere/MetaSearch load order.
|
3
11
|
* Big fix - stop altering the supplied hash in Builder#build.
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -309,13 +309,27 @@ Normally, you won't supply this parameter yourself, but instead will use the hel
|
|
309
309
|
<tt>sort_link</tt> in your views, like so:
|
310
310
|
|
311
311
|
<%= sort_link @search, :title %>
|
312
|
-
|
312
|
+
|
313
|
+
Or, if in the context of a form_for against a MetaSearch::Builder:
|
314
|
+
|
315
|
+
<%= f.sort_link :title %>
|
313
316
|
|
314
317
|
The <tt>@search</tt> object is the instance of MetaSearch::Builder you got back earlier from
|
315
318
|
your controller. The other required parameter is the attribute name itself. Optionally,
|
316
319
|
you can provide a string as a 3rd parameter to override the default link name, and then
|
317
320
|
additional hashed for the +options+ and +html_options+ hashes for link_to.
|
318
321
|
|
322
|
+
You can sort by more than one column as well, by creating a link like:
|
323
|
+
|
324
|
+
<%= sort_link :name_and_salary %>
|
325
|
+
|
326
|
+
If you'd like to do a custom sort, you can do so by setting up two scopes in your model:
|
327
|
+
|
328
|
+
scope :sort_by_custom_name_asc, order('custom_name ASC')
|
329
|
+
scope :sort_by_custom_name_desc, order('custom_name DESC')
|
330
|
+
|
331
|
+
You can then do <tt>sort_link @search, :custom_name</tt> and it will work as you expect.
|
332
|
+
|
319
333
|
All <tt>sort_link</tt>-generated links will have the CSS class sort_link, as well as a
|
320
334
|
directional class (ascending or descending) if the link is for a currently sorted column,
|
321
335
|
for your styling enjoyment.
|
data/Rakefile
CHANGED
@@ -15,10 +15,10 @@ begin
|
|
15
15
|
gem.homepage = "http://metautonomo.us/projects/metasearch/"
|
16
16
|
gem.authors = ["Ernie Miller"]
|
17
17
|
gem.add_development_dependency "shoulda"
|
18
|
-
gem.add_dependency "activerecord", "~> 3.0.
|
19
|
-
gem.add_dependency "activesupport", "~> 3.0.
|
20
|
-
gem.add_dependency "actionpack", "~> 3.0.
|
21
|
-
gem.add_dependency "arel", "~>
|
18
|
+
gem.add_dependency "activerecord", "~> 3.0.2"
|
19
|
+
gem.add_dependency "activesupport", "~> 3.0.2"
|
20
|
+
gem.add_dependency "actionpack", "~> 3.0.2"
|
21
|
+
gem.add_dependency "arel", "~> 2.0.2"
|
22
22
|
gem.post_install_message = <<END
|
23
23
|
|
24
24
|
*** Thanks for installing MetaSearch! ***
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.9
|
data/lib/meta_search/builder.rb
CHANGED
@@ -22,7 +22,7 @@ module MetaSearch
|
|
22
22
|
include ModelCompatibility
|
23
23
|
include Utility
|
24
24
|
|
25
|
-
attr_reader :base, :search_attributes, :join_dependency, :errors
|
25
|
+
attr_reader :base, :search_key, :search_attributes, :join_dependency, :errors
|
26
26
|
delegate *RELATION_METHODS + [:to => :relation]
|
27
27
|
|
28
28
|
# Initialize a new Builder. Requires a base model to wrap, and supports a couple of options
|
@@ -30,7 +30,7 @@ module MetaSearch
|
|
30
30
|
def initialize(base_or_relation, opts = {})
|
31
31
|
@relation = base_or_relation.scoped
|
32
32
|
@base = @relation.klass
|
33
|
-
@
|
33
|
+
@search_key = opts[:search_key] ? opts[:search_key].to_s : 'search'
|
34
34
|
@join_dependency = build_join_dependency
|
35
35
|
@search_attributes = {}
|
36
36
|
@errors = ActiveModel::Errors.new(self)
|
@@ -42,19 +42,23 @@ module MetaSearch
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def get_column(column, base = @base)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
base.columns_hash[column.to_s] unless base_excludes_attribute(base, column)
|
46
|
+
end
|
47
|
+
|
48
|
+
def base_excludes_attribute(base, column)
|
49
|
+
base._metasearch_include_attributes.blank? ?
|
50
|
+
base._metasearch_exclude_attributes.include?(column.to_s) :
|
51
|
+
!base._metasearch_include_attributes.include?(column.to_s)
|
50
52
|
end
|
51
53
|
|
52
54
|
def get_association(assoc, base = @base)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
55
|
+
base.reflect_on_association(assoc.to_sym) unless base_excludes_association(base, assoc)
|
56
|
+
end
|
57
|
+
|
58
|
+
def base_excludes_association(base, assoc)
|
59
|
+
base._metasearch_include_associations.blank? ?
|
60
|
+
base._metasearch_exclude_associations.include?(assoc.to_s) :
|
61
|
+
!base._metasearch_include_associations.include?(assoc.to_s)
|
58
62
|
end
|
59
63
|
|
60
64
|
def get_attribute(name, parent = @join_dependency.join_base)
|
@@ -62,9 +66,9 @@ module MetaSearch
|
|
62
66
|
if get_column(name, parent.active_record)
|
63
67
|
if parent.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
|
64
68
|
relation = parent.relation.is_a?(Array) ? parent.relation.last : parent.relation
|
65
|
-
attribute = relation
|
69
|
+
attribute = relation[name]
|
66
70
|
else
|
67
|
-
attribute = @relation.
|
71
|
+
attribute = @relation.arel_table[name]
|
68
72
|
end
|
69
73
|
elsif (segments = name.to_s.split(/_/)).size > 1
|
70
74
|
remainder = []
|
@@ -103,9 +107,8 @@ module MetaSearch
|
|
103
107
|
end
|
104
108
|
|
105
109
|
def respond_to?(method_id, include_private = false)
|
106
|
-
return true if super
|
110
|
+
return true if super
|
107
111
|
|
108
|
-
# Curses! Looks like we'll need to do this the hard way.
|
109
112
|
method_name = method_id.to_s
|
110
113
|
if RELATION_METHODS.map(&:to_s).include?(method_name)
|
111
114
|
true
|
@@ -123,6 +126,22 @@ module MetaSearch
|
|
123
126
|
|
124
127
|
private
|
125
128
|
|
129
|
+
def assign_attributes(opts)
|
130
|
+
opts.each_pair do |k, v|
|
131
|
+
self.send("#{k}=", v)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def enforce_join_depth_limit!
|
136
|
+
raise JoinDepthError, "Maximum join depth of #{MAX_JOIN_DEPTH} exceeded." if @join_dependency.join_associations.detect {|ja|
|
137
|
+
gauge_depth_of_join_association(ja) > MAX_JOIN_DEPTH
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
def gauge_depth_of_join_association(ja)
|
142
|
+
1 + (ja.respond_to?(:parent) ? gauge_depth_of_join_association(ja.parent) : 0)
|
143
|
+
end
|
144
|
+
|
126
145
|
def method_missing(method_id, *args, &block)
|
127
146
|
method_name = method_id.to_s
|
128
147
|
if method_name =~ /^meta_sort=?$/
|
@@ -141,43 +160,22 @@ module MetaSearch
|
|
141
160
|
end
|
142
161
|
end
|
143
162
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
association_joins = joins.select do |j|
|
148
|
-
[Hash, Array, Symbol].include?(j.class) && !array_of_strings?(j)
|
149
|
-
end
|
150
|
-
|
151
|
-
stashed_association_joins = joins.select do |j|
|
152
|
-
j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
|
153
|
-
end
|
154
|
-
|
155
|
-
non_association_joins = (joins - association_joins - stashed_association_joins)
|
156
|
-
custom_joins = custom_join_sql(*non_association_joins)
|
157
|
-
|
158
|
-
ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, association_joins, custom_joins)
|
163
|
+
def matches_named_method(name)
|
164
|
+
method_name = name.to_s.sub(/\=$/, '')
|
165
|
+
return method_name if @base._metasearch_methods.has_key?(method_name)
|
159
166
|
end
|
160
167
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
arel = arel.join(join_string)
|
171
|
-
end
|
172
|
-
else
|
173
|
-
arel = arel.join(join)
|
174
|
-
end
|
168
|
+
def matches_attribute_method(method_id)
|
169
|
+
method_name = preferred_method_name(method_id)
|
170
|
+
where = Where.new(method_id) rescue nil
|
171
|
+
return nil unless method_name && where
|
172
|
+
match = method_name.match("^(.*)_(#{where.name})=?$")
|
173
|
+
attribute, predicate = match.captures
|
174
|
+
attributes = attribute.split(/_or_/)
|
175
|
+
if attributes.all? {|a| where.types.include?(column_type(a))}
|
176
|
+
return match
|
175
177
|
end
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
def array_of_strings?(o)
|
180
|
-
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
|
178
|
+
nil
|
181
179
|
end
|
182
180
|
|
183
181
|
def get_sort
|
@@ -187,36 +185,24 @@ module MetaSearch
|
|
187
185
|
def set_sort(val)
|
188
186
|
column, direction = val.split('.')
|
189
187
|
direction ||= 'asc'
|
190
|
-
if ['asc','desc'].include?(direction)
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
if found_assoc = get_association(segments.join('_'), base)
|
205
|
-
if found_assoc.options[:polymorphic]
|
206
|
-
unless delimiter = remainder.index('type')
|
207
|
-
raise PolymorphicAssociationMissingTypeError, "Polymorphic association specified without a type"
|
208
|
-
end
|
209
|
-
polymorphic_class, attribute_name = remainder[0...delimiter].join('_'),
|
210
|
-
remainder[delimiter + 1...remainder.size].join('_')
|
211
|
-
polymorphic_class = polymorphic_class.classify.constantize
|
212
|
-
type = column_type(attribute_name, polymorphic_class)
|
213
|
-
else
|
214
|
-
type = column_type(remainder.join('_'), found_assoc.klass)
|
188
|
+
if ['asc','desc'].include?(direction)
|
189
|
+
if @base.respond_to?("sort_by_#{column}_#{direction}")
|
190
|
+
search_attributes['meta_sort'] = val
|
191
|
+
@relation = @relation.send("sort_by_#{column}_#{direction}")
|
192
|
+
elsif attribute = get_attribute(column)
|
193
|
+
search_attributes['meta_sort'] = val
|
194
|
+
@relation = @relation.order(attribute.send(direction).to_sql)
|
195
|
+
elsif column.scan('_and_').present?
|
196
|
+
attribute_names = column.split('_and_')
|
197
|
+
attributes = attribute_names.map {|n| get_attribute(n)}
|
198
|
+
if attribute_names.size == attributes.compact.size # We found all attributes
|
199
|
+
search_attributes['meta_sort'] = val
|
200
|
+
attributes.each do |attribute|
|
201
|
+
@relation = @relation.order(attribute.send(direction).to_sql)
|
215
202
|
end
|
216
203
|
end
|
217
204
|
end
|
218
205
|
end
|
219
|
-
type
|
220
206
|
end
|
221
207
|
|
222
208
|
def get_named_method_value(name)
|
@@ -250,6 +236,38 @@ module MetaSearch
|
|
250
236
|
end
|
251
237
|
end
|
252
238
|
|
239
|
+
def column_type(name, base = @base)
|
240
|
+
type = nil
|
241
|
+
if column = get_column(name, base)
|
242
|
+
type = column.type
|
243
|
+
elsif (segments = name.split(/_/)).size > 1
|
244
|
+
type = type_from_association_segments(segments, base)
|
245
|
+
end
|
246
|
+
type
|
247
|
+
end
|
248
|
+
|
249
|
+
def type_from_association_segments(segments, base)
|
250
|
+
remainder = []
|
251
|
+
found_assoc = nil
|
252
|
+
type = nil
|
253
|
+
while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
|
254
|
+
if found_assoc = get_association(segments.join('_'), base)
|
255
|
+
if found_assoc.options[:polymorphic]
|
256
|
+
unless delimiter = remainder.index('type')
|
257
|
+
raise PolymorphicAssociationMissingTypeError, "Polymorphic association specified without a type"
|
258
|
+
end
|
259
|
+
polymorphic_class, attribute_name = remainder[0...delimiter].join('_'),
|
260
|
+
remainder[delimiter + 1...remainder.size].join('_')
|
261
|
+
polymorphic_class = polymorphic_class.classify.constantize
|
262
|
+
type = column_type(attribute_name, polymorphic_class)
|
263
|
+
else
|
264
|
+
type = column_type(remainder.join('_'), found_assoc.klass)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
type
|
269
|
+
end
|
270
|
+
|
253
271
|
def build_or_find_association(association, parent = @join_dependency.join_base, klass = nil)
|
254
272
|
found_association = @join_dependency.join_associations.detect do |assoc|
|
255
273
|
assoc.reflection.name == association.to_sym &&
|
@@ -257,75 +275,47 @@ module MetaSearch
|
|
257
275
|
assoc.parent == parent
|
258
276
|
end
|
259
277
|
unless found_association
|
260
|
-
@join_dependency.send(:build_with_metasearch, association, parent, Arel::OuterJoin, klass)
|
278
|
+
@join_dependency.send(:build_with_metasearch, association, parent, Arel::Nodes::OuterJoin, klass)
|
261
279
|
found_association = @join_dependency.join_associations.last
|
262
280
|
@relation = @relation.joins(found_association)
|
263
281
|
end
|
264
282
|
found_association
|
265
283
|
end
|
266
284
|
|
267
|
-
def
|
268
|
-
|
269
|
-
gauge_depth_of_join_association(ja) > MAX_JOIN_DEPTH
|
270
|
-
}
|
271
|
-
end
|
272
|
-
|
273
|
-
def gauge_depth_of_join_association(ja)
|
274
|
-
1 + (ja.respond_to?(:parent) ? gauge_depth_of_join_association(ja.parent) : 0)
|
275
|
-
end
|
276
|
-
|
277
|
-
def matches_named_method(name)
|
278
|
-
method_name = name.to_s.sub(/\=$/, '')
|
279
|
-
return method_name if @base._metasearch_methods.has_key?(method_name)
|
280
|
-
end
|
285
|
+
def build_join_dependency
|
286
|
+
joins = @relation.joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
|
281
287
|
|
282
|
-
|
283
|
-
|
284
|
-
where = Where.new(method_id) rescue nil
|
285
|
-
return nil unless method_name && where
|
286
|
-
match = method_name.match("^(.*)_(#{where.name})=?$")
|
287
|
-
attribute, predicate = match.captures
|
288
|
-
attributes = attribute.split(/_or_/)
|
289
|
-
if attributes.all? {|a| where.types.include?(column_type(a))}
|
290
|
-
return match
|
288
|
+
association_joins = joins.select do |j|
|
289
|
+
[Hash, Array, Symbol].include?(j.class) && !array_of_strings?(j)
|
291
290
|
end
|
292
|
-
nil
|
293
|
-
end
|
294
291
|
|
295
|
-
|
296
|
-
|
297
|
-
where = Where.new(method_name) rescue nil
|
298
|
-
return nil unless where
|
299
|
-
where.aliases.each do |a|
|
300
|
-
break if method_name.sub!(/#{a}(=?)$/, "#{where.name}\\1")
|
292
|
+
stashed_association_joins = joins.select do |j|
|
293
|
+
j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
|
301
294
|
end
|
302
|
-
method_name
|
303
|
-
end
|
304
295
|
|
305
|
-
|
306
|
-
|
307
|
-
self.send("#{k}=", v)
|
308
|
-
end
|
309
|
-
end
|
296
|
+
non_association_joins = (joins - association_joins - stashed_association_joins)
|
297
|
+
custom_joins = custom_join_sql(*non_association_joins)
|
310
298
|
|
311
|
-
|
312
|
-
column = self.column(attribute)
|
313
|
-
column.type if column
|
299
|
+
ActiveRecord::Associations::ClassMethods::JoinDependency.new(@base, association_joins, custom_joins)
|
314
300
|
end
|
315
301
|
|
316
|
-
def
|
317
|
-
|
318
|
-
|
319
|
-
|
302
|
+
def custom_join_sql(*joins)
|
303
|
+
arel = @relation.table
|
304
|
+
joins.each do |join|
|
305
|
+
next if join.blank?
|
320
306
|
|
321
|
-
|
322
|
-
|
323
|
-
|
307
|
+
case join
|
308
|
+
when Hash, Array, Symbol
|
309
|
+
if array_of_strings?(join)
|
310
|
+
join_string = join.join(' ')
|
311
|
+
arel = arel.join(join_string)
|
312
|
+
end
|
313
|
+
else
|
314
|
+
arel = arel.join(join)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
arel.joins(arel)
|
324
318
|
end
|
325
319
|
|
326
|
-
def association_class_for(association, attribute)
|
327
|
-
column = self.association_column(association, attribute)
|
328
|
-
column.klass if column
|
329
|
-
end
|
330
320
|
end
|
331
321
|
end
|
@@ -2,17 +2,22 @@ module MetaSearch
|
|
2
2
|
module Helpers
|
3
3
|
module FormHelper
|
4
4
|
def apply_form_for_options!(object_or_array, options)
|
5
|
-
if object_or_array.is_a?(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
options[:html] ||= {}
|
11
|
-
options[:html].reverse_merge!(html_options)
|
12
|
-
options[:url] ||= polymorphic_path(object_or_array.map {|o| o.is_a?(MetaSearch::Builder) ? o.base : o})
|
5
|
+
if object_or_array.is_a?(MetaSearch::Builder)
|
6
|
+
builder = object_or_array
|
7
|
+
url = polymorphic_path(object_or_array.base)
|
8
|
+
elsif object_or_array.is_a?(Array) && (builder = object_or_array.detect {|o| o.is_a?(MetaSearch::Builder)})
|
9
|
+
url = polymorphic_path(object_or_array.map {|o| o.is_a?(MetaSearch::Builder) ? o.base : o})
|
13
10
|
else
|
14
|
-
super
|
11
|
+
super and return
|
15
12
|
end
|
13
|
+
|
14
|
+
html_options = {
|
15
|
+
:class => options[:as] ? "#{options[:as]}_search" : "#{builder.base.to_s.underscore}_search",
|
16
|
+
:id => options[:as] ? "#{options[:as]}_search" : "#{builder.base.to_s.underscore}_search",
|
17
|
+
:method => :get }
|
18
|
+
options[:html] ||= {}
|
19
|
+
options[:html].reverse_merge!(html_options)
|
20
|
+
options[:url] ||= url
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -19,7 +19,7 @@ module MetaSearch
|
|
19
19
|
def sort_link(builder, attribute, *args)
|
20
20
|
raise ArgumentError, "Need a MetaSearch::Builder search object as first param!" unless builder.is_a?(MetaSearch::Builder)
|
21
21
|
attr_name = attribute.to_s
|
22
|
-
name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : attr_name
|
22
|
+
name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name)
|
23
23
|
prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.')
|
24
24
|
current_order = prev_attr == attr_name ? prev_order : nil
|
25
25
|
new_order = current_order == 'asc' ? 'desc' : 'asc'
|
@@ -28,7 +28,7 @@ module MetaSearch
|
|
28
28
|
css = ['sort_link', current_order].compact.join(' ')
|
29
29
|
html_options[:class] = [css, html_options[:class]].compact.join(' ')
|
30
30
|
options.merge!(
|
31
|
-
|
31
|
+
builder.search_key => builder.search_attributes.merge(
|
32
32
|
'meta_sort' => [attr_name, new_order].join('.')
|
33
33
|
)
|
34
34
|
)
|
@@ -13,8 +13,8 @@ module MetaSearch
|
|
13
13
|
join_associations.detect {|a| association == a} ||
|
14
14
|
(
|
15
15
|
association.class == MetaSearch::PolymorphicJoinAssociation ?
|
16
|
-
build_with_metasearch(association.reflection.name, association.find_parent_in(self) || join_base, association.
|
17
|
-
build(association.reflection.name, association.find_parent_in(self) || join_base, association.
|
16
|
+
build_with_metasearch(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type, association.reflection.klass) :
|
17
|
+
build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
|
18
18
|
)
|
19
19
|
end
|
20
20
|
self
|
@@ -22,7 +22,7 @@ module MetaSearch
|
|
22
22
|
|
23
23
|
protected
|
24
24
|
|
25
|
-
def build_with_metasearch(association, parent = nil,
|
25
|
+
def build_with_metasearch(association, parent = nil, join_type = Arel::Nodes::InnerJoin, polymorphic_class = nil)
|
26
26
|
parent ||= @joins.last
|
27
27
|
case association
|
28
28
|
when Symbol, String
|
@@ -30,13 +30,17 @@ module MetaSearch
|
|
30
30
|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?"
|
31
31
|
if reflection.options[:polymorphic]
|
32
32
|
@reflections << reflection
|
33
|
-
|
33
|
+
association = build_polymorphic_join_association(reflection, parent, polymorphic_class)
|
34
|
+
association.join_type = join_type
|
35
|
+
@joins << association
|
34
36
|
else
|
35
37
|
@reflections << reflection
|
36
|
-
|
38
|
+
association = build_join_association(reflection, parent)
|
39
|
+
association.join_type = join_type
|
40
|
+
@joins << association
|
37
41
|
end
|
38
42
|
else
|
39
|
-
build(association, parent,
|
43
|
+
build(association, parent, join_type) # Shouldn't get here.
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
@@ -59,7 +63,7 @@ module MetaSearch
|
|
59
63
|
@parent_table_name = @parent.active_record.table_name
|
60
64
|
@aliased_table_name = aliased_table_name_for(table_name)
|
61
65
|
@join = nil
|
62
|
-
@
|
66
|
+
@join_type = Arel::Nodes::InnerJoin
|
63
67
|
end
|
64
68
|
|
65
69
|
def ==(other)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module MetaSearch
|
2
|
-
|
2
|
+
|
3
3
|
module ModelCompatibility
|
4
|
+
|
4
5
|
def self.included(base)
|
5
6
|
base.extend ClassMethods
|
6
7
|
end
|
7
8
|
|
8
|
-
# Force default "Update search" text
|
9
9
|
def persisted?
|
10
|
-
|
10
|
+
false
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_key
|
@@ -21,26 +21,29 @@ module MetaSearch
|
|
21
21
|
def to_model
|
22
22
|
self
|
23
23
|
end
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
class Name < String
|
27
|
+
attr_reader :singular, :plural, :element, :collection, :partial_path, :human, :param_key, :route_key
|
28
|
+
alias_method :cache_key, :collection
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super("Search")
|
32
|
+
@singular = "search".freeze
|
33
|
+
@plural = "searches".freeze
|
34
|
+
@element = "search".freeze
|
35
|
+
@human = "Search".freeze
|
36
|
+
@collection = "meta_search/searches".freeze
|
37
|
+
@partial_path = "#{@collection}/#{@element}".freeze
|
38
|
+
@param_key = "search".freeze
|
39
|
+
@route_key = "searches".freeze
|
38
40
|
end
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
43
|
+
module ClassMethods
|
44
|
+
def model_name
|
45
|
+
@_model_name ||= Name.new
|
44
46
|
end
|
45
47
|
end
|
48
|
+
|
46
49
|
end
|
@@ -2,92 +2,94 @@ require 'active_support/concern'
|
|
2
2
|
require 'meta_search/method'
|
3
3
|
require 'meta_search/builder'
|
4
4
|
|
5
|
-
module MetaSearch
|
6
|
-
module
|
5
|
+
module MetaSearch
|
6
|
+
module Searches
|
7
|
+
module ActiveRecord
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
base.class_eval do
|
13
|
+
class_attribute :_metasearch_include_attributes, :_metasearch_exclude_attributes
|
14
|
+
class_attribute :_metasearch_include_associations, :_metasearch_exclude_associations
|
15
|
+
class_attribute :_metasearch_methods
|
16
|
+
self._metasearch_include_attributes =
|
17
|
+
self._metasearch_exclude_attributes =
|
18
|
+
self._metasearch_exclude_associations =
|
19
|
+
self._metasearch_include_associations = []
|
20
|
+
self._metasearch_methods = {}
|
21
|
+
end
|
20
22
|
end
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
builder.build(opts)
|
33
|
-
end
|
24
|
+
module ClassMethods
|
25
|
+
# Prepares the search to run against your model. Returns an instance of
|
26
|
+
# MetaSearch::Builder, which behaves pretty much like an ActiveRecord::Relation,
|
27
|
+
# in that it doesn't actually query the database until you do something that
|
28
|
+
# requires it to do so.
|
29
|
+
def metasearch(params = nil, opts = nil)
|
30
|
+
builder = MetaSearch::Builder.new(self, opts || {})
|
31
|
+
builder.build(params || {})
|
32
|
+
end
|
34
33
|
|
35
|
-
|
34
|
+
alias_method :search, :metasearch unless respond_to?(:search)
|
36
35
|
|
37
|
-
|
36
|
+
private
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
# Excludes model attributes from searchability. This means that searches can't be created against
|
39
|
+
# these columns, whether the search is based on this model, or the model's attributes are being
|
40
|
+
# searched by association from another model. If a Comment <tt>belongs_to :article</tt> but declares
|
41
|
+
# <tt>attr_unsearchable :user_id</tt> then <tt>Comment.search</tt> won't accept parameters
|
42
|
+
# like <tt>:user_id_equals</tt>, nor will an Article.search accept the parameter
|
43
|
+
# <tt>:comments_user_id_equals</tt>.
|
44
|
+
def attr_unsearchable(*args)
|
45
|
+
args.flatten.each do |attr|
|
46
|
+
attr = attr.to_s
|
47
|
+
raise(ArgumentError, "No persisted attribute (column) named #{attr} in #{self}") unless self.columns_hash.has_key?(attr)
|
48
|
+
self._metasearch_exclude_attributes = (self._metasearch_exclude_attributes + [attr]).uniq
|
49
|
+
end
|
50
50
|
end
|
51
|
-
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
52
|
+
# Like <tt>attr_unsearchable</tt>, but operates as a whitelist rather than blacklist. If both
|
53
|
+
# <tt>attr_searchable</tt> and <tt>attr_unsearchable</tt> are present, the latter
|
54
|
+
# is ignored.
|
55
|
+
def attr_searchable(*args)
|
56
|
+
args.flatten.each do |attr|
|
57
|
+
attr = attr.to_s
|
58
|
+
raise(ArgumentError, "No persisted attribute (column) named #{attr} in #{self}") unless self.columns_hash.has_key?(attr)
|
59
|
+
self._metasearch_include_attributes = (self._metasearch_include_attributes + [attr]).uniq
|
60
|
+
end
|
61
61
|
end
|
62
|
-
end
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
63
|
+
# Excludes model associations from searchability. This mean that searches can't be created against
|
64
|
+
# these associations. An article that <tt>has_many :comments</tt> but excludes comments from
|
65
|
+
# searching by declaring <tt>assoc_unsearchable :comments</tt> won't make any of the
|
66
|
+
# <tt>comments_*</tt> methods available.
|
67
|
+
def assoc_unsearchable(*args)
|
68
|
+
args.flatten.each do |assoc|
|
69
|
+
assoc = assoc.to_s
|
70
|
+
raise(ArgumentError, "No such association #{assoc} in #{self}") unless self.reflect_on_all_associations.map {|a| a.name.to_s}.include?(assoc)
|
71
|
+
self._metasearch_exclude_associations = (self._metasearch_exclude_associations + [assoc]).uniq
|
72
|
+
end
|
73
73
|
end
|
74
|
-
end
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# As with <tt>attr_searchable</tt> this is the whitelist version of
|
76
|
+
# <tt>assoc_unsearchable</tt>
|
77
|
+
def assoc_searchable(*args)
|
78
|
+
args.flatten.each do |assoc|
|
79
|
+
assoc = assoc.to_s
|
80
|
+
raise(ArgumentError, "No such association #{assoc} in #{self}") unless self.reflect_on_all_associations.map {|a| a.name.to_s}.include?(assoc)
|
81
|
+
self._metasearch_include_associations = (self._metasearch_include_associations + [assoc]).uniq
|
82
|
+
end
|
83
83
|
end
|
84
|
-
end
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
def search_methods(*args)
|
86
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
87
|
+
args.flatten.map(&:to_s).each do |arg|
|
88
|
+
self._metasearch_methods[arg] = MetaSearch::Method.new(arg, opts)
|
89
|
+
end
|
90
90
|
end
|
91
|
+
|
92
|
+
alias_method :search_method, :search_methods
|
91
93
|
end
|
92
94
|
end
|
93
95
|
end
|
data/lib/meta_search/utility.rb
CHANGED
@@ -8,6 +8,20 @@ module MetaSearch
|
|
8
8
|
|
9
9
|
private
|
10
10
|
|
11
|
+
def preferred_method_name(method_id)
|
12
|
+
method_name = method_id.to_s
|
13
|
+
where = Where.new(method_name) rescue nil
|
14
|
+
return nil unless where
|
15
|
+
where.aliases.each do |a|
|
16
|
+
break if method_name.sub!(/#{a}(=?)$/, "#{where.name}\\1")
|
17
|
+
end
|
18
|
+
method_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def array_of_strings?(o)
|
22
|
+
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
|
23
|
+
end
|
24
|
+
|
11
25
|
def array_of_arrays?(vals)
|
12
26
|
vals.is_a?(Array) && vals.first.is_a?(Array)
|
13
27
|
end
|
data/lib/meta_search/where.rb
CHANGED
@@ -245,7 +245,6 @@ module MetaSearch
|
|
245
245
|
create_where_from_args(*args + [{
|
246
246
|
:types => where.types,
|
247
247
|
:predicate => "#{where.predicate}_#{compound}".to_sym,
|
248
|
-
:splat_param => true,
|
249
248
|
# Only use valid elements in the array
|
250
249
|
:formatter => Proc.new {|param|
|
251
250
|
param.select {|p| where.validator.call(p)}.map {|p| where.formatter.call(p)}
|
data/lib/meta_search.rb
CHANGED
@@ -13,11 +13,11 @@ module MetaSearch
|
|
13
13
|
['equals', 'eq', {:validator => Proc.new {|param| !param.blank? || (param == false)}}],
|
14
14
|
['does_not_equal', 'ne', 'not_eq', {:types => ALL_TYPES, :predicate => :not_eq}],
|
15
15
|
['contains', 'like', 'matches', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}],
|
16
|
-
['does_not_contain', 'nlike', 'not_matches', {:types => STRINGS, :predicate => :
|
16
|
+
['does_not_contain', 'nlike', 'not_matches', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}%"'}],
|
17
17
|
['starts_with', 'sw', {:types => STRINGS, :predicate => :matches, :formatter => '"#{param}%"'}],
|
18
|
-
['does_not_start_with', 'dnsw', {:types => STRINGS, :predicate => :
|
18
|
+
['does_not_start_with', 'dnsw', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}%"'}],
|
19
19
|
['ends_with', 'ew', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}"'}],
|
20
|
-
['does_not_end_with', 'dnew', {:types => STRINGS, :predicate => :
|
20
|
+
['does_not_end_with', 'dnew', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}"'}],
|
21
21
|
['greater_than', 'gt', {:types => (NUMBERS + DATES + TIMES), :predicate => :gt}],
|
22
22
|
['less_than', 'lt', {:types => (NUMBERS + DATES + TIMES), :predicate => :lt}],
|
23
23
|
['greater_than_or_equal_to', 'gte', 'gteq', {:types => (NUMBERS + DATES + TIMES), :predicate => :gteq}],
|
@@ -26,8 +26,8 @@ module MetaSearch
|
|
26
26
|
['not_in', 'ni', 'not_in', {:types => ALL_TYPES, :predicate => :not_in}],
|
27
27
|
['is_true', {:types => BOOLEANS, :skip_compounds => true}],
|
28
28
|
['is_false', {:types => BOOLEANS, :skip_compounds => true, :formatter => Proc.new {|param| !param}}],
|
29
|
-
['is_present', {:types => (NUMBERS + STRINGS), :predicate => :not_eq_all, :
|
30
|
-
['is_blank', {:types => (NUMBERS + STRINGS), :predicate => :eq_any, :
|
29
|
+
['is_present', {:types => (NUMBERS + STRINGS), :predicate => :not_eq_all, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| [nil, '']}}],
|
30
|
+
['is_blank', {:types => (NUMBERS + STRINGS), :predicate => :eq_any, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| [nil, '']}}],
|
31
31
|
['is_null', {:types => ALL_TYPES, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| nil}}],
|
32
32
|
['is_not_null', {:types => ALL_TYPES, :predicate => :not_eq, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| nil}}]
|
33
33
|
]
|
data/meta_search.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{meta_search}
|
8
|
-
s.version = "0.9.
|
8
|
+
s.version = "0.9.9"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ernie Miller"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-11-15}
|
13
13
|
s.description = %q{
|
14
14
|
Allows simple search forms to be created against an AR3 model
|
15
15
|
and its associations, has useful view helpers for sort links
|
@@ -57,6 +57,8 @@ Gem::Specification.new do |s|
|
|
57
57
|
"test/fixtures/projects.yml",
|
58
58
|
"test/fixtures/schema.rb",
|
59
59
|
"test/helper.rb",
|
60
|
+
"test/locales/en.yml",
|
61
|
+
"test/locales/es.yml",
|
60
62
|
"test/test_search.rb",
|
61
63
|
"test/test_view_helpers.rb"
|
62
64
|
]
|
@@ -91,23 +93,23 @@ you're feeling especially appreciative. It'd help me justify this
|
|
91
93
|
|
92
94
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
93
95
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
94
|
-
s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.
|
95
|
-
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.
|
96
|
-
s.add_runtime_dependency(%q<actionpack>, ["~> 3.0.
|
97
|
-
s.add_runtime_dependency(%q<arel>, ["~>
|
96
|
+
s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.2"])
|
97
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.2"])
|
98
|
+
s.add_runtime_dependency(%q<actionpack>, ["~> 3.0.2"])
|
99
|
+
s.add_runtime_dependency(%q<arel>, ["~> 2.0.2"])
|
98
100
|
else
|
99
101
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
100
|
-
s.add_dependency(%q<activerecord>, ["~> 3.0.
|
101
|
-
s.add_dependency(%q<activesupport>, ["~> 3.0.
|
102
|
-
s.add_dependency(%q<actionpack>, ["~> 3.0.
|
103
|
-
s.add_dependency(%q<arel>, ["~>
|
102
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.2"])
|
103
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.2"])
|
104
|
+
s.add_dependency(%q<actionpack>, ["~> 3.0.2"])
|
105
|
+
s.add_dependency(%q<arel>, ["~> 2.0.2"])
|
104
106
|
end
|
105
107
|
else
|
106
108
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
107
|
-
s.add_dependency(%q<activerecord>, ["~> 3.0.
|
108
|
-
s.add_dependency(%q<activesupport>, ["~> 3.0.
|
109
|
-
s.add_dependency(%q<actionpack>, ["~> 3.0.
|
110
|
-
s.add_dependency(%q<arel>, ["~>
|
109
|
+
s.add_dependency(%q<activerecord>, ["~> 3.0.2"])
|
110
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.2"])
|
111
|
+
s.add_dependency(%q<actionpack>, ["~> 3.0.2"])
|
112
|
+
s.add_dependency(%q<arel>, ["~> 2.0.2"])
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
data/test/fixtures/developer.rb
CHANGED
@@ -2,7 +2,10 @@ class Developer < ActiveRecord::Base
|
|
2
2
|
belongs_to :company
|
3
3
|
has_and_belongs_to_many :projects
|
4
4
|
has_many :notes, :as => :notable
|
5
|
-
|
5
|
+
|
6
6
|
attr_searchable :name, :salary
|
7
7
|
assoc_searchable :notes, :projects, :company
|
8
|
+
|
9
|
+
scope :sort_by_salary_and_name_asc, order('salary ASC, name ASC')
|
10
|
+
scope :sort_by_salary_and_name_desc, order('salary DESC, name DESC')
|
8
11
|
end
|
data/test/helper.rb
CHANGED
@@ -25,6 +25,8 @@ end
|
|
25
25
|
|
26
26
|
Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables)
|
27
27
|
|
28
|
+
I18n.load_path = Dir[File.join(File.dirname(__FILE__), 'locales', '*.{rb,yml}')]
|
29
|
+
|
28
30
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
29
31
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
30
32
|
|
data/test/locales/en.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
en:
|
2
|
+
date:
|
3
|
+
formats:
|
4
|
+
# Use the strftime parameters for formats.
|
5
|
+
# When no format has been given, it uses default.
|
6
|
+
# You can provide other formats here if you like!
|
7
|
+
default: "%Y-%m-%d"
|
8
|
+
short: "%b %d"
|
9
|
+
long: "%B %d, %Y"
|
10
|
+
|
11
|
+
day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
|
12
|
+
abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
|
13
|
+
|
14
|
+
# Don't forget the nil at the beginning; there's no such thing as a 0th month
|
15
|
+
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
|
16
|
+
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
|
17
|
+
# Used in date_select and datetime_select.
|
18
|
+
order:
|
19
|
+
- :year
|
20
|
+
- :month
|
21
|
+
- :day
|
22
|
+
|
23
|
+
time:
|
24
|
+
formats:
|
25
|
+
default: "%a, %d %b %Y %H:%M:%S %z"
|
26
|
+
short: "%d %b %H:%M"
|
27
|
+
long: "%B %d, %Y %H:%M"
|
28
|
+
am: "am"
|
29
|
+
pm: "pm"
|
30
|
+
|
31
|
+
# Used in array.to_sentence.
|
32
|
+
support:
|
33
|
+
array:
|
34
|
+
words_connector: ", "
|
35
|
+
two_words_connector: " and "
|
36
|
+
last_word_connector: ", and "
|
data/test/locales/es.yml
ADDED
data/test/test_search.rb
CHANGED
@@ -298,6 +298,17 @@ class TestSearch < Test::Unit::TestCase
|
|
298
298
|
end
|
299
299
|
end
|
300
300
|
|
301
|
+
context "sorted by salary and name in descending order" do
|
302
|
+
setup do
|
303
|
+
@s.meta_sort = 'salary_and_name.desc'
|
304
|
+
end
|
305
|
+
|
306
|
+
should "sort by salary and name in descending order" do
|
307
|
+
assert_equal Developer.order('salary DESC, name DESC').all,
|
308
|
+
@s.all
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
301
312
|
context "where developer is Bob-approved" do
|
302
313
|
setup do
|
303
314
|
@s.notes_note_equals = "A straight shooter with upper management written all over him."
|
data/test/test_view_helpers.rb
CHANGED
@@ -198,6 +198,80 @@ class TestViewHelpers < ActionView::TestCase
|
|
198
198
|
assert_no_match /Created at ▲/,
|
199
199
|
@f.sort_link(:created_at, :controller => 'companies')
|
200
200
|
end
|
201
|
+
|
202
|
+
context "and a localization" do
|
203
|
+
setup do
|
204
|
+
I18n.locale = :es
|
205
|
+
end
|
206
|
+
|
207
|
+
teardown do
|
208
|
+
I18n.locale = nil
|
209
|
+
end
|
210
|
+
|
211
|
+
should "use the localized name for the attribute" do
|
212
|
+
assert_match /Nombre/,
|
213
|
+
@f.sort_link(:name, :controller => 'companies')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context "A developer search form sorted by a custom sort method" do
|
219
|
+
setup do
|
220
|
+
@s = Developer.search
|
221
|
+
@s.meta_sort = 'salary_and_name.asc'
|
222
|
+
form_for @s do |f|
|
223
|
+
@f = f
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
should "generate a sort link with humanized text" do
|
228
|
+
assert_match /Salary and name ▲/,
|
229
|
+
@f.sort_link(:salary_and_name, :controller => 'developers')
|
230
|
+
end
|
231
|
+
|
232
|
+
should "sort results as expected" do
|
233
|
+
assert_equal Developer.order('salary ASC, name ASC'),
|
234
|
+
@s.all
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "A developer search form sorted by multiple columns" do
|
239
|
+
setup do
|
240
|
+
@s = Developer.search
|
241
|
+
@s.meta_sort = 'name_and_salary.asc'
|
242
|
+
form_for @s do |f|
|
243
|
+
@f = f
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
should "generate a sort link with humanized text" do
|
248
|
+
assert_match /Name and salary ▲/,
|
249
|
+
@f.sort_link(:name_and_salary, :controller => 'developers')
|
250
|
+
end
|
251
|
+
|
252
|
+
should "order by both columns in the order they were specified" do
|
253
|
+
assert_match /ORDER BY "developers"."name" ASC, "developers"."salary" ASC/,
|
254
|
+
@s.to_sql
|
255
|
+
end
|
256
|
+
|
257
|
+
should "return expected results" do
|
258
|
+
assert_equal Developer.order('name ASC, salary ASC').all,
|
259
|
+
@s.all
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context "A company search form with an alternate search_key" do
|
264
|
+
setup do
|
265
|
+
@s = Company.search({}, :search_key => 'searchy_mcsearchhead')
|
266
|
+
form_for @s do |f|
|
267
|
+
@f = f
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
should "generate a sort link that places meta_sort param under the specified key" do
|
272
|
+
assert_match /searchy_mcsearchhead/,
|
273
|
+
@f.sort_link(:name, :controller => 'companies')
|
274
|
+
end
|
201
275
|
end
|
202
276
|
|
203
277
|
context "A company search" do
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 9
|
9
|
+
version: 0.9.9
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ernie Miller
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-11-15 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -41,8 +41,8 @@ dependencies:
|
|
41
41
|
segments:
|
42
42
|
- 3
|
43
43
|
- 0
|
44
|
-
-
|
45
|
-
version: 3.0.
|
44
|
+
- 2
|
45
|
+
version: 3.0.2
|
46
46
|
type: :runtime
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
@@ -56,8 +56,8 @@ dependencies:
|
|
56
56
|
segments:
|
57
57
|
- 3
|
58
58
|
- 0
|
59
|
-
-
|
60
|
-
version: 3.0.
|
59
|
+
- 2
|
60
|
+
version: 3.0.2
|
61
61
|
type: :runtime
|
62
62
|
version_requirements: *id003
|
63
63
|
- !ruby/object:Gem::Dependency
|
@@ -71,8 +71,8 @@ dependencies:
|
|
71
71
|
segments:
|
72
72
|
- 3
|
73
73
|
- 0
|
74
|
-
-
|
75
|
-
version: 3.0.
|
74
|
+
- 2
|
75
|
+
version: 3.0.2
|
76
76
|
type: :runtime
|
77
77
|
version_requirements: *id004
|
78
78
|
- !ruby/object:Gem::Dependency
|
@@ -84,10 +84,10 @@ dependencies:
|
|
84
84
|
- - ~>
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
segments:
|
87
|
-
-
|
87
|
+
- 2
|
88
88
|
- 0
|
89
|
-
-
|
90
|
-
version:
|
89
|
+
- 2
|
90
|
+
version: 2.0.2
|
91
91
|
type: :runtime
|
92
92
|
version_requirements: *id005
|
93
93
|
description: "\n Allows simple search forms to be created against an AR3 model\n and its associations, has useful view helpers for sort links\n and multiparameter fields as well.\n "
|
@@ -136,6 +136,8 @@ files:
|
|
136
136
|
- test/fixtures/projects.yml
|
137
137
|
- test/fixtures/schema.rb
|
138
138
|
- test/helper.rb
|
139
|
+
- test/locales/en.yml
|
140
|
+
- test/locales/es.yml
|
139
141
|
- test/test_search.rb
|
140
142
|
- test/test_view_helpers.rb
|
141
143
|
has_rdoc: true
|