searchgasm 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|