searchgasm 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +11 -0
- data/Rakefile +2 -3
- data/lib/searchgasm/config.rb +2 -2
- data/lib/searchgasm/core_ext/hash.rb +37 -3
- data/lib/searchgasm/helpers/control_types/link.rb +79 -7
- data/lib/searchgasm/helpers/control_types/remote_select.rb +1 -1
- data/lib/searchgasm/helpers/form.rb +1 -1
- data/lib/searchgasm/helpers/utilities.rb +55 -5
- data/lib/searchgasm/search/base.rb +2 -8
- data/lib/searchgasm/search/ordering.rb +133 -50
- data/lib/searchgasm/search/protection.rb +3 -3
- data/lib/searchgasm/version.rb +1 -1
- data/searchgasm.gemspec +3 -6
- data/test/test_search_ordering.rb +98 -31
- metadata +2 -5
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 1.2.1 released 2008-09-24
|
2
|
+
|
3
|
+
* Fixed problem when determining if an order_by_link is currently being ordered. Just "stringified" both comparable values.
|
4
|
+
* Removed default order_by and order_as. They will ONLY have values if you specify how to order, otherwise they are nil.
|
5
|
+
* Removed order_as requirement. order_as is optional.
|
6
|
+
* Added in deep_merge methods for hash, copied over from ActiveSupport 2.1
|
7
|
+
* Improved order by auto joins to be based off of what order_by returns instead of setting it when setting order_by.
|
8
|
+
* Added priority_order_by. Useful if you want to order featured products first and then order as usual. See documentation in Searchgasm::Search::Ordering for more info.
|
9
|
+
* Added in base64 support for order_by and priority_order_by so that it's value is safe in the URL
|
10
|
+
* Added priority_order_by_link
|
11
|
+
|
1
12
|
== 1.2.0 released 2008-09-24
|
2
13
|
|
3
14
|
* Added searchgasm_params and searchgasm_url helper to use outside of the control type helpers.
|
data/Rakefile
CHANGED
@@ -10,7 +10,6 @@ Echoe.new 'searchgasm' do |p|
|
|
10
10
|
p.project = 'searchgasm'
|
11
11
|
p.summary = "Object based ActiveRecord searching, ordering, pagination, and more!"
|
12
12
|
p.url = "http://github.com/binarylogic/searchgasm"
|
13
|
-
p.dependencies = ['activerecord', 'activesupport
|
13
|
+
p.dependencies = ['activerecord', 'activesupport']
|
14
14
|
p.include_rakefile = true
|
15
|
-
end
|
16
|
-
|
15
|
+
end
|
data/lib/searchgasm/config.rb
CHANGED
@@ -44,7 +44,7 @@ module Searchgasm
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def hidden_fields # :nodoc:
|
47
|
-
@hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page])
|
47
|
+
@hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page, :priority_order])
|
48
48
|
end
|
49
49
|
|
50
50
|
# Which hidden fields to automatically include when creating a form with a Searchgasm object. See Searchgasm::Helpers::Form for more info.
|
@@ -135,7 +135,7 @@ module Searchgasm
|
|
135
135
|
# The reason for this not to disturb regular queries such as Whatever.find(:all). You would not expect that to be limited.
|
136
136
|
#
|
137
137
|
# * <tt>Default:</tt> The 3rd option in your per_page_choices, default of 50
|
138
|
-
# * <tt>Accepts:</tt> Any value in your per_page choices, nil means "show all"
|
138
|
+
# * <tt>Accepts:</tt> Any value in your per_page choices, nil or a blank string means "show all"
|
139
139
|
def per_page=(value)
|
140
140
|
@per_page = value
|
141
141
|
end
|
@@ -16,19 +16,53 @@ module Searchgasm
|
|
16
16
|
new_hash
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def deep_delete_duplicate_keys(hash)
|
20
20
|
hash.each do |k, v|
|
21
21
|
if v.is_a?(Hash) && self[k]
|
22
22
|
self[k].deep_delete_duplicates(v)
|
23
|
-
|
23
|
+
delete(k) if self[k].blank?
|
24
24
|
else
|
25
|
-
|
25
|
+
delete(k)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
self
|
30
30
|
end
|
31
31
|
|
32
|
+
def deep_delete(value)
|
33
|
+
case value
|
34
|
+
when Array
|
35
|
+
value.each { |v| deep_delete(v) }
|
36
|
+
when Hash
|
37
|
+
value.each do |k, v|
|
38
|
+
next unless self[k].is_a?(Hash)
|
39
|
+
|
40
|
+
case v
|
41
|
+
when Hash, Array
|
42
|
+
self[k].deep_delete(v)
|
43
|
+
when String, Symbol
|
44
|
+
self[k].delete(v)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
when String, Symbol
|
48
|
+
delete(value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def deep_merge(other_hash)
|
53
|
+
self.merge(other_hash) do |key, oldval, newval|
|
54
|
+
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
55
|
+
newval = newval.to_hash if newval.respond_to?(:to_hash)
|
56
|
+
oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
61
|
+
# Modifies the receiver in place.
|
62
|
+
def deep_merge!(other_hash)
|
63
|
+
replace(deep_merge(other_hash))
|
64
|
+
end
|
65
|
+
|
32
66
|
# assert_valid_keys was killing performance. Array.flatten was the culprit, so I rewrote this method, got a 35% performance increase
|
33
67
|
def fast_assert_valid_keys(valid_keys)
|
34
68
|
unknown_keys = keys - valid_keys
|
@@ -80,7 +80,10 @@ module Searchgasm
|
|
80
80
|
# === Advanced Options
|
81
81
|
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
82
82
|
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
83
|
-
# * <tt>:
|
83
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
84
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
85
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
86
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
84
87
|
def order_by_link(order_by, options = {})
|
85
88
|
order_by = deep_stringify(order_by)
|
86
89
|
add_order_by_link_defaults!(order_by, options)
|
@@ -110,7 +113,10 @@ module Searchgasm
|
|
110
113
|
# === Advanced Options
|
111
114
|
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
112
115
|
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
113
|
-
# * <tt>:
|
116
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
117
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
118
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
119
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
114
120
|
def order_as_link(order_as, options = {})
|
115
121
|
add_order_as_link_defaults!(order_as, options)
|
116
122
|
html = searchgasm_state_for(:order_as, options)
|
@@ -124,6 +130,48 @@ module Searchgasm
|
|
124
130
|
html
|
125
131
|
end
|
126
132
|
|
133
|
+
# This is similar to order_by_link but with a small difference. The best way to explain priority ordering is with an example. Let's say you wanted to list products on a page. You have "featured" products
|
134
|
+
# that you want to show up first, no matter what. This is what this is all about. It makes ordering by featured products a priority, then searching by price, quantity, etc. is the same as it has always been.
|
135
|
+
#
|
136
|
+
# The difference between order_by_link and priority_order_by_link is that priority_order_by_link it just a switch. Turn it on or turn it off. You don't neccessarily want to flip between ASC and DESC. If you do
|
137
|
+
# then you should just incorporate this into your regular order_by, like: order_by_link [:featured, :price]
|
138
|
+
#
|
139
|
+
# === Example uses for a User class that has many orders
|
140
|
+
#
|
141
|
+
# priority_order_by_link(:featured, "DESC")
|
142
|
+
# order_by_link([:featured, :created_at], "ASC")
|
143
|
+
# order_by_link({:orders => :featured}, "ASC")
|
144
|
+
# order_by_link([{:orders => :featured}, :featured], "ASC")
|
145
|
+
# order_by_link(:featured, "ASC", :text => "Featured", :html => {:class => "featured_link"})
|
146
|
+
#
|
147
|
+
# === Options
|
148
|
+
# * <tt>:activate_text</tt> -- default: "Show #{column_name.to_s.humanize} first"
|
149
|
+
# * <tt>:deactivate_text</tt> -- default: "Don't show #{column_name.to_s.humanize} first", text for the link, text for the link
|
150
|
+
# * <tt>:column_name</tt> -- default: column_name.to_s.humanize, automatically inferred by what you are ordering by and is added into the active_text and deactive_text strings.
|
151
|
+
# * <tt>:text</tt> -- default: :activate_text or :deactivate_text depending on if its active or not, Overwriting this will make this text stay the same, no matter way. A good alternative would be "Toggle featured first"
|
152
|
+
# * <tt>:html</tt> -- html arrtributes for the <a> tag.
|
153
|
+
#
|
154
|
+
# === Advanced Options
|
155
|
+
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
156
|
+
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
157
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
158
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
159
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
160
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
161
|
+
def priority_order_by_link(priority_order_by, priority_order_as, options = {})
|
162
|
+
priority_order_by = deep_stringify(priority_order_by)
|
163
|
+
add_priority_order_by_link_defaults!(priority_order_by, priority_order_as, options)
|
164
|
+
html = searchgasm_state_for(:priority_order_by, options) + searchgasm_state_for(:priority_order_as, options)
|
165
|
+
|
166
|
+
if !options[:is_remote]
|
167
|
+
html += link_to(options[:text], options[:url], options[:html])
|
168
|
+
else
|
169
|
+
html += link_to_remote(options[:text], options[:remote].merge(:url => options[:url]), options[:html])
|
170
|
+
end
|
171
|
+
|
172
|
+
html
|
173
|
+
end
|
174
|
+
|
127
175
|
# Creates a link for limiting how many items are on each page
|
128
176
|
#
|
129
177
|
# === Example uses
|
@@ -141,7 +189,10 @@ module Searchgasm
|
|
141
189
|
# === Advanced Options
|
142
190
|
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
143
191
|
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
144
|
-
# * <tt>:
|
192
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
193
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
194
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
195
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
145
196
|
def per_page_link(per_page, options = {})
|
146
197
|
add_per_page_link_defaults!(per_page, options)
|
147
198
|
html = searchgasm_state_for(:per_page, options)
|
@@ -170,7 +221,10 @@ module Searchgasm
|
|
170
221
|
# === Advanced Options
|
171
222
|
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
172
223
|
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
173
|
-
# * <tt>:
|
224
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
225
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
226
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
227
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
174
228
|
def page_link(page, options = {})
|
175
229
|
add_page_link_defaults!(page, options)
|
176
230
|
html = searchgasm_state_for(:page, options)
|
@@ -190,7 +244,7 @@ module Searchgasm
|
|
190
244
|
options[:text] ||= determine_order_by_text(order_by)
|
191
245
|
options[:asc_indicator] ||= Config.asc_indicator
|
192
246
|
options[:desc_indicator] ||= Config.desc_indicator
|
193
|
-
options[:text] += options[:search_obj].desc? ? options[:desc_indicator] : options[:asc_indicator] if options[:search_obj].order_by == order_by
|
247
|
+
options[:text] += options[:search_obj].desc? ? options[:desc_indicator] : options[:asc_indicator] if deep_stringify(options[:search_obj].order_by) == order_by
|
194
248
|
options[:url] = searchgasm_params(options.merge(:search_params => {:order_by => order_by}))
|
195
249
|
options
|
196
250
|
end
|
@@ -202,6 +256,22 @@ module Searchgasm
|
|
202
256
|
options
|
203
257
|
end
|
204
258
|
|
259
|
+
def add_priority_order_by_link_defaults!(priority_order_by, priority_order_as, options = {})
|
260
|
+
add_searchgasm_control_defaults!(:priority_order_by, options)
|
261
|
+
options[:column_name] ||= determine_order_by_text(priority_order_by).downcase
|
262
|
+
options[:activate_text] ||= "Show #{options[:column_name]} first"
|
263
|
+
options[:deactivate_text] ||= "Don't show #{options[:column_name]} first"
|
264
|
+
active = deep_stringify(options[:search_obj].priority_order_by) == priority_order_by && options[:search_obj].priority_order_as == priority_order_as
|
265
|
+
options[:text] ||= active ? options[:deactivate_text] : options[:activate_text]
|
266
|
+
if active
|
267
|
+
options.merge!(:search_params => {:priority_order_by => nil, :priority_order_as => nil})
|
268
|
+
else
|
269
|
+
options.merge!(:search_params => {:priority_order_by => priority_order_by, :priority_order_as => priority_order_as})
|
270
|
+
end
|
271
|
+
options[:url] = searchgasm_params(options)
|
272
|
+
options
|
273
|
+
end
|
274
|
+
|
205
275
|
def add_per_page_link_defaults!(per_page, options = {})
|
206
276
|
add_searchgasm_control_defaults!(:per_page, options)
|
207
277
|
options[:text] ||= per_page.blank? ? "Show all" : "#{per_page} per page"
|
@@ -234,13 +304,15 @@ module Searchgasm
|
|
234
304
|
when String
|
235
305
|
obj
|
236
306
|
when Symbol
|
237
|
-
obj
|
307
|
+
obj.to_s
|
238
308
|
when Array
|
239
|
-
obj
|
309
|
+
obj.collect { |item| deep_stringify(item) }
|
240
310
|
when Hash
|
241
311
|
new_obj = {}
|
242
312
|
obj.each { |key, value| new_obj[key.to_s] = deep_stringify(value) }
|
243
313
|
new_obj
|
314
|
+
else
|
315
|
+
obj
|
244
316
|
end
|
245
317
|
end
|
246
318
|
end
|
@@ -23,7 +23,7 @@ module Searchgasm
|
|
23
23
|
per_page_select(options)
|
24
24
|
end
|
25
25
|
|
26
|
-
# Please see page_links. All options are the same and applicable here,
|
26
|
+
# Please see page_links. All options are the same and applicable here, except the :prev, :next, :first, and :last options. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
|
27
27
|
def remote_page_select(options = {})
|
28
28
|
add_remote_defaults!(options)
|
29
29
|
page_select(options)
|
@@ -122,7 +122,7 @@ module Searchgasm
|
|
122
122
|
options = args.extract_options!
|
123
123
|
options
|
124
124
|
search_options[:hidden_fields].each do |field|
|
125
|
-
html = hidden_field(name, field, :object => search_object, :id => "#{name}_#{field}_hidden", :value => (field == :order_by ?
|
125
|
+
html = hidden_field(name, field, :object => search_object, :id => "#{name}_#{field}_hidden", :value => (field == :order_by ? searchgasm_base64_value(search_object.order_by) : search_object.send(field)))
|
126
126
|
|
127
127
|
# For edge rails and older version compatibility, passing a binding to concat was deprecated
|
128
128
|
begin
|
@@ -1,11 +1,31 @@
|
|
1
1
|
module Searchgasm
|
2
2
|
module Helpers #:nodoc:
|
3
3
|
module Utilities # :nodoc:
|
4
|
-
# Builds a hash of params for creating a url.
|
4
|
+
# Builds a hash of params for creating a url and preserves any existing params. You can pass this into url_for and build your url. Although most rails helpers accept a hash.
|
5
|
+
#
|
6
|
+
# Let's take the page_link helper. Here is the code behind that helper:
|
7
|
+
#
|
8
|
+
# link_to("Page 2", searchgasm_params(:search_params => {:page => 2}))
|
9
|
+
#
|
10
|
+
# That's pretty much it. So if you wanted to roll your own link to execute a search, go for it. It's pretty simple. Pass conditions instead of the page, set how the search will be ordered, etc.
|
11
|
+
#
|
12
|
+
# <b>Be careful</b> when taking this approach though. Searchgasm helps you out when you use form_for. For example, when you use the per_page_select helper, it adds in a hidden form field with the value of the page. So when
|
13
|
+
# your search form is submitted it searches the document for that element, finds the current value, which is the current per_page value, and includes that in the search. So when a user searches the per_page
|
14
|
+
# value stays consistent. If you use the searchgasm_params you are on your own. I am always curious how people are using searchgasm. So if you are building your own helpers contact me and maybe I can help you
|
15
|
+
# and add in a helper for you, making it an *official* feature.
|
16
|
+
#
|
17
|
+
# === Options
|
18
|
+
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
19
|
+
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
20
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
21
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
22
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
23
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
5
24
|
def searchgasm_params(options = {})
|
6
25
|
add_searchgasm_defaults!(options)
|
7
26
|
options[:search_params] ||= {}
|
8
27
|
options[:literal_search_params] ||= {}
|
28
|
+
|
9
29
|
options[:params] ||= {}
|
10
30
|
params_copy = params.deep_dup.with_indifferent_access
|
11
31
|
search_params = options[:params_scope].blank? ? params_copy : params_copy.delete(options[:params_scope])
|
@@ -13,18 +33,22 @@ module Searchgasm
|
|
13
33
|
search_params = search_params.with_indifferent_access
|
14
34
|
search_params.delete(:commit)
|
15
35
|
search_params.delete(:page)
|
16
|
-
search_params.
|
36
|
+
search_params.deep_delete_duplicate_keys(options[:literal_search_params])
|
37
|
+
search_params.deep_delete(options[:exclude_search_params])
|
17
38
|
|
18
39
|
if options[:search_params]
|
19
40
|
search_params.deep_merge!(options[:search_params])
|
20
41
|
|
21
42
|
if options[:search_params][:order_by] && !options[:search_params][:order_as]
|
22
|
-
search_params[:order_as] = (options[:search_obj].order_by == options[:search_params][:order_by] && options[:search_obj].asc?) ? "DESC" : "ASC"
|
43
|
+
search_params[:order_as] = (options[:search_obj].order_by == options[:search_params][:order_by] && options[:search_obj].asc?) ? "DESC" : "ASC"
|
23
44
|
end
|
45
|
+
|
46
|
+
[:order_by, :priority_order_by].each { |base64_field| search_params[base64_field] = searchgasm_base64_value(search_params[base64_field]) if search_params.has_key?(base64_field) }
|
24
47
|
end
|
25
48
|
|
26
49
|
new_params = params_copy
|
27
50
|
new_params.deep_merge!(options[:params])
|
51
|
+
new_params.deep_delete(options[:exclude_params])
|
28
52
|
|
29
53
|
if options[:params_scope].blank? || search_params.blank?
|
30
54
|
new_params
|
@@ -33,6 +57,32 @@ module Searchgasm
|
|
33
57
|
end
|
34
58
|
end
|
35
59
|
|
60
|
+
# Similar to searchgasm_hash, but instead returns a string url. The reason this exists is to assist in creating urls in javascript. It's the muscle behind all of the select helpers that searchgasm provides.
|
61
|
+
# Take the instance where you want to do:
|
62
|
+
#
|
63
|
+
# :onchange => "window.location = '#{url_for(searchgasm_params)}&my_param=' + this.value;"
|
64
|
+
#
|
65
|
+
# Well the above obviously won't work. Do you need to apped the url with a ? or a &? What about that tricky :params_scope? That's where this is handy, beacuse it does all of the params string building for you. Check it out:
|
66
|
+
#
|
67
|
+
# :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:per_page => "' + escape(this.value) + '"}) + "';"
|
68
|
+
#
|
69
|
+
# or what about something a little more tricky?
|
70
|
+
#
|
71
|
+
# :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:conditions => {:name_contains => "' + escape(this.value) + '"}}) + "';"
|
72
|
+
#
|
73
|
+
# I have personally used this for an event calendar. Above the calendar there was a drop down for each month. Here is the code:
|
74
|
+
#
|
75
|
+
# :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:conditions => {:occurs_at_after => "' + escape(this.value) + '"}}) + "';"
|
76
|
+
#
|
77
|
+
# Now when the user changes the month in the drop down it just runs a new search that sets my conditions to occurs_at_after = selected month. Then in my controller I set occurs_at_before = occurs_at_after.at_end_of_month.
|
78
|
+
#
|
79
|
+
# === Options
|
80
|
+
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
|
81
|
+
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
|
82
|
+
# * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
|
83
|
+
# * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
|
84
|
+
# * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
|
85
|
+
# * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
|
36
86
|
def searchgasm_url(options = {})
|
37
87
|
search_params = searchgasm_params(options)
|
38
88
|
url = url_for(search_params)
|
@@ -65,7 +115,7 @@ module Searchgasm
|
|
65
115
|
html_options[:class] = classes.join(" ")
|
66
116
|
end
|
67
117
|
|
68
|
-
def
|
118
|
+
def searchgasm_base64_value(order_by)
|
69
119
|
case order_by
|
70
120
|
when String
|
71
121
|
order_by
|
@@ -79,7 +129,7 @@ module Searchgasm
|
|
79
129
|
html = ""
|
80
130
|
unless @added_state_for.include?(option)
|
81
131
|
value = options[:search_obj].send(option)
|
82
|
-
html = hidden_field(options[:params_scope], option, :value => (option == :order_by ?
|
132
|
+
html = hidden_field(options[:params_scope], option, :value => (option == :order_by ? searchgasm_base64_value(value) : value))
|
83
133
|
@added_state_for << option
|
84
134
|
end
|
85
135
|
html
|
@@ -16,7 +16,7 @@ module Searchgasm #:nodoc:
|
|
16
16
|
AR_OPTIONS = (AR_FIND_OPTIONS + AR_CALCULATIONS_OPTIONS).uniq
|
17
17
|
|
18
18
|
# Options that ActiveRecord doesn't suppport, but Searchgasm does
|
19
|
-
SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page]
|
19
|
+
SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page, :priority_order, :priority_order_by, :priority_order_as]
|
20
20
|
|
21
21
|
# Valid options you can use when searching
|
22
22
|
OPTIONS = SPECIAL_FIND_OPTIONS + AR_OPTIONS # the order is very important, these options get set in this order
|
@@ -100,13 +100,7 @@ module Searchgasm #:nodoc:
|
|
100
100
|
def options=(values)
|
101
101
|
return unless values.is_a?(Hash)
|
102
102
|
values.symbolize_keys.fast_assert_valid_keys(OPTIONS)
|
103
|
-
|
104
|
-
OPTIONS.each do |option|
|
105
|
-
next unless values.has_key?(option)
|
106
|
-
send("#{option}=", values[option])
|
107
|
-
end
|
108
|
-
|
109
|
-
values
|
103
|
+
values.each { |key, value| send("#{key}=", value) }
|
110
104
|
end
|
111
105
|
|
112
106
|
# Sanitizes everything down into options ActiveRecord::Base.find can understand
|
@@ -20,75 +20,67 @@ module Searchgasm
|
|
20
20
|
klass.class_eval do
|
21
21
|
alias_method_chain :auto_joins, :ordering
|
22
22
|
alias_method_chain :order=, :ordering
|
23
|
+
alias_method_chain :sanitize, :ordering
|
24
|
+
attr_reader :priority_order
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
def auto_joins_with_ordering # :nodoc:
|
27
|
-
@memoized_auto_joins ||= merge_joins(auto_joins_without_ordering, order_by_auto_joins)
|
29
|
+
@memoized_auto_joins ||= merge_joins(auto_joins_without_ordering, order_by_auto_joins, priority_order_by_auto_joins)
|
28
30
|
end
|
29
31
|
|
30
32
|
def order_with_ordering=(value) # :nodoc
|
31
33
|
@order_by = nil
|
32
34
|
@order_as = nil
|
33
|
-
|
35
|
+
@order_by_auto_joins = nil
|
34
36
|
@memoized_auto_joins = nil
|
35
37
|
self.order_without_ordering = value
|
36
38
|
end
|
37
39
|
|
38
40
|
# Convenience method for determining if the ordering is ascending
|
39
41
|
def asc?
|
42
|
+
return false if order_as.nil?
|
40
43
|
!desc?
|
41
44
|
end
|
42
45
|
|
43
46
|
# Convenience method for determining if the ordering is descending
|
44
47
|
def desc?
|
48
|
+
return false if order_as.nil?
|
45
49
|
order_as == "DESC"
|
46
50
|
end
|
47
51
|
|
48
52
|
# Determines how the search is being ordered: as DESC or ASC
|
49
53
|
def order_as
|
50
|
-
|
54
|
+
return if order.blank?
|
55
|
+
return @order_as if @order_as
|
56
|
+
|
57
|
+
case order
|
58
|
+
when /ASC$/i
|
59
|
+
@order_as = "ASC"
|
60
|
+
when /DESC$/i
|
61
|
+
@order_as = "DESC"
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
51
65
|
end
|
52
66
|
|
53
67
|
# Sets how the results will be ordered: ASC or DESC
|
54
68
|
def order_as=(value)
|
55
|
-
value = value.to_s.upcase
|
56
|
-
raise(ArgumentError, "order_as only accepts a string as ASC or DESC")
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
else
|
69
|
+
value = value.blank? ? nil : value.to_s.upcase
|
70
|
+
raise(ArgumentError, "order_as only accepts a blank string / nil or a string as 'ASC' or 'DESC'") if !value.blank? && !["ASC", "DESC"].include?(value)
|
71
|
+
if @order_by
|
72
|
+
@order = order_by_to_order(@order_by, value)
|
73
|
+
elsif order
|
61
74
|
@order.gsub!(/(ASC|DESC)/i, value)
|
62
75
|
end
|
63
|
-
|
64
76
|
@order_as = value
|
65
77
|
end
|
66
78
|
|
67
79
|
# Determines by what columns the search is being ordered. This is nifty in that is reverse engineers the order SQL to determine this, only
|
68
80
|
# if you haven't explicitly set the order_by option yourself.
|
69
81
|
def order_by
|
70
|
-
return
|
71
|
-
|
72
|
-
if !order.blank?
|
73
|
-
# Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
|
74
|
-
order_parts = order.split(",").collect do |part|
|
75
|
-
part.strip!
|
76
|
-
part.gsub!(/ (ASC|DESC)$/i, "").gsub!(/(.*)\./, "")
|
77
|
-
table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
|
78
|
-
part.gsub!(/[^a-z0-9_]/i, "")
|
79
|
-
reflection = nil
|
80
|
-
if table_name && table_name != klass.table_name
|
81
|
-
reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
|
82
|
-
next unless reflection
|
83
|
-
{reflection.name.to_s => part}
|
84
|
-
else
|
85
|
-
part
|
86
|
-
end
|
87
|
-
end.compact
|
88
|
-
@order_by = order_parts.size <= 1 ? order_parts.first : order_parts
|
89
|
-
else
|
90
|
-
@order_by = klass.primary_key
|
91
|
-
end
|
82
|
+
return if order.blank?
|
83
|
+
@order_by ||= order_to_order_by(order)
|
92
84
|
end
|
93
85
|
|
94
86
|
# Lets you set how to order the data
|
@@ -101,23 +93,86 @@ module Searchgasm
|
|
101
93
|
# order_by = [:id, name] # => users.id ASC, user.name ASC
|
102
94
|
# order_by = [:id, {:user_group => :name}] # => users.id ASC, user_groups.name ASC
|
103
95
|
def order_by=(value)
|
104
|
-
|
96
|
+
@order_by_auto_joins = nil
|
105
97
|
@memoized_auto_joins = nil
|
106
98
|
@order_by = get_order_by_value(value)
|
107
|
-
@order = order_by_to_order(@order_by, order_as)
|
99
|
+
@order = order_by_to_order(@order_by, @order_as)
|
108
100
|
@order_by
|
109
101
|
end
|
110
102
|
|
111
103
|
# Returns the joins neccessary for the "order" statement so that we don't get an SQL error
|
112
104
|
def order_by_auto_joins
|
113
|
-
@order_by_auto_joins ||=
|
114
|
-
|
115
|
-
|
116
|
-
|
105
|
+
@order_by_auto_joins ||= build_order_by_auto_joins(order_by)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Let's you set a priority order. Meaning this will get ordered first before anything else, but is unnoticeable and abstracted out from your regular order. For example, lets say you have a model called Product
|
109
|
+
# that had a "featured" boolean column. You want to order the products by the price, quantity, etc., but you want the featured products to always be first.
|
110
|
+
#
|
111
|
+
# Without a priority order your controller would get cluttered and your code would be much more complicated. All of your order_by_link methods would have to be order_by_link [:featured, :price], :text => "Price"
|
112
|
+
# Your order_by_link methods alternate between ASC and DESC, so the featured products would jump from the top the bottom. It presents a lot of "work arounds". So priority_order solves this.
|
113
|
+
def priority_order=(value)
|
114
|
+
@priority_order = value
|
115
|
+
end
|
116
|
+
|
117
|
+
# Same as order_by but for your priority order. See priority_order= for more informaton on priority_order.
|
118
|
+
def priority_order_by
|
119
|
+
return if priority_order.blank?
|
120
|
+
@priority_order_by ||= order_to_order_by(priority_order)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Same as order_by= but for your priority order. See priority_order= for more informaton on priority_order.
|
124
|
+
def priority_order_by=(value)
|
125
|
+
@priority_order_by_auto_joins = nil
|
126
|
+
@memoized_auto_joins = nil
|
127
|
+
@priority_order_by = get_order_by_value(value)
|
128
|
+
@priority_order = order_by_to_order(@priority_order_by, @priority_order_as)
|
129
|
+
@priority_order_by
|
130
|
+
end
|
131
|
+
|
132
|
+
# Same as order_as but for your priority order. See priority_order= for more informaton on priority_order.
|
133
|
+
def priority_order_as
|
134
|
+
return if priority_order.blank?
|
135
|
+
return @priority_order_as if @priority_order_as
|
136
|
+
|
137
|
+
case priority_order
|
138
|
+
when /ASC$/i
|
139
|
+
@priority_order_as = "ASC"
|
140
|
+
when /DESC$/i
|
141
|
+
@priority_order_as = "DESC"
|
142
|
+
else
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Same as order_as= but for your priority order. See priority_order= for more informaton on priority_order.
|
148
|
+
def priority_order_as=(value)
|
149
|
+
value = value.blank? ? nil : value.to_s.upcase
|
150
|
+
raise(ArgumentError, "priority_order_as only accepts a blank string / nil or a string as 'ASC' or 'DESC'") if !value.blank? && !["ASC", "DESC"].include?(value)
|
151
|
+
if @priority_order_by
|
152
|
+
@priority_order = order_by_to_order(@priority_order_by, value)
|
153
|
+
elsif priority_order
|
154
|
+
@priority_order.gsub!(/(ASC|DESC)/i, value)
|
155
|
+
end
|
156
|
+
@priority_order_as = value
|
157
|
+
end
|
158
|
+
|
159
|
+
def priority_order_by_auto_joins
|
160
|
+
@priority_order_by_auto_joins ||= build_order_by_auto_joins(priority_order_by)
|
161
|
+
end
|
162
|
+
|
163
|
+
def sanitize_with_ordering(searching = true)
|
164
|
+
find_options = sanitize_without_ordering(searching)
|
165
|
+
unless priority_order.blank?
|
166
|
+
order_parts = [priority_order, find_options[:order]].compact
|
167
|
+
find_options[:order] = order_parts.join(", ")
|
168
|
+
end
|
169
|
+
find_options
|
117
170
|
end
|
118
171
|
|
119
172
|
private
|
120
|
-
def order_by_to_order(order_by, order_as, alt_klass = nil
|
173
|
+
def order_by_to_order(order_by, order_as, alt_klass = nil)
|
174
|
+
return if order_by.blank?
|
175
|
+
|
121
176
|
k = alt_klass || klass
|
122
177
|
table_name = k.table_name
|
123
178
|
sql_parts = []
|
@@ -130,23 +185,51 @@ module Searchgasm
|
|
130
185
|
key = order_by.keys.first
|
131
186
|
reflection = k.reflect_on_association(key.to_sym)
|
132
187
|
value = order_by.values.first
|
133
|
-
|
134
|
-
sql_parts << order_by_to_order(value, order_as, reflection.klass, new_joins)
|
188
|
+
sql_parts << order_by_to_order(value, order_as, reflection.klass)
|
135
189
|
when Symbol, String
|
136
|
-
|
137
|
-
|
138
|
-
sql_parts <<
|
190
|
+
part = "#{quote_table_name(table_name)}.#{quote_column_name(order_by)}"
|
191
|
+
part += " #{order_as}" unless order_as.blank?
|
192
|
+
sql_parts << part
|
139
193
|
end
|
140
194
|
|
141
195
|
sql_parts.join(", ")
|
142
196
|
end
|
143
197
|
|
144
|
-
def
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
198
|
+
def order_to_order_by(order)
|
199
|
+
# Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
|
200
|
+
order_parts = order.split(",").collect do |part|
|
201
|
+
part.strip!
|
202
|
+
part.gsub!(/ (ASC|DESC)$/i, "").gsub!(/(.*)\./, "")
|
203
|
+
table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
|
204
|
+
part.gsub!(/[^a-z0-9_]/i, "")
|
205
|
+
reflection = nil
|
206
|
+
if table_name && table_name != klass.table_name
|
207
|
+
reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
|
208
|
+
next unless reflection
|
209
|
+
{reflection.name.to_s => part}
|
210
|
+
else
|
211
|
+
part
|
212
|
+
end
|
213
|
+
end.compact
|
214
|
+
order_parts.size <= 1 ? order_parts.first : order_parts
|
215
|
+
end
|
216
|
+
|
217
|
+
def build_order_by_auto_joins(order_by_value)
|
218
|
+
case order_by_value
|
219
|
+
when Array
|
220
|
+
order_by_value.collect { |value| build_order_by_auto_joins(value) }.uniq.compact
|
221
|
+
when Hash
|
222
|
+
key = order_by_value.keys.first
|
223
|
+
value = order_by_value.values.first
|
224
|
+
case value
|
225
|
+
when Hash
|
226
|
+
{key => build_order_by_auto_joins(value)}
|
227
|
+
else
|
228
|
+
key
|
229
|
+
end
|
230
|
+
else
|
231
|
+
nil
|
232
|
+
end
|
150
233
|
end
|
151
234
|
|
152
235
|
def get_order_by_value(value)
|
@@ -22,11 +22,11 @@ module Searchgasm
|
|
22
22
|
# User.all(params[:search])
|
23
23
|
module Protection
|
24
24
|
# Options that are allowed when protecting against SQL injections (still checked though)
|
25
|
-
SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
|
25
|
+
SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset] - [:priority_order]
|
26
26
|
|
27
|
-
VULNERABLE_FIND_OPTIONS = Base::AR_FIND_OPTIONS - SAFE_OPTIONS
|
27
|
+
VULNERABLE_FIND_OPTIONS = Base::AR_FIND_OPTIONS - SAFE_OPTIONS + [:priority_order]
|
28
28
|
|
29
|
-
VULNERABLE_CALCULATIONS_OPTIONS = Base::AR_CALCULATIONS_OPTIONS - SAFE_OPTIONS
|
29
|
+
VULNERABLE_CALCULATIONS_OPTIONS = Base::AR_CALCULATIONS_OPTIONS - SAFE_OPTIONS + [:priority_order]
|
30
30
|
|
31
31
|
# Options that are not allowed, at all, when protecting against SQL injections
|
32
32
|
VULNERABLE_OPTIONS = Base::OPTIONS - SAFE_OPTIONS
|
data/lib/searchgasm/version.rb
CHANGED
data/searchgasm.gemspec
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Searchgasm-1.2.
|
2
|
+
# Gem::Specification for Searchgasm-1.2.1
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
--- !ruby/object:Gem::Specification
|
6
6
|
name: searchgasm
|
7
7
|
version: !ruby/object:Gem::Version
|
8
|
-
version: 1.2.
|
8
|
+
version: 1.2.1
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- Ben Johnson of Binary Logic
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
|
15
|
-
date: 2008-09-
|
15
|
+
date: 2008-09-26 00:00:00 -04:00
|
16
16
|
default_executable:
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
@@ -34,9 +34,6 @@ dependencies:
|
|
34
34
|
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: "0"
|
37
|
-
- - "="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 2.1.0
|
40
37
|
version:
|
41
38
|
- !ruby/object:Gem::Dependency
|
42
39
|
name: echoe
|
@@ -1,49 +1,22 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
2
|
|
3
3
|
class TestSearchOrdering < Test::Unit::TestCase
|
4
|
-
def test_order_as
|
5
|
-
search = Account.new_search
|
6
|
-
assert_equal nil, search.order
|
7
|
-
assert_equal "ASC", search.order_as
|
8
|
-
assert search.asc?
|
9
|
-
|
10
|
-
search.order_as = "DESC"
|
11
|
-
assert_equal "DESC", search.order_as
|
12
|
-
assert search.desc?
|
13
|
-
assert_equal "\"accounts\".\"id\" DESC", search.order
|
14
|
-
|
15
|
-
search.order = "id ASC"
|
16
|
-
assert_equal "ASC", search.order_as
|
17
|
-
assert search.asc?
|
18
|
-
assert_equal "id ASC", search.order
|
19
|
-
|
20
|
-
search.order = "id DESC"
|
21
|
-
assert_equal "DESC", search.order_as
|
22
|
-
assert search.desc?
|
23
|
-
assert_equal "id DESC", search.order
|
24
|
-
|
25
|
-
search.order_by = "name"
|
26
|
-
assert_equal "DESC", search.order_as
|
27
|
-
assert search.desc?
|
28
|
-
assert_equal "\"accounts\".\"name\" DESC", search.order
|
29
|
-
end
|
30
|
-
|
31
4
|
def test_order_by
|
32
5
|
search = Account.new_search
|
33
6
|
assert_equal nil, search.order
|
34
|
-
assert_equal
|
7
|
+
assert_equal nil, search.order_by
|
35
8
|
|
36
9
|
search.order_by = "first_name"
|
37
10
|
assert_equal "first_name", search.order_by
|
38
|
-
assert_equal "\"accounts\".\"first_name\"
|
11
|
+
assert_equal "\"accounts\".\"first_name\"", search.order
|
39
12
|
|
40
13
|
search.order_by = "last_name"
|
41
14
|
assert_equal "last_name", search.order_by
|
42
|
-
assert_equal "\"accounts\".\"last_name\"
|
15
|
+
assert_equal "\"accounts\".\"last_name\"", search.order
|
43
16
|
|
44
17
|
search.order_by = ["first_name", "last_name"]
|
45
18
|
assert_equal ["first_name", "last_name"], search.order_by
|
46
|
-
assert_equal "\"accounts\".\"first_name\"
|
19
|
+
assert_equal "\"accounts\".\"first_name\", \"accounts\".\"last_name\"", search.order
|
47
20
|
|
48
21
|
search.order = "created_at DESC"
|
49
22
|
assert_equal "created_at", search.order_by
|
@@ -77,4 +50,98 @@ class TestSearchOrdering < Test::Unit::TestCase
|
|
77
50
|
assert_equal nil, search.order_by
|
78
51
|
assert_equal "`line_items`.id DESC", search.order
|
79
52
|
end
|
53
|
+
|
54
|
+
def test_order_as
|
55
|
+
search = Account.new_search
|
56
|
+
assert_equal nil, search.order
|
57
|
+
assert_equal nil, search.order_as
|
58
|
+
assert !search.asc?
|
59
|
+
|
60
|
+
search.order_as = "DESC"
|
61
|
+
assert_equal nil, search.order_as
|
62
|
+
assert !search.desc?
|
63
|
+
assert_equal nil, search.order
|
64
|
+
|
65
|
+
search.order_by = "name"
|
66
|
+
assert_equal "\"accounts\".\"name\" DESC", search.order
|
67
|
+
|
68
|
+
search.order_as = "ASC"
|
69
|
+
assert_equal "\"accounts\".\"name\" ASC", search.order
|
70
|
+
assert search.asc?
|
71
|
+
|
72
|
+
search.order = "id ASC"
|
73
|
+
assert_equal "ASC", search.order_as
|
74
|
+
assert search.asc?
|
75
|
+
assert_equal "id ASC", search.order
|
76
|
+
|
77
|
+
search.order = "id DESC"
|
78
|
+
assert_equal "DESC", search.order_as
|
79
|
+
assert search.desc?
|
80
|
+
assert_equal "id DESC", search.order
|
81
|
+
|
82
|
+
search.order_by = "name"
|
83
|
+
assert_equal "DESC", search.order_as
|
84
|
+
assert search.desc?
|
85
|
+
assert_equal "\"accounts\".\"name\" DESC", search.order
|
86
|
+
|
87
|
+
assert_raise(ArgumentError) { search.order_as = "awesome" }
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_order_by_auto_joins
|
91
|
+
search = Account.new_search
|
92
|
+
assert_equal nil, search.order_by_auto_joins
|
93
|
+
search.order_by = :name
|
94
|
+
assert_equal nil, search.order_by_auto_joins
|
95
|
+
search.order_by = {:users => :first_name}
|
96
|
+
assert_equal :users, search.order_by_auto_joins
|
97
|
+
search.order_by = [{:users => :first_name}, {:orders => :total}, {:users => {:user_groups => :name}}]
|
98
|
+
assert_equal [:users, :orders, {:users => :user_groups}], search.order_by_auto_joins
|
99
|
+
search.priority_order_by = {:users => :first_name}
|
100
|
+
assert_equal [:users, :orders, {:users => :user_groups}], search.order_by_auto_joins
|
101
|
+
search.priority_order_by = {:users => {:orders => :total}}
|
102
|
+
assert_equal({:users => :orders}, search.priority_order_by_auto_joins)
|
103
|
+
assert_equal [:users, :orders, {:users => :user_groups}, {:users => :orders}], search.auto_joins
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_priority_order_by
|
107
|
+
search = Account.new_search
|
108
|
+
assert_equal nil, search.priority_order
|
109
|
+
assert_equal nil, search.priority_order_by
|
110
|
+
assert_equal nil, search.priority_order_as
|
111
|
+
|
112
|
+
search.priority_order_by = :name
|
113
|
+
assert_equal "\"accounts\".\"name\"", search.priority_order
|
114
|
+
assert_equal "\"accounts\".\"name\"", search.sanitize[:order]
|
115
|
+
assert_equal nil, search.order
|
116
|
+
assert_equal :name, search.priority_order_by
|
117
|
+
assert_equal nil, search.priority_order_as
|
118
|
+
|
119
|
+
search.order_by = :id
|
120
|
+
assert_equal "\"accounts\".\"name\", \"accounts\".\"id\"", search.sanitize[:order]
|
121
|
+
search.order_as = "DESC"
|
122
|
+
assert_equal "\"accounts\".\"name\", \"accounts\".\"id\" DESC", search.sanitize[:order]
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_priority_order_as
|
126
|
+
search = Account.new_search
|
127
|
+
assert_equal nil, search.priority_order_as
|
128
|
+
assert_equal nil, search.order_as
|
129
|
+
search.priority_order_as = "ASC"
|
130
|
+
assert_equal nil, search.priority_order_as
|
131
|
+
assert_equal nil, search.order_as
|
132
|
+
search.priority_order_by = :name
|
133
|
+
assert_equal "ASC", search.priority_order_as
|
134
|
+
assert_equal nil, search.order_as
|
135
|
+
search.priority_order_as = "DESC"
|
136
|
+
assert_equal "DESC", search.priority_order_as
|
137
|
+
assert_equal nil, search.order_as
|
138
|
+
assert_raise(ArgumentError) { search.priority_order_as = "awesome" }
|
139
|
+
search.priority_order = nil
|
140
|
+
assert_equal nil, search.priority_order_as
|
141
|
+
assert_equal nil, search.order_as
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_sanitize
|
145
|
+
# tested in test_priority_order_by
|
146
|
+
end
|
80
147
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchgasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Johnson of Binary Logic
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-09-
|
12
|
+
date: 2008-09-26 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -31,9 +31,6 @@ dependencies:
|
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: "0"
|
34
|
-
- - "="
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: 2.1.0
|
37
34
|
version:
|
38
35
|
- !ruby/object:Gem::Dependency
|
39
36
|
name: echoe
|