searchgasm 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +64 -0
- data/Manifest +3 -1
- data/README.rdoc +19 -29
- data/lib/searchgasm/active_record/associations.rb +10 -1
- data/lib/searchgasm/active_record/base.rb +60 -10
- data/lib/searchgasm/active_record.rb +8 -0
- data/lib/searchgasm/conditions/base.rb +66 -19
- data/lib/searchgasm/core_ext/hash.rb +22 -0
- data/lib/searchgasm/helpers/control_types/links.rb +65 -31
- data/lib/searchgasm/helpers/control_types/select.rb +1 -1
- data/lib/searchgasm/helpers/control_types.rb +18 -2
- data/lib/searchgasm/helpers/form.rb +10 -4
- data/lib/searchgasm/helpers/utilities.rb +27 -2
- data/lib/searchgasm/helpers.rb +7 -1
- data/lib/searchgasm/search/base.rb +38 -9
- data/lib/searchgasm/search/conditions.rb +26 -6
- data/lib/searchgasm/search/ordering.rb +14 -3
- data/lib/searchgasm/search/pagination.rb +52 -7
- data/lib/searchgasm/search/protection.rb +25 -3
- data/lib/searchgasm/version.rb +1 -1
- data/lib/searchgasm.rb +3 -0
- data/searchgasm.gemspec +9 -5
- data/test/test_condition_base.rb +4 -0
- data/test/test_conditions_base.rb +10 -10
- data/test/test_search_base.rb +13 -9
- data/test/test_search_conditions.rb +2 -2
- data/test/test_search_ordering.rb +2 -2
- data/test/test_search_pagination.rb +3 -3
- metadata +8 -4
- data/CHANGELOG +0 -23
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
== 1.0.1 released 2008-09-08
|
2
|
+
|
3
|
+
* Cached "searchers" so when a new search object is instantiated it doesn't go through all of the meta programming and method creation. Helps a lot with performance. You will see the speed benefits after the first instantiation.
|
4
|
+
* Added in new options for page_links.
|
5
|
+
* Fixed minor bugs when doing page_links.
|
6
|
+
* Updated documentation to be more detailed and inclusive.
|
7
|
+
|
8
|
+
== 1.0.0 released 2008-09-08
|
9
|
+
|
10
|
+
* Major changes in the helpers, they were completely re-engineered. Hence the new version. I established a pattern between all helpers giving you complete flexibility as to how they are used. All helpers are called differently now (see documentation).
|
11
|
+
|
12
|
+
== 0.9.10 released 2008-09-08
|
13
|
+
|
14
|
+
* Fixed bug with setting the per_page configuration to only take effect on protected searches, thus staying out of the way of normal searching.
|
15
|
+
* Hardened more tests
|
16
|
+
|
17
|
+
== 0.9.9 released 2008-09-07
|
18
|
+
|
19
|
+
* Fixed setting per_page to nil, false, or ''. This is done to "show all" results.
|
20
|
+
|
21
|
+
== 0.9.8 released 2008-09-06
|
22
|
+
|
23
|
+
* Fixed order_by helper bug when guessing the text with arrays. Should use the first value instead of last.
|
24
|
+
* Added in per_page config option.
|
25
|
+
|
26
|
+
== 0.9.7 released 2008-09-06
|
27
|
+
|
28
|
+
* Complete class restructure. Moved the 3 main components into their own base level class: Search, Conditions, Condition
|
29
|
+
* Split logic and functionality into their own modules, implemented via alias_chain_method
|
30
|
+
* Added in helpers for using in a rails app
|
31
|
+
* Added link to documentation and live example in README
|
32
|
+
* Various small bug fixes
|
33
|
+
* Hardened tests
|
34
|
+
|
35
|
+
== 0.9.6 released 2008-09-04
|
36
|
+
|
37
|
+
* Fixed bug when instantiating with nil options
|
38
|
+
|
39
|
+
== 0.9.5 released 2008-09-03
|
40
|
+
|
41
|
+
* Enhanced searching with conditions only, added in search methods and calculations
|
42
|
+
* Updated README to include examples
|
43
|
+
|
44
|
+
== 0.9.4 released 2008-09-03
|
45
|
+
|
46
|
+
* Cleaned up search methods
|
47
|
+
* Removed reset!method for both searching and searching by conditions
|
48
|
+
|
49
|
+
== 0.9.3 released 2008-09-02
|
50
|
+
|
51
|
+
* Changed structure of conditions to have their own class
|
52
|
+
* Added API for adding your own conditions.
|
53
|
+
|
54
|
+
== 0.9.2 released 2008-09-02
|
55
|
+
|
56
|
+
* Enhanced protection from SQL injections (made more efficient)
|
57
|
+
|
58
|
+
== 0.9.1 released 2008-09-02
|
59
|
+
|
60
|
+
* Added aliases for datetime, date, time, and timestamp attrs. You could call created_at_after, now you can also call created_after. Just removed the "at" requirement.
|
61
|
+
|
62
|
+
== 0.9.0 released 2008-09-01
|
63
|
+
|
64
|
+
* First release
|
data/Manifest
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
CHANGELOG
|
1
|
+
CHANGELOG.rdoc
|
2
2
|
examples/README.rdoc
|
3
3
|
init.rb
|
4
4
|
lib/searchgasm/active_record/associations.rb
|
5
5
|
lib/searchgasm/active_record/base.rb
|
6
|
+
lib/searchgasm/active_record.rb
|
6
7
|
lib/searchgasm/condition/base.rb
|
7
8
|
lib/searchgasm/condition/begins_with.rb
|
8
9
|
lib/searchgasm/condition/child_of.rb
|
@@ -22,6 +23,7 @@ lib/searchgasm/condition/tree.rb
|
|
22
23
|
lib/searchgasm/conditions/base.rb
|
23
24
|
lib/searchgasm/conditions/protection.rb
|
24
25
|
lib/searchgasm/config.rb
|
26
|
+
lib/searchgasm/core_ext/hash.rb
|
25
27
|
lib/searchgasm/helpers/control_types/link.rb
|
26
28
|
lib/searchgasm/helpers/control_types/links.rb
|
27
29
|
lib/searchgasm/helpers/control_types/remote_link.rb
|
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab
|
|
8
8
|
|
9
9
|
* <b>Documentation:</b> http://searchgasm.rubyforge.org
|
10
10
|
* <b>Easy pagination, ordering, and searching tutorial:</b> http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm
|
11
|
-
* <b>
|
11
|
+
* <b>Live example of the tutorial above (with source):</b> http://searchgasm_example.binarylogic.com
|
12
12
|
|
13
13
|
== Install and use
|
14
14
|
|
@@ -67,8 +67,8 @@ Now your view. Things to note in this view:
|
|
67
67
|
|
68
68
|
1. Passing a search object right into form\_for and fields\_for
|
69
69
|
2. The built in conditions for each column and how you can traverse the relationships and set conditions on them
|
70
|
-
3. The
|
71
|
-
4. The
|
70
|
+
3. The order_by_link helper
|
71
|
+
4. The page_select and per_page_select helpers
|
72
72
|
5. All of your search logic is in 1 spot: your view. Nice and DRY.
|
73
73
|
|
74
74
|
Your view:
|
@@ -82,25 +82,28 @@ Your view:
|
|
82
82
|
= orders.select :total_gt, (1..100)
|
83
83
|
= f.submit "Search"
|
84
84
|
|
85
|
-
|
86
|
-
%
|
87
|
-
%
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
- if @users_count > 0
|
86
|
+
%table
|
87
|
+
%tr
|
88
|
+
%th= order_by_link :account => :name
|
89
|
+
%th= order_by_link :first_name
|
90
|
+
%th= order_by_link :last_name
|
91
|
+
%th= order_by_link :email
|
92
|
+
- @users.each do |user|
|
92
93
|
%tr
|
93
94
|
%td= user.account? ? user.account.name : "-"
|
94
95
|
%td= user.first_name
|
95
96
|
%td= user.last_name
|
96
97
|
%td= user.email
|
97
98
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
Per page:
|
100
|
+
= per_page_select
|
101
|
+
Page:
|
102
|
+
= page_select
|
103
|
+
- else
|
104
|
+
No users were found.
|
102
105
|
|
103
|
-
<b>
|
106
|
+
<b>See my tutorial on this example: http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm</b>
|
104
107
|
|
105
108
|
== Exhaustive Example w/ Object Based Searching (great for form_for or fields_for)
|
106
109
|
|
@@ -174,13 +177,6 @@ Any of the options used in the above example can be used in these, but for the s
|
|
174
177
|
search.per_page = 20
|
175
178
|
search.all
|
176
179
|
|
177
|
-
If you want to use Searchgasm directly:
|
178
|
-
|
179
|
-
search = Searchgasm::Search::Base.new(User, :conditions => {:age_gt => 18})
|
180
|
-
search.conditions.first_name_contains = "Ben"
|
181
|
-
search.per_page = 20
|
182
|
-
search.all
|
183
|
-
|
184
180
|
== Search with conditions only
|
185
181
|
|
186
182
|
Don't need pagination, ordering, or any of the other options? Search with conditions only.
|
@@ -194,19 +190,13 @@ Pass a conditions object right into ActiveRecord:
|
|
194
190
|
|
195
191
|
User.all(:conditions => conditions)
|
196
192
|
|
197
|
-
Again, if you want to use Searchgasm directly:
|
198
|
-
|
199
|
-
conditions = Searchgasm::Conditions::Base.new(User, :age_gt => 18)
|
200
|
-
conditions.first_name_contains = "Ben"
|
201
|
-
conditions.all
|
202
|
-
|
203
193
|
== Scoped searching
|
204
194
|
|
205
195
|
@current_user.orders.find(:all, :conditions => {:total_lte => 500})
|
206
196
|
@current_user.orders.count(:conditions => {:total_lte => 500})
|
207
197
|
@current_user.orders.sum('total', :conditions => {:total_lte => 500})
|
208
198
|
|
209
|
-
search = @current_user.orders.build_search(
|
199
|
+
search = @current_user.orders.build_search(:conditions => {:total_lte => 500})
|
210
200
|
|
211
201
|
== Searching trees
|
212
202
|
|
@@ -1,31 +1,39 @@
|
|
1
1
|
module Searchgasm
|
2
2
|
module ActiveRecord
|
3
|
+
# = Searchgasm ActiveRecord Associations
|
4
|
+
#
|
5
|
+
# These methods hook into ActiveRecords association methods and add in searchgasm functionality.
|
3
6
|
module Associations
|
4
7
|
module AssociationCollection
|
8
|
+
# This is an alias method chain. It hook into ActiveRecord's "find" method for associations and checks to see if Searchgasm should get involved.
|
5
9
|
def find_with_searchgasm(*args)
|
6
10
|
options = args.extract_options!
|
7
11
|
args << sanitize_options_with_searchgasm(options)
|
8
12
|
find_without_searchgasm(*args)
|
9
13
|
end
|
10
14
|
|
15
|
+
# See build_conditions under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
|
11
16
|
def build_conditions(options = {}, &block)
|
12
17
|
conditions = @reflection.klass.build_conditions(options, &block)
|
13
18
|
conditions.scope = scope(:find)[:conditions]
|
14
19
|
conditions
|
15
20
|
end
|
16
21
|
|
22
|
+
# See build_conditions! under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
|
17
23
|
def build_conditions!(options = {}, &block)
|
18
24
|
conditions = @reflection.klass.build_conditions!(options, &block)
|
19
25
|
conditions.scope = scope(:find)[:conditions]
|
20
26
|
conditions
|
21
27
|
end
|
22
|
-
|
28
|
+
|
29
|
+
# See build_search under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
|
23
30
|
def build_search(options = {}, &block)
|
24
31
|
conditions = @reflection.klass.build_search(options, &block)
|
25
32
|
conditions.scope = scope(:find)[:conditions]
|
26
33
|
conditions
|
27
34
|
end
|
28
35
|
|
36
|
+
# See build_conditions! under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
|
29
37
|
def build_search!(options = {}, &block)
|
30
38
|
conditions = @reflection.klass.build_search!(options, &block)
|
31
39
|
conditions.scope = scope(:find)[:conditions]
|
@@ -34,6 +42,7 @@ module Searchgasm
|
|
34
42
|
end
|
35
43
|
|
36
44
|
module HasManyAssociation
|
45
|
+
# This is an alias method chain. It hook into ActiveRecord's "calculate" method for has many associations and checks to see if Searchgasm should get involved.
|
37
46
|
def count_with_searchgasm(*args)
|
38
47
|
column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
|
39
48
|
count_without_searchgasm(column_name, sanitize_options_with_searchgasm(options))
|
@@ -1,13 +1,17 @@
|
|
1
1
|
module Searchgasm
|
2
|
-
module ActiveRecord
|
2
|
+
module ActiveRecord
|
3
|
+
# = Searchgasm ActiveRecord Base
|
4
|
+
# Adds in base level functionality to ActiveRecord
|
3
5
|
module Base
|
6
|
+
# This is an alias method chain. It hook into ActiveRecord's "calculate" method and checks to see if Searchgasm should get involved.
|
4
7
|
def calculate_with_searchgasm(*args)
|
5
8
|
options = args.extract_options!
|
6
9
|
options = sanitize_options_with_searchgasm(options)
|
7
10
|
args << options
|
8
11
|
calculate_without_searchgasm(*args)
|
9
12
|
end
|
10
|
-
|
13
|
+
|
14
|
+
# This is an alias method chain. It hooks into ActiveRecord's "find" method and checks to see if Searchgasm should get involved.
|
11
15
|
def find_with_searchgasm(*args)
|
12
16
|
options = args.extract_options!
|
13
17
|
options = sanitize_options_with_searchgasm(options)
|
@@ -15,12 +19,31 @@ module Searchgasm
|
|
15
19
|
find_without_searchgasm(*args)
|
16
20
|
end
|
17
21
|
|
22
|
+
# This is an alias method chain. It hooks into ActiveRecord's scopes and checks to see if Searchgasm should get involved. Allowing you to use all of Searchgasms conditions and tools
|
23
|
+
# in scopes as well.
|
24
|
+
#
|
25
|
+
# === Examples
|
26
|
+
#
|
27
|
+
# named_scope :top_expensive, :conditions => {:total_gt => 1_000_000}, :per_page => 10
|
28
|
+
#
|
29
|
+
# with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
|
30
|
+
# find(:all)
|
31
|
+
# end
|
18
32
|
def scope_with_searchgasm(method, key = nil)
|
19
33
|
scope = scope_without_searchgasm(method, key)
|
20
34
|
return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
|
21
35
|
scope
|
22
36
|
end
|
23
|
-
|
37
|
+
|
38
|
+
# This is a special method that Searchgasm adds in. It returns a new conditions object on the model. So you can search by conditions *only*.
|
39
|
+
#
|
40
|
+
# <b>This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating.</b>
|
41
|
+
#
|
42
|
+
# === Examples
|
43
|
+
#
|
44
|
+
# conditions = User.new_conditions
|
45
|
+
# conditions.first_name_contains = "Ben"
|
46
|
+
# conditions.all # can call any search method: first, find(:all), find(:first), sum("id"), etc...
|
24
47
|
def build_conditions(values = {}, &block)
|
25
48
|
conditions = searchgasm_conditions
|
26
49
|
conditions.protect = true
|
@@ -28,13 +51,28 @@ module Searchgasm
|
|
28
51
|
yield conditions if block_given?
|
29
52
|
conditions
|
30
53
|
end
|
31
|
-
|
54
|
+
|
55
|
+
# See build_conditions. This is the same method but *without* protection. Do *NOT* pass in a params object to this method.
|
32
56
|
def build_conditions!(values = {}, &block)
|
33
57
|
conditions = searchgasm_conditions(values)
|
34
58
|
yield conditions if block_given?
|
35
59
|
conditions
|
36
60
|
end
|
37
|
-
|
61
|
+
|
62
|
+
# This is a special method that Searchgasm adds in. It returns a new search object on the model. So you can search via an object.
|
63
|
+
#
|
64
|
+
# <b>This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating.</b>
|
65
|
+
#
|
66
|
+
# This method has an alias "new_search"
|
67
|
+
#
|
68
|
+
# === Examples
|
69
|
+
#
|
70
|
+
# search = User.new_search
|
71
|
+
# search.conditions.first_name_contains = "Ben"
|
72
|
+
# search.per_page = 20
|
73
|
+
# search.page = 2
|
74
|
+
# search.order_by = {:user_group => :name}
|
75
|
+
# search.all # can call any search method: first, find(:all), find(:first), sum("id"), etc...
|
38
76
|
def build_search(options = {}, &block)
|
39
77
|
search = searchgasm_searcher
|
40
78
|
search.protect = true
|
@@ -42,26 +80,38 @@ module Searchgasm
|
|
42
80
|
yield search if block_given?
|
43
81
|
search
|
44
82
|
end
|
45
|
-
|
83
|
+
|
84
|
+
# See build_search. This is the same method but *without* protection. Do *NOT* pass in a params object to this method.
|
85
|
+
#
|
86
|
+
# This also has an alias "new_search!"
|
46
87
|
def build_search!(options = {}, &block)
|
47
88
|
search = searchgasm_searcher(options)
|
48
89
|
yield search if block_given?
|
49
90
|
search
|
50
91
|
end
|
51
92
|
|
93
|
+
# Similar to ActiveRecord's attr_protected, but for conditions. It will block any conditions in this array that are being mass assigned. Mass assignments are:
|
94
|
+
#
|
95
|
+
# === Examples
|
96
|
+
#
|
97
|
+
# search = User.new_search(:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"})
|
98
|
+
# search.options = {:conditions => {:first_name_like => "Ben", :email_contains => "binarylogic.com"}}
|
99
|
+
#
|
100
|
+
# If first_name_like is in the list of conditions_protected then it will be removed from the hash.
|
52
101
|
def conditions_protected(*conditions)
|
53
102
|
write_inheritable_attribute(:conditions_protected, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
|
54
103
|
end
|
55
104
|
|
56
|
-
def protected_conditions
|
105
|
+
def protected_conditions # :nodoc:
|
57
106
|
read_inheritable_attribute(:conditions_protected)
|
58
107
|
end
|
59
108
|
|
109
|
+
# This is the reverse of conditions_protected. You can specify conditions here and *only* these conditions will be allowed in mass assignment. Any condition not specified here will be blocked.
|
60
110
|
def conditions_accessible(*conditions)
|
61
111
|
write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (protected_conditions || []))
|
62
112
|
end
|
63
113
|
|
64
|
-
def accessible_conditions
|
114
|
+
def accessible_conditions # :nodoc:
|
65
115
|
read_inheritable_attribute(:conditions_accessible)
|
66
116
|
end
|
67
117
|
|
@@ -72,11 +122,11 @@ module Searchgasm
|
|
72
122
|
end
|
73
123
|
|
74
124
|
def searchgasm_conditions(options = {})
|
75
|
-
Searchgasm::Conditions::Base.new(
|
125
|
+
Searchgasm::Conditions::Base.create_virtual_class(self).new(options)
|
76
126
|
end
|
77
127
|
|
78
128
|
def searchgasm_searcher(options = {})
|
79
|
-
Searchgasm::Search::Base.new(
|
129
|
+
Searchgasm::Search::Base.create_virtual_class(self).new(options)
|
80
130
|
end
|
81
131
|
end
|
82
132
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
# == Searchgasm ActiveRecord
|
3
|
+
#
|
4
|
+
# Hooks into ActiveRecord to add all of the searchgasm functionality into your models. Only uses what is publically available, doesn't dig into internals, and
|
5
|
+
# searchgasm only gets involved when needed.
|
6
|
+
module ActiveRecord
|
7
|
+
end
|
8
|
+
end
|
@@ -7,9 +7,11 @@ module Searchgasm
|
|
7
7
|
class Base
|
8
8
|
include Utilities
|
9
9
|
|
10
|
-
attr_accessor :
|
10
|
+
attr_accessor :relationship_name, :scope
|
11
11
|
|
12
12
|
class << self
|
13
|
+
attr_accessor :added_klass_conditions, :added_column_conditions, :added_associations
|
14
|
+
|
13
15
|
# Registers a condition as an available condition for a column or a class.
|
14
16
|
#
|
15
17
|
# === Example
|
@@ -40,9 +42,9 @@ module Searchgasm
|
|
40
42
|
# end
|
41
43
|
#
|
42
44
|
# Searchgasm::Seearch::Conditions.register_condition(SoundsLikeCondition)
|
43
|
-
def register_condition(
|
44
|
-
raise(ArgumentError, "You can only register conditions that extend Searchgasm::Condition::Base") unless
|
45
|
-
conditions <<
|
45
|
+
def register_condition(condition_class)
|
46
|
+
raise(ArgumentError, "You can only register conditions that extend Searchgasm::Condition::Base") unless condition_class.ancestors.include?(Searchgasm::Condition::Base)
|
47
|
+
conditions << condition_class unless conditions.include?(condition_class)
|
46
48
|
end
|
47
49
|
|
48
50
|
# A list of available condition type classes
|
@@ -50,19 +52,50 @@ module Searchgasm
|
|
50
52
|
@@conditions ||= []
|
51
53
|
end
|
52
54
|
|
53
|
-
|
55
|
+
# A list of all associations created, used for caching and performance
|
56
|
+
def association_names
|
57
|
+
@association_names ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
# A list of all conditions available, users for caching and performance
|
61
|
+
def condition_names
|
62
|
+
@condition_names ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
def needed?(model_class, conditions) # :nodoc:
|
54
66
|
if conditions.is_a?(Hash)
|
55
67
|
conditions.stringify_keys.keys.each do |condition|
|
56
|
-
return true unless
|
68
|
+
return true unless model_class.column_names.include?(condition)
|
57
69
|
end
|
58
70
|
end
|
59
71
|
|
60
72
|
false
|
61
73
|
end
|
74
|
+
|
75
|
+
# Creates virtual classes for the class passed to it. This is a neccesity for keeping dynamically created method
|
76
|
+
# names specific to models. It provides caching and helps a lot with performance.
|
77
|
+
def create_virtual_class(model_class)
|
78
|
+
class_search_name = "::#{model_class.name}Conditions"
|
79
|
+
|
80
|
+
begin
|
81
|
+
class_search_name.constantize
|
82
|
+
rescue NameError
|
83
|
+
eval <<-end_eval
|
84
|
+
class #{class_search_name} < ::Searchgasm::Conditions::Base; end;
|
85
|
+
end_eval
|
86
|
+
|
87
|
+
class_search_name.constantize
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# The class / model we are searching
|
92
|
+
def klass
|
93
|
+
# Can't cache this because thin and mongrel don't play nice with caching constants
|
94
|
+
name.split("::").last.gsub(/Conditions$/, "").constantize
|
95
|
+
end
|
62
96
|
end
|
63
97
|
|
64
|
-
def initialize(
|
65
|
-
self.klass = klass
|
98
|
+
def initialize(init_conditions = {})
|
66
99
|
add_klass_conditions!
|
67
100
|
add_column_conditions!
|
68
101
|
add_associations!
|
@@ -90,6 +123,10 @@ module Searchgasm
|
|
90
123
|
i.blank? ? nil : (i.size == 1 ? i.first : i)
|
91
124
|
end
|
92
125
|
|
126
|
+
def klass
|
127
|
+
self.class.klass
|
128
|
+
end
|
129
|
+
|
93
130
|
# Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
|
94
131
|
def sanitize
|
95
132
|
conditions = merge_conditions(*objects.collect { |object| object.sanitize })
|
@@ -128,11 +165,15 @@ module Searchgasm
|
|
128
165
|
|
129
166
|
private
|
130
167
|
def add_associations!
|
168
|
+
return true if self.class.added_associations
|
169
|
+
|
131
170
|
klass.reflect_on_all_associations.each do |association|
|
171
|
+
self.class.association_names << association.name.to_s
|
172
|
+
|
132
173
|
self.class.class_eval <<-"end_eval", __FILE__, __LINE__
|
133
174
|
def #{association.name}
|
134
175
|
if @#{association.name}.nil?
|
135
|
-
@#{association.name} =
|
176
|
+
@#{association.name} = #{association.class_name}.new_conditions
|
136
177
|
@#{association.name}.relationship_name = "#{association.name}"
|
137
178
|
objects << @#{association.name}
|
138
179
|
end
|
@@ -143,9 +184,13 @@ module Searchgasm
|
|
143
184
|
def reset_#{association.name}!; objects.delete(#{association.name}); @#{association.name} = nil; end
|
144
185
|
end_eval
|
145
186
|
end
|
187
|
+
|
188
|
+
self.class.added_associations = true
|
146
189
|
end
|
147
190
|
|
148
191
|
def add_column_conditions!
|
192
|
+
return true if self.class.added_column_conditions
|
193
|
+
|
149
194
|
klass.columns.each do |column|
|
150
195
|
self.class.conditions.each do |condition_klass|
|
151
196
|
name = condition_klass.name_for_column(column)
|
@@ -154,10 +199,13 @@ module Searchgasm
|
|
154
199
|
condition_klass.aliases_for_column(column).each { |alias_name| add_condition_alias!(alias_name, name) }
|
155
200
|
end
|
156
201
|
end
|
202
|
+
|
203
|
+
self.class.added_column_conditions = true
|
157
204
|
end
|
158
205
|
|
159
206
|
def add_condition!(condition, name, column = nil)
|
160
|
-
condition_names << name
|
207
|
+
self.class.condition_names << name
|
208
|
+
|
161
209
|
self.class.class_eval <<-"end_eval", __FILE__, __LINE__
|
162
210
|
def #{name}_object
|
163
211
|
if @#{name}.nil?
|
@@ -174,7 +222,8 @@ module Searchgasm
|
|
174
222
|
end
|
175
223
|
|
176
224
|
def add_condition_alias!(alias_name, name)
|
177
|
-
condition_names << alias_name
|
225
|
+
self.class.condition_names << alias_name
|
226
|
+
|
178
227
|
self.class.class_eval do
|
179
228
|
alias_method alias_name, name
|
180
229
|
alias_method "#{alias_name}=", "#{name}="
|
@@ -182,26 +231,24 @@ module Searchgasm
|
|
182
231
|
end
|
183
232
|
|
184
233
|
def add_klass_conditions!
|
234
|
+
return true if self.class.added_klass_conditions
|
235
|
+
|
185
236
|
self.class.conditions.each do |condition|
|
186
237
|
name = condition.name_for_klass(klass)
|
187
238
|
next if name.blank?
|
188
239
|
add_condition!(condition, name)
|
189
240
|
condition.aliases_for_klass(klass).each { |alias_name| add_condition_alias!(alias_name, name) }
|
190
241
|
end
|
242
|
+
|
243
|
+
self.class.added_klass_conditions = true
|
191
244
|
end
|
192
245
|
|
193
246
|
def assert_valid_conditions(conditions)
|
194
|
-
|
195
|
-
keys += klass.reflect_on_all_associations.collect { |association| association.name }
|
196
|
-
conditions.symbolize_keys.assert_valid_keys(keys)
|
247
|
+
conditions.stringify_keys.assert_valid_keys(self.class.condition_names + self.class.association_names)
|
197
248
|
end
|
198
249
|
|
199
250
|
def associations
|
200
|
-
objects.select { |object| object.
|
201
|
-
end
|
202
|
-
|
203
|
-
def condition_names
|
204
|
-
@condition_names ||= []
|
251
|
+
objects.select { |object| object.class < ::Searchgasm::Conditions::Base }
|
205
252
|
end
|
206
253
|
|
207
254
|
def objects
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Searchgasm
|
2
|
+
module CoreExt # :nodoc: all
|
3
|
+
module Hash
|
4
|
+
def deep_dup
|
5
|
+
new_hash = {}
|
6
|
+
|
7
|
+
self.each do |k, v|
|
8
|
+
case v
|
9
|
+
when Hash
|
10
|
+
new_hash[k] = v.deep_dup
|
11
|
+
else
|
12
|
+
new_hash[k] = v
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
new_hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Hash.send(:include, Searchgasm::CoreExt::Hash)
|