meta_search 0.9.8 → 0.9.9
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/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
|