searchgasm 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/MIT-LICENSE +20 -0
- data/Manifest +28 -0
- data/README.mdown +240 -0
- data/Rakefile +17 -0
- data/init.rb +1 -0
- data/lib/searchgasm/active_record/associations.rb +56 -0
- data/lib/searchgasm/active_record/base.rb +70 -0
- data/lib/searchgasm/active_record/protection.rb +37 -0
- data/lib/searchgasm/helpers.rb +100 -0
- data/lib/searchgasm/search/base.rb +142 -0
- data/lib/searchgasm/search/condition.rb +168 -0
- data/lib/searchgasm/search/conditions.rb +154 -0
- data/lib/searchgasm/search/utilities.rb +34 -0
- data/lib/searchgasm/version.rb +82 -0
- data/lib/searchgasm.rb +9 -0
- data/searchgasm.gemspec +124 -0
- data/test/fixtures/accounts.yml +15 -0
- data/test/fixtures/orders.yml +14 -0
- data/test/fixtures/users.yml +27 -0
- data/test/libs/acts_as_tree.rb +98 -0
- data/test/libs/rexml_fix.rb +14 -0
- data/test/test_active_record_associations.rb +38 -0
- data/test/test_active_record_base.rb +48 -0
- data/test/test_active_record_protection.rb +0 -0
- data/test/test_helper.rb +78 -0
- data/test/test_searchgasm_base.rb +116 -0
- data/test/test_searchgasm_condition.rb +149 -0
- data/test/test_searchgasm_conditions.rb +137 -0
- metadata +122 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.9.0. First release
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Ben Johnson of Binary Logic (binarylogic.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
init.rb
|
3
|
+
lib/searchgasm/active_record/associations.rb
|
4
|
+
lib/searchgasm/active_record/base.rb
|
5
|
+
lib/searchgasm/active_record/protection.rb
|
6
|
+
lib/searchgasm/helpers.rb
|
7
|
+
lib/searchgasm/search/base.rb
|
8
|
+
lib/searchgasm/search/condition.rb
|
9
|
+
lib/searchgasm/search/conditions.rb
|
10
|
+
lib/searchgasm/search/utilities.rb
|
11
|
+
lib/searchgasm/version.rb
|
12
|
+
lib/searchgasm.rb
|
13
|
+
Manifest
|
14
|
+
MIT-LICENSE
|
15
|
+
Rakefile
|
16
|
+
README.mdown
|
17
|
+
test/fixtures/accounts.yml
|
18
|
+
test/fixtures/orders.yml
|
19
|
+
test/fixtures/users.yml
|
20
|
+
test/libs/acts_as_tree.rb
|
21
|
+
test/libs/rexml_fix.rb
|
22
|
+
test/test_active_record_associations.rb
|
23
|
+
test/test_active_record_base.rb
|
24
|
+
test/test_active_record_protection.rb
|
25
|
+
test/test_helper.rb
|
26
|
+
test/test_searchgasm_base.rb
|
27
|
+
test/test_searchgasm_condition.rb
|
28
|
+
test/test_searchgasm_conditions.rb
|
data/README.mdown
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
# Searchgasm
|
2
|
+
|
3
|
+
Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab a towel and let's dive in.
|
4
|
+
|
5
|
+
Searchgasm originated to satisfy a VERY simple need: so that I could use my form builder when making search forms. Sounds simple right? The goal was to use an object, that represents a search, just like an ActiveRecord object in form\_for and fields\_for.
|
6
|
+
|
7
|
+
I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "santiizes" down into the options passed into ActiveRecord::Base.find(). It basically serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. This doesn't step on the toes or dig into he internals of ActiveRecord. It uses what ActiveRecord provides publicly. Letting ActiveRecord do all of the hard work and keeping this plugin solid and less brittle.
|
8
|
+
|
9
|
+
Here's where you get aroused...
|
10
|
+
|
11
|
+
## Install and use
|
12
|
+
|
13
|
+
sudo gem install searchgasm
|
14
|
+
|
15
|
+
For rails > 2.1
|
16
|
+
|
17
|
+
# environment.rb
|
18
|
+
config.gem "searchgasm"
|
19
|
+
|
20
|
+
For rails < 2.1
|
21
|
+
|
22
|
+
# environment.rb
|
23
|
+
require "searchgasm"
|
24
|
+
|
25
|
+
Or as a plugin
|
26
|
+
|
27
|
+
script/plugin install git://github.com/binarylogic/searchgasm.git
|
28
|
+
|
29
|
+
Now go into your console and try out any of these example with your own models.
|
30
|
+
|
31
|
+
**For all examples, let's assume the following relationships: User => Orders => Line Items**
|
32
|
+
|
33
|
+
## Super Simple Example
|
34
|
+
|
35
|
+
User.all(
|
36
|
+
:conditions => {
|
37
|
+
:first_name_contains => "Ben",
|
38
|
+
:email_ends_with => "binarylogic.com"
|
39
|
+
},
|
40
|
+
:page => 3,
|
41
|
+
:per_page => 20
|
42
|
+
)
|
43
|
+
|
44
|
+
## Detailed Example w/ object based searching
|
45
|
+
|
46
|
+
# new_search returns an object, you can call "find", "all", "first" also, see "different ways to search" below
|
47
|
+
|
48
|
+
search = User.new_search(
|
49
|
+
:conditions => {
|
50
|
+
:first_name_contains => "Ben",
|
51
|
+
:age_gt => 18,
|
52
|
+
:orders => {:total_lt => 100}
|
53
|
+
},
|
54
|
+
:per_page => 20,
|
55
|
+
:page => 2,
|
56
|
+
:order_by => {:orders => :total},
|
57
|
+
:order_as => "DESC"
|
58
|
+
)
|
59
|
+
search.conditions.email_ends_with = "binarylogic.com"
|
60
|
+
search.conditions.oders.line_items.created_at_after = Time.now
|
61
|
+
search.per_page = 50 # overrides the 20 set above
|
62
|
+
|
63
|
+
# Call ANY of the ActiveRecord options
|
64
|
+
search.group = "last_name"
|
65
|
+
search.readonly = true
|
66
|
+
# ... see ActiveRecord documentation
|
67
|
+
|
68
|
+
# Return results just like ActiveRecord
|
69
|
+
search.all
|
70
|
+
search.search # alias for all
|
71
|
+
search.first
|
72
|
+
|
73
|
+
## Calculations
|
74
|
+
|
75
|
+
Using the object from above:
|
76
|
+
|
77
|
+
search.average('id')
|
78
|
+
search.count
|
79
|
+
search.maximum('id')
|
80
|
+
search.minimum('id')
|
81
|
+
search.sum('id')
|
82
|
+
search.calculate(:sum, 'id') # any of the above calculations
|
83
|
+
|
84
|
+
Or do it from your model:
|
85
|
+
|
86
|
+
User.count(:conditions => {:first_name_contains => "Ben"})
|
87
|
+
# ... all other calcualtions, etc.
|
88
|
+
|
89
|
+
## Different ways to search, take your pick
|
90
|
+
|
91
|
+
Any of the options used in the above example can be used in these, but for the sake of brevity I am only using a few:
|
92
|
+
|
93
|
+
User.all(:conditions => {:age_gt => 18}, :per_page => 20)
|
94
|
+
|
95
|
+
User.first(:conditions => {:age_gt => 18}, :per_page => 20)
|
96
|
+
|
97
|
+
User.find(:all, :conditions => {::age_gt => 18}, :per_page => 20)
|
98
|
+
|
99
|
+
User.find(:first, :conditions => {::age_gt => 18}, :per_page => 20)
|
100
|
+
|
101
|
+
search = User.new_search(:conditions => {:age_gt => 18})
|
102
|
+
search.conditions.first_name_contains = "Ben"
|
103
|
+
search.per_page = 20
|
104
|
+
search.all
|
105
|
+
|
106
|
+
If you want to be hardcore:
|
107
|
+
|
108
|
+
search = Searchgasm::Search.new(User, :conditions => {:age_gt => 18})
|
109
|
+
search.conditions.first_name_contains = "Ben"
|
110
|
+
search.per_page = 20
|
111
|
+
search.all
|
112
|
+
|
113
|
+
## Search with conditions only (great for form\_for or fields\_for)
|
114
|
+
|
115
|
+
conditions = User.new_conditions(:age_gt => 18)
|
116
|
+
conditions.first_name_contains = "Ben"
|
117
|
+
conditions.search
|
118
|
+
conditions.all
|
119
|
+
# ... all operations above are available
|
120
|
+
|
121
|
+
Pass a conditions object right into ActiveRecord:
|
122
|
+
|
123
|
+
User.all(:conditions => conditions) # same as conditions.search
|
124
|
+
|
125
|
+
Again, if you want to be hardcore:
|
126
|
+
|
127
|
+
conditions = Searchgasm::Conditions.new(User, :age_gt => 18)
|
128
|
+
conditions.first_name_contains = "Ben"
|
129
|
+
conditions.search
|
130
|
+
|
131
|
+
## Scoped searching
|
132
|
+
|
133
|
+
@current_user.orders.find(:all, :conditions => {:total_lte => 500})
|
134
|
+
@current_user.orders.count(:conditions => {:total_lte => 500})
|
135
|
+
@current_user.orders.sum('total', :conditions => {:total_lte => 500})
|
136
|
+
|
137
|
+
search = @current_user.orders.build_search('total', :conditions => {:total_lte => 500})
|
138
|
+
|
139
|
+
## Searching trees
|
140
|
+
|
141
|
+
For tree data structures you get a few nifty methods. Let's assume Users is a tree data structure.
|
142
|
+
|
143
|
+
# Child of
|
144
|
+
User.all(:conditions => {:child_of => User.roots.first})
|
145
|
+
User.all(:conditions => {:child_of => User.roots.first.id})
|
146
|
+
|
147
|
+
# Sibling of
|
148
|
+
User.all(:conditions => {:sibling_of => User.roots.first})
|
149
|
+
User.all(:conditions => {:sibling_of => User.roots.first.id})
|
150
|
+
|
151
|
+
# Descendent of (includes all recursive children: children, grand children, great grand children, etc)
|
152
|
+
User.all(:conditions => {:descendent_of => User.roots.first})
|
153
|
+
User.all(:conditions => {:descendent_of => User.roots.first.id})
|
154
|
+
|
155
|
+
# Inclusive descendent_of. Same as above but includes the root
|
156
|
+
User.all(:conditions => {:inclusive_descendent_of => User.roots.first})
|
157
|
+
User.all(:conditions => {:inclusive_descendent_of => User.roots.first.id})
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
## Available anywhere (relationships & named scopes)
|
162
|
+
|
163
|
+
Not only can you use searchgasm when searching, but you can use it when setting up relationships or named scopes:
|
164
|
+
|
165
|
+
class User < ActiveRecord::Base
|
166
|
+
has_many :expensive_pending_orders, :conditions => {:total_greater_than => 1_000_000, :state => :pending}, :per_page => 20
|
167
|
+
named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
|
168
|
+
end
|
169
|
+
|
170
|
+
## Always use protection (searching with params)
|
171
|
+
|
172
|
+
If there is one thing we in sex ed. it's to always use protection. As I mentioned above the purpose of this plugin was to create a search object and use it in form\_for or fields\_for. What about receiving the params in the controller and protecting against SQL injection?
|
173
|
+
|
174
|
+
accounts = Account.find_with_protection(params[:search])
|
175
|
+
accounts = Account.all_with_protection(params[:search])
|
176
|
+
account = Account.first_with_protection(params[:search])
|
177
|
+
|
178
|
+
For the lazy programmer:
|
179
|
+
|
180
|
+
accounts = Account.findwp(params[:search])
|
181
|
+
accounts = Account.allwp(params[:search])
|
182
|
+
account = Account.firstwp(params[:search])
|
183
|
+
|
184
|
+
This performs various checks to ensure SQL injection is impossible. I'm sure you know this, but I have to say it: *DO NOT* pass params into the "find", "all", or "first" methods, otherwise you are opening yourself up to SQL injections.
|
185
|
+
|
186
|
+
# DO NOT DO THIS!
|
187
|
+
accounts = Account.all(params[:search])
|
188
|
+
|
189
|
+
# OR THIS!
|
190
|
+
accounts = Account.all(:conditions => params[:conditions])
|
191
|
+
|
192
|
+
## Available Conditions
|
193
|
+
|
194
|
+
Depending on the type, each column comes preloaded with a bunch of nifty conditions:
|
195
|
+
|
196
|
+
all columns
|
197
|
+
=> :equals, :does_not_equal
|
198
|
+
|
199
|
+
:string, :text
|
200
|
+
=> :begins_with, :contains, :keywords, :ends_with
|
201
|
+
|
202
|
+
:integer, :float, :decimal,:datetime, :timestamp, :time, :date
|
203
|
+
=> :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to
|
204
|
+
|
205
|
+
tree data structures (see above "searching trees")
|
206
|
+
=> :child_of, :sibling_of, :descendent_of, :inclusive_descendent_of
|
207
|
+
|
208
|
+
Some of these conditions come with aliases, so you have your choice how to call the conditions. For example you can use "greater\_than" or "gt":
|
209
|
+
|
210
|
+
:equals; => :is
|
211
|
+
:does_not_equal => :is_not, :not
|
212
|
+
:begins_with => :starts_with
|
213
|
+
:contains => :like
|
214
|
+
:greater_than => :gt, :after
|
215
|
+
:greater_than_or_equal_to => :at_least, :gte
|
216
|
+
:less_than => :lt, :before
|
217
|
+
:less_than_or_equal_to => :at_most, :lte
|
218
|
+
|
219
|
+
### Enhanced searching and blacklisted words
|
220
|
+
|
221
|
+
You will notice above there is "contains" and "keywords". The difference is that "keywords" is an enhanced search. It acts like a real keyword search. It finds those keywords, in any order, and blacklists meaningless words such as "and", "the", etc. "contains" finds the EXACT string in the column you are searching, spaces and all.
|
222
|
+
|
223
|
+
## Creating your search form
|
224
|
+
|
225
|
+
After all of that, here's why I love this plugin:
|
226
|
+
|
227
|
+
<% form_for :search, User.new_conditions, :url => users_path do |f| %>
|
228
|
+
<%= f.text_field :first_name_contains %>
|
229
|
+
<%= f.calendar_date_select :created_at_after %>
|
230
|
+
<%= f.select :age_gt, (1..100) %>
|
231
|
+
<% end %>
|
232
|
+
|
233
|
+
## Credits
|
234
|
+
|
235
|
+
Author: [Ben Johnson](http://github.com/binarylogic) of [Binary Logic](http://www.binarylogic.com)
|
236
|
+
|
237
|
+
Credit to [Zack Ham](http://github.com/zackham) and [Robert Malko](http://github.com/malkomalko/) for helping with feature suggestions, cleaning up the readme / wiki, and cleaning up my code.
|
238
|
+
|
239
|
+
|
240
|
+
Copyright (c) 2008 [Ben Johnson](http://github.com/binarylogic) of [Binary Logic](http://www.binarylogic.com), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'echoe'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) << "/lib/searchgasm/version"
|
5
|
+
|
6
|
+
Echoe.new 'searchgasm' do |p|
|
7
|
+
p.version = BinaryLogic::Searchgasm::Version::STRING
|
8
|
+
p.author = "Ben Johnson of Binary Logic"
|
9
|
+
p.email = 'bjohnson@binarylogic.com'
|
10
|
+
p.project = 'searchgasm'
|
11
|
+
p.summary = "Orgasmic ActiveRecord searching"
|
12
|
+
p.description = "Makes ActiveRecord searching easier, robust, and powerful. Automatic conditions, pagination support, object based searching, and more."
|
13
|
+
p.url = "http://github.com/binarylogic/searchgasm"
|
14
|
+
p.dependencies = %w(activerecord)
|
15
|
+
p.include_rakefile = true
|
16
|
+
end
|
17
|
+
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/lib/searchgasm"
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module BinaryLogic
|
2
|
+
module Searchgasm
|
3
|
+
module ActiveRecord
|
4
|
+
module Associations
|
5
|
+
module AssociationCollection
|
6
|
+
def self.included(klass)
|
7
|
+
klass.class_eval do
|
8
|
+
include Protection
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_with_searchgasm(*args)
|
13
|
+
options = args.extract_options!
|
14
|
+
args << sanitize_options_with_searchgasm(options)
|
15
|
+
find_without_searchgasm(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_conditions(options = {}, &block)
|
19
|
+
@reflection.klass.build_conditions(options.merge(:scope => scope(:find)[:conditions]), &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_search(options = {}, &block)
|
23
|
+
@reflection.klass.build_search(options.merge(:scope => scope(:find)[:conditions]), &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module HasManyAssociation
|
28
|
+
def count_with_searchgasm(*args)
|
29
|
+
column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
|
30
|
+
count_without_searchgasm(column_name, sanitize_options_with_searchgasm(options))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
ActiveRecord::Associations::AssociationCollection.send(:include, BinaryLogic::Searchgasm::ActiveRecord::Associations::AssociationCollection)
|
39
|
+
|
40
|
+
module ::ActiveRecord
|
41
|
+
module Associations
|
42
|
+
class AssociationCollection
|
43
|
+
alias_method_chain :find, :searchgasm
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveRecord::Associations::HasManyAssociation.send(:include, BinaryLogic::Searchgasm::ActiveRecord::Associations::HasManyAssociation)
|
49
|
+
|
50
|
+
module ::ActiveRecord
|
51
|
+
module Associations
|
52
|
+
class HasManyAssociation
|
53
|
+
alias_method_chain :count, :searchgasm
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module BinaryLogic
|
2
|
+
module Searchgasm
|
3
|
+
module ActiveRecord
|
4
|
+
module Base
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
alias_method :new_conditions, :build_conditions
|
8
|
+
alias_method :new_search, :build_search
|
9
|
+
include Protection
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def calculate_with_searchgasm(*args)
|
14
|
+
options = args.extract_options!
|
15
|
+
options = sanitize_options_with_searchgasm(options)
|
16
|
+
args << options
|
17
|
+
calculate_without_searchgasm(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_with_searchgasm(*args)
|
21
|
+
options = args.extract_options!
|
22
|
+
options = sanitize_options_with_searchgasm(options)
|
23
|
+
args << options
|
24
|
+
find_without_searchgasm(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def scope_with_searchgasm(method, key = nil)
|
28
|
+
scope = scope_without_searchgasm(method, key)
|
29
|
+
return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find
|
30
|
+
scope
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_conditions(options = {})
|
34
|
+
BinaryLogic::Searchgasm::Search::Conditions(self, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_search(options = {})
|
38
|
+
searcher = searchgasm_searcher(options)
|
39
|
+
yield searcher if block_given?
|
40
|
+
searcher
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def sanitize_options_with_searchgasm(options)
|
45
|
+
searchgasm_searcher(options).sanitize
|
46
|
+
end
|
47
|
+
|
48
|
+
def searchgasm_searcher(options)
|
49
|
+
BinaryLogic::Searchgasm::Search::Base.new(self, options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
ActiveRecord::Base.send(:extend, BinaryLogic::Searchgasm::ActiveRecord::Base)
|
57
|
+
|
58
|
+
module ::ActiveRecord
|
59
|
+
class Base
|
60
|
+
class << self
|
61
|
+
alias_method_chain :calculate, :searchgasm
|
62
|
+
alias_method_chain :find, :searchgasm
|
63
|
+
alias_method_chain :scope, :searchgasm
|
64
|
+
|
65
|
+
def valid_find_options
|
66
|
+
VALID_FIND_OPTIONS
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module BinaryLogic
|
2
|
+
module Searchgasm
|
3
|
+
module ActiveRecord
|
4
|
+
module Protection
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
alias_method :new_search, :build_search
|
8
|
+
alias_method :findwp, :find_with_protection
|
9
|
+
alias_method :allwp, :all_with_protection
|
10
|
+
alias_method :firstwp, :first_with_protection
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_with_protection(*args)
|
15
|
+
options = args.extract_options!
|
16
|
+
options.merge!(:protect => true)
|
17
|
+
args << options
|
18
|
+
find(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def all_with_protection(*args)
|
22
|
+
options = args.extract_options!
|
23
|
+
options.merge!(:protect => true)
|
24
|
+
args << options
|
25
|
+
all(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def first_with_protection(*args)
|
29
|
+
options = args.extract_options!
|
30
|
+
options.merge!(:protect => true)
|
31
|
+
args << options
|
32
|
+
first(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
3
|
+
module BinaryLogic
|
4
|
+
module SearchGasm
|
5
|
+
module Helpers
|
6
|
+
def order_by_link(text, searcher, options = {})
|
7
|
+
options[:order_by] ||= text.underscore.gsub(/ /, "_")
|
8
|
+
options[:order_as] ||= "ASC"
|
9
|
+
options[:form_prefix] ||= determine_form_prefix(searcher)
|
10
|
+
|
11
|
+
if searcher.order_by == options[:order_by]
|
12
|
+
options[:order_as] = searcher.order_as == "ASC" ? "DESC" : "ASC"
|
13
|
+
|
14
|
+
text = content_tag("span", text + (searcher.order_as == "ASC" ? " ▲" : " ▼"), :class => searcher.order_as == "ASC" ? "ordering asc" : "ordering desc")
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:order_by] = Base64.encode64(Marshal.dump(options[:order_by])) if !options[:order_by].nil? && !options[:order_by].is_a?(String) && !options[:order_by].is_a?(Symbol)
|
18
|
+
|
19
|
+
link_to_function(text, "submit_form({form_prefix: '#{options[:form_prefix]}', dont_reset: true, fields: {order_by: '#{escape_javascript(options[:order_by])}', order_as: '#{options[:order_as]}'}});")
|
20
|
+
end
|
21
|
+
|
22
|
+
# tag methods
|
23
|
+
#------------------------------------------------------------------------------
|
24
|
+
def page_select_tag(name, items_count, searcher, options = {})
|
25
|
+
options = options.dup
|
26
|
+
form_prefix = options.delete(:form_prefix) || determine_form_prefix(searcher)
|
27
|
+
options[:id] ||= "select_tag_#{unique_id}"
|
28
|
+
options[:onchange] ||= "submit_form({form_prefix: '#{form_prefix}', dont_reset: true, fields: {page: this.value}});"
|
29
|
+
items_count = items_count.to_i
|
30
|
+
per_page = searcher.per_page.to_i
|
31
|
+
page = searcher.page.to_i
|
32
|
+
page = 1 if page < 1
|
33
|
+
page_options = page_options_for_select(items_count, per_page)
|
34
|
+
|
35
|
+
html = ""
|
36
|
+
if page_options.size > 0
|
37
|
+
html << (button_to_function("Prev", "sel = $('#{options[:id]}'); sel.value = '#{page-1}'; sel.onchange();", :class => "prev_page") + " ") if page > 1
|
38
|
+
html << select_tag(name, options_for_select(page_options, page), options)
|
39
|
+
html << (" " + button_to_function("Next", "sel = $('#{options[:id]}'); sel.value = '#{page+1}'; sel.onchange();", :class => "next_page")) if page < page_options.size
|
40
|
+
end
|
41
|
+
|
42
|
+
html
|
43
|
+
end
|
44
|
+
|
45
|
+
def per_page_select_tag(name, items_count, searcher, options = {})
|
46
|
+
options = options.dup
|
47
|
+
form_prefix = options.delete(:form_prefix) || determine_form_prefix(searcher)
|
48
|
+
options[:onchange] ||= "submit_form({form_prefix: '#{form_prefix}', dont_reset: true, fields: {per_page: this.value}});"
|
49
|
+
items_count = items_count.to_i
|
50
|
+
per_page = searcher.per_page.to_i
|
51
|
+
per_page = 0 if items_count <= per_page
|
52
|
+
|
53
|
+
# set up per page options
|
54
|
+
per_page_options = per_page_options_for_select(items_count)
|
55
|
+
|
56
|
+
return select_tag(name, options_for_select(per_page_options, per_page), options) if per_page_options.size > 0
|
57
|
+
|
58
|
+
""
|
59
|
+
end
|
60
|
+
|
61
|
+
# utility methods
|
62
|
+
#------------------------------------------------------------------------------
|
63
|
+
def page_options_for_select(items_count, per_page)
|
64
|
+
page_count = per_page > 0 ? (items_count.to_f / per_page.to_f).ceil : 1
|
65
|
+
page_options = []
|
66
|
+
if page_count > 1
|
67
|
+
page_count.times do |page|
|
68
|
+
page_number = page + 1
|
69
|
+
page_options << ["Page #{page_number} of #{page_count}", page_number]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
page_options
|
73
|
+
end
|
74
|
+
|
75
|
+
def per_page_options_for_select(items_count)
|
76
|
+
per_page_options = []
|
77
|
+
per_page_values = [10, 20, 30, 40, 50, 75, 100, 125, 150, 175, 200, 300, 400, 500, 1000, 1500, 2000]
|
78
|
+
per_page_values.each do |per_page_num|
|
79
|
+
if items_count > per_page_num
|
80
|
+
per_page_options << ["#{per_page_num} per page", per_page_num] if per_page_num > 0
|
81
|
+
else
|
82
|
+
break
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if per_page_options.size > 0
|
87
|
+
per_page_options << ["Show all #{items_count}", 0]
|
88
|
+
end
|
89
|
+
|
90
|
+
per_page_options
|
91
|
+
end
|
92
|
+
|
93
|
+
def determine_form_prefix(searcher)
|
94
|
+
"#{searcher.class.name.underscore.gsub(/_searcher/, "").pluralize}_search_"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
ActionController::Base.helper BinaryLogic::SearchGasm::Helpers
|