searchlogic 1.6.6 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (178) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGELOG.rdoc +17 -0
  3. data/{MIT-LICENSE → LICENSE} +2 -2
  4. data/README.rdoc +128 -379
  5. data/Rakefile +56 -20
  6. data/VERSION.yml +4 -0
  7. data/init.rb +1 -1
  8. data/lib/searchlogic.rb +18 -98
  9. data/lib/searchlogic/core_ext/object.rb +33 -13
  10. data/lib/searchlogic/core_ext/proc.rb +11 -0
  11. data/lib/searchlogic/named_scopes/alias_scope.rb +63 -0
  12. data/lib/searchlogic/named_scopes/associations.rb +126 -0
  13. data/lib/searchlogic/named_scopes/conditions.rb +215 -0
  14. data/lib/searchlogic/named_scopes/ordering.rb +53 -0
  15. data/lib/searchlogic/rails_helpers.rb +69 -0
  16. data/lib/searchlogic/search.rb +146 -0
  17. data/rails/init.rb +1 -0
  18. data/searchlogic.gemspec +69 -0
  19. data/spec/core_ext/object_spec.rb +7 -0
  20. data/spec/core_ext/proc_spec.rb +9 -0
  21. data/spec/named_scopes/alias_scope_spec.rb +15 -0
  22. data/spec/named_scopes/associations_spec.rb +120 -0
  23. data/spec/named_scopes/conditions_spec.rb +253 -0
  24. data/spec/named_scopes/ordering_spec.rb +23 -0
  25. data/spec/search_spec.rb +283 -0
  26. data/spec/spec_helper.rb +78 -0
  27. metadata +40 -231
  28. data/Manifest.txt +0 -158
  29. data/TODO.rdoc +0 -4
  30. data/lib/searchlogic/active_record/associations.rb +0 -52
  31. data/lib/searchlogic/active_record/base.rb +0 -224
  32. data/lib/searchlogic/active_record/connection_adapters/mysql_adapter.rb +0 -176
  33. data/lib/searchlogic/active_record/connection_adapters/postgresql_adapter.rb +0 -172
  34. data/lib/searchlogic/active_record/connection_adapters/sqlite_adapter.rb +0 -80
  35. data/lib/searchlogic/condition/base.rb +0 -165
  36. data/lib/searchlogic/condition/begins_with.rb +0 -17
  37. data/lib/searchlogic/condition/blank.rb +0 -24
  38. data/lib/searchlogic/condition/child_of.rb +0 -11
  39. data/lib/searchlogic/condition/descendant_of.rb +0 -11
  40. data/lib/searchlogic/condition/ends_with.rb +0 -17
  41. data/lib/searchlogic/condition/equals.rb +0 -33
  42. data/lib/searchlogic/condition/greater_than.rb +0 -15
  43. data/lib/searchlogic/condition/greater_than_or_equal_to.rb +0 -15
  44. data/lib/searchlogic/condition/inclusive_descendant_of.rb +0 -10
  45. data/lib/searchlogic/condition/keywords.rb +0 -52
  46. data/lib/searchlogic/condition/less_than.rb +0 -15
  47. data/lib/searchlogic/condition/less_than_or_equal_to.rb +0 -15
  48. data/lib/searchlogic/condition/like.rb +0 -15
  49. data/lib/searchlogic/condition/nested_set.rb +0 -17
  50. data/lib/searchlogic/condition/nil.rb +0 -21
  51. data/lib/searchlogic/condition/not_begin_with.rb +0 -20
  52. data/lib/searchlogic/condition/not_blank.rb +0 -19
  53. data/lib/searchlogic/condition/not_end_with.rb +0 -20
  54. data/lib/searchlogic/condition/not_equal.rb +0 -27
  55. data/lib/searchlogic/condition/not_have_keywords.rb +0 -20
  56. data/lib/searchlogic/condition/not_like.rb +0 -20
  57. data/lib/searchlogic/condition/not_nil.rb +0 -19
  58. data/lib/searchlogic/condition/sibling_of.rb +0 -14
  59. data/lib/searchlogic/conditions/any_or_all.rb +0 -42
  60. data/lib/searchlogic/conditions/base.rb +0 -244
  61. data/lib/searchlogic/conditions/groups.rb +0 -74
  62. data/lib/searchlogic/conditions/magic_methods.rb +0 -286
  63. data/lib/searchlogic/conditions/multiparameter_attributes.rb +0 -105
  64. data/lib/searchlogic/conditions/protection.rb +0 -36
  65. data/lib/searchlogic/config.rb +0 -31
  66. data/lib/searchlogic/config/helpers.rb +0 -338
  67. data/lib/searchlogic/config/search.rb +0 -53
  68. data/lib/searchlogic/core_ext/hash.rb +0 -75
  69. data/lib/searchlogic/helpers/control_types/link.rb +0 -310
  70. data/lib/searchlogic/helpers/control_types/links.rb +0 -242
  71. data/lib/searchlogic/helpers/control_types/remote_link.rb +0 -87
  72. data/lib/searchlogic/helpers/control_types/remote_links.rb +0 -72
  73. data/lib/searchlogic/helpers/control_types/remote_select.rb +0 -36
  74. data/lib/searchlogic/helpers/control_types/select.rb +0 -82
  75. data/lib/searchlogic/helpers/form.rb +0 -208
  76. data/lib/searchlogic/helpers/utilities.rb +0 -197
  77. data/lib/searchlogic/modifiers/absolute.rb +0 -15
  78. data/lib/searchlogic/modifiers/acos.rb +0 -11
  79. data/lib/searchlogic/modifiers/asin.rb +0 -11
  80. data/lib/searchlogic/modifiers/atan.rb +0 -11
  81. data/lib/searchlogic/modifiers/avg.rb +0 -15
  82. data/lib/searchlogic/modifiers/base.rb +0 -27
  83. data/lib/searchlogic/modifiers/ceil.rb +0 -15
  84. data/lib/searchlogic/modifiers/char_length.rb +0 -15
  85. data/lib/searchlogic/modifiers/cos.rb +0 -15
  86. data/lib/searchlogic/modifiers/cot.rb +0 -15
  87. data/lib/searchlogic/modifiers/count.rb +0 -11
  88. data/lib/searchlogic/modifiers/day_of_month.rb +0 -15
  89. data/lib/searchlogic/modifiers/day_of_week.rb +0 -15
  90. data/lib/searchlogic/modifiers/day_of_year.rb +0 -15
  91. data/lib/searchlogic/modifiers/degrees.rb +0 -11
  92. data/lib/searchlogic/modifiers/exp.rb +0 -15
  93. data/lib/searchlogic/modifiers/floor.rb +0 -15
  94. data/lib/searchlogic/modifiers/hex.rb +0 -11
  95. data/lib/searchlogic/modifiers/hour.rb +0 -11
  96. data/lib/searchlogic/modifiers/log.rb +0 -15
  97. data/lib/searchlogic/modifiers/log10.rb +0 -11
  98. data/lib/searchlogic/modifiers/log2.rb +0 -11
  99. data/lib/searchlogic/modifiers/lower.rb +0 -15
  100. data/lib/searchlogic/modifiers/ltrim.rb +0 -15
  101. data/lib/searchlogic/modifiers/md5.rb +0 -11
  102. data/lib/searchlogic/modifiers/microseconds.rb +0 -11
  103. data/lib/searchlogic/modifiers/milliseconds.rb +0 -11
  104. data/lib/searchlogic/modifiers/minute.rb +0 -15
  105. data/lib/searchlogic/modifiers/month.rb +0 -15
  106. data/lib/searchlogic/modifiers/octal.rb +0 -15
  107. data/lib/searchlogic/modifiers/radians.rb +0 -11
  108. data/lib/searchlogic/modifiers/round.rb +0 -11
  109. data/lib/searchlogic/modifiers/rtrim.rb +0 -15
  110. data/lib/searchlogic/modifiers/second.rb +0 -15
  111. data/lib/searchlogic/modifiers/sign.rb +0 -11
  112. data/lib/searchlogic/modifiers/sin.rb +0 -11
  113. data/lib/searchlogic/modifiers/square_root.rb +0 -15
  114. data/lib/searchlogic/modifiers/sum.rb +0 -11
  115. data/lib/searchlogic/modifiers/tan.rb +0 -15
  116. data/lib/searchlogic/modifiers/trim.rb +0 -15
  117. data/lib/searchlogic/modifiers/upper.rb +0 -15
  118. data/lib/searchlogic/modifiers/week.rb +0 -11
  119. data/lib/searchlogic/modifiers/year.rb +0 -11
  120. data/lib/searchlogic/search/base.rb +0 -148
  121. data/lib/searchlogic/search/conditions.rb +0 -53
  122. data/lib/searchlogic/search/ordering.rb +0 -244
  123. data/lib/searchlogic/search/pagination.rb +0 -121
  124. data/lib/searchlogic/search/protection.rb +0 -89
  125. data/lib/searchlogic/search/searching.rb +0 -32
  126. data/lib/searchlogic/shared/utilities.rb +0 -57
  127. data/lib/searchlogic/shared/virtual_classes.rb +0 -39
  128. data/lib/searchlogic/version.rb +0 -79
  129. data/test/active_record_tests/associations_test.rb +0 -94
  130. data/test/active_record_tests/base_test.rb +0 -115
  131. data/test/condition_tests/base_test.rb +0 -62
  132. data/test/condition_tests/begins_with_test.rb +0 -11
  133. data/test/condition_tests/blank_test.rb +0 -31
  134. data/test/condition_tests/child_of_test.rb +0 -17
  135. data/test/condition_tests/descendant_of_test.rb +0 -12
  136. data/test/condition_tests/ends_with_test.rb +0 -11
  137. data/test/condition_tests/equals_test.rb +0 -28
  138. data/test/condition_tests/greater_than_or_equal_to_test.rb +0 -11
  139. data/test/condition_tests/greater_than_test.rb +0 -11
  140. data/test/condition_tests/inclusive_descendant_of_test.rb +0 -12
  141. data/test/condition_tests/keywords_test.rb +0 -23
  142. data/test/condition_tests/less_than_or_equal_to_test.rb +0 -11
  143. data/test/condition_tests/less_than_test.rb +0 -11
  144. data/test/condition_tests/like_test.rb +0 -11
  145. data/test/condition_tests/nil_test.rb +0 -31
  146. data/test/condition_tests/not_begin_with_test.rb +0 -8
  147. data/test/condition_tests/not_blank_test.rb +0 -8
  148. data/test/condition_tests/not_end_with_test.rb +0 -8
  149. data/test/condition_tests/not_equal_test.rb +0 -19
  150. data/test/condition_tests/not_have_keywords_test.rb +0 -8
  151. data/test/condition_tests/not_like_test.rb +0 -8
  152. data/test/condition_tests/not_nil_test.rb +0 -13
  153. data/test/condition_tests/sibling_of_test.rb +0 -15
  154. data/test/conditions_tests/any_or_all_test.rb +0 -23
  155. data/test/conditions_tests/base_test.rb +0 -185
  156. data/test/conditions_tests/groups_test.rb +0 -68
  157. data/test/conditions_tests/magic_methods_test.rb +0 -36
  158. data/test/conditions_tests/multiparameter_attributes_test.rb +0 -15
  159. data/test/conditions_tests/protection_test.rb +0 -18
  160. data/test/config_test.rb +0 -23
  161. data/test/fixtures/accounts.yml +0 -12
  162. data/test/fixtures/animals.yml +0 -7
  163. data/test/fixtures/orders.yml +0 -12
  164. data/test/fixtures/user_groups.yml +0 -5
  165. data/test/fixtures/users.yml +0 -45
  166. data/test/libs/awesome_nested_set.rb +0 -545
  167. data/test/libs/awesome_nested_set/.autotest +0 -13
  168. data/test/libs/awesome_nested_set/compatability.rb +0 -29
  169. data/test/libs/awesome_nested_set/helper.rb +0 -40
  170. data/test/libs/awesome_nested_set/named_scope.rb +0 -140
  171. data/test/libs/rexml_fix.rb +0 -14
  172. data/test/modifier_tests/day_of_month_test.rb +0 -16
  173. data/test/search_tests/base_test.rb +0 -241
  174. data/test/search_tests/conditions_test.rb +0 -21
  175. data/test/search_tests/ordering_test.rb +0 -167
  176. data/test/search_tests/pagination_test.rb +0 -74
  177. data/test/search_tests/protection_test.rb +0 -26
  178. data/test/test_helper.rb +0 -122
@@ -1,244 +0,0 @@
1
- module Searchlogic
2
- module Conditions # :nodoc:
3
- # = Conditions
4
- #
5
- # Represents a collection of conditions and performs various tasks on that collection. For information on each condition see Searchlogic::Condition.
6
- # Each condition has its own file and class and the source for each condition is pretty self explanatory.
7
- class Base
8
- include Shared::Utilities
9
- include Shared::VirtualClasses
10
-
11
- attr_accessor :object_name
12
-
13
- class << self
14
- # Registers a condition as an available condition for a column or a class. MySQL supports a "sounds like" function. I want to use it, so let's add it.
15
- #
16
- # === Example
17
- #
18
- # # config/initializers/searchlogic.rb
19
- # # Actual function for MySQL databases only
20
- # class SoundsLike < Searchlogic::Condition::Base
21
- # # The name of the conditions. By default its the name of the class, if you want alternate or alias conditions just add them on.
22
- # # If you don't want to add aliases you don't even need to define this method
23
- # def self.condition_names_for_column
24
- # super + ["similar_to", "sounds"]
25
- # end
26
- #
27
- # # You can return an array or a string. NOT a hash, because all of these conditions
28
- # # need to eventually get merged together. The array or string can be anything you would put in
29
- # # the :conditions option for ActiveRecord::Base.find(). Also notice the column_sql variable. This is essentail
30
- # # for applying modifiers and should be used in your conditions wherever you want the column.
31
- # def to_conditions(value)
32
- # ["#{column_sql} SOUNDS LIKE ?", value]
33
- # end
34
- # end
35
- #
36
- # Searchlogic::Conditions::Base.register_condition(SoundsLike)
37
- def register_condition(condition_class)
38
- raise(ArgumentError, "You can only register conditions that extend Searchlogic::Condition::Base") unless condition_class.ancestors.include?(Searchlogic::Condition::Base)
39
- conditions << condition_class unless conditions.include?(condition_class)
40
- end
41
-
42
- # A list of available condition type classes
43
- def conditions
44
- @@conditions ||= []
45
- end
46
-
47
- # Registers a modifier as an available modifier for each column.
48
- #
49
- # === Example
50
- #
51
- # # config/initializers/searchlogic.rb
52
- # class Ceil < Searchlogic::Modifiers::Base
53
- # # The name of the modifier. By default its the name of the class, if you want alternate or alias modifiers just add them on.
54
- # # If you don't want to add aliases you don't even need to define this method
55
- # def self.modifier_names
56
- # super + ["round_up"]
57
- # end
58
- #
59
- # # The name of the method in the connection adapters (see below). By default its the name of your class suffixed with "_sql".
60
- # # So in this example it would be "ceil_sql". Unless you want to change that you don't need to define this method.
61
- # def self.adapter_method_name
62
- # super
63
- # end
64
- #
65
- # # This is the type of value returned from the modifier. This is neccessary for typcasting values for the modifier when
66
- # # applied to a column
67
- # def self.return_type
68
- # :integer
69
- # end
70
- # end
71
- #
72
- # Searchlogic::Seearch::Conditions.register_modifiers(Ceil)
73
- #
74
- # Now here's the fun part, applying this modifier to each connection adapter. Some databases call modifiers differently. If they all apply them the same you can
75
- # add in the function to ActiveRecord::ConnectionAdapters::AbstractAdapter, otherwise you need to add them to each
76
- # individually: ActiveRecord::ConnectionAdapters::MysqlAdapter, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter, ActiveRecord::ConnectionAdapters::SQLiteAdapter
77
- #
78
- # Do this by includine a model with your method. The name of your method, by default, is: #{modifier_name}_sql. So in the example above it would be "ceil_sql"
79
- #
80
- # module CeilAdapterMethod
81
- # def ceil_sql(column_name)
82
- # "CEIL(#{column_name})"
83
- # end
84
- # end
85
- #
86
- # ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, CeilAdapterMethod)
87
- # # ... include for the rest of the adapters
88
- def register_modifier(modifier_class)
89
- raise(ArgumentError, "You can only register conditions that extend Searchlogic::Modifiers::Base") unless modifier_class.ancestors.include?(Searchlogic::Modifiers::Base)
90
- modifiers << modifier_class unless modifiers.include?(modifier_class)
91
- end
92
-
93
- # A list of available modifier classes
94
- def modifiers
95
- @@modifiers ||= []
96
- end
97
-
98
- def needed?(model_class, conditions) # :nodoc:
99
- return false if conditions.blank?
100
-
101
- if conditions.is_a?(Hash)
102
- return true if conditions[:any]
103
- stringified_conditions = conditions.stringify_keys
104
- stringified_conditions.keys.each { |condition| return false if condition.include?(".") } # setting conditions on associations, which is just another way of writing SQL, and we ignore SQL
105
-
106
- column_names = model_class.column_names
107
- stringified_conditions.keys.each do |condition|
108
- return true unless column_names.include?(condition)
109
- end
110
- end
111
-
112
- false
113
- end
114
- end
115
-
116
- # Initializes a conditions object, accepts a hash of conditions as the single parameter
117
- def initialize(init_conditions = {})
118
- self.conditions = init_conditions
119
- end
120
-
121
- # A list of joins to use when searching, includes relationships
122
- def auto_joins
123
- j = []
124
- association_objects.each do |association|
125
- next if association.conditions.blank?
126
- association_joins = association.auto_joins
127
- j << (association_joins.blank? ? association.object_name : {association.object_name => association_joins})
128
- end
129
- j.blank? ? nil : (j.size == 1 ? j.first : j)
130
- end
131
-
132
- # Provides a much more informative and easier to understand inspection of the object
133
- def inspect
134
- "#<#{klass}Conditions#{conditions.blank? ? "" : " #{conditions.inspect}"}>"
135
- end
136
-
137
- # Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
138
- def sanitize
139
- return @conditions if @conditions # return the conditions if the user set them with a string, aka sql conditions
140
- joined_conditions = nil
141
- objects.each do |object|
142
- sanitized_conditions = group?(object) ? scope_condition(object.sanitize) : object.sanitize
143
- joined_conditions = merge_conditions(joined_conditions, sanitized_conditions, :any => join_object_with_any?(object))
144
- end
145
- joined_conditions
146
- end
147
-
148
- # Allows you to set the conditions via a hash.
149
- def conditions=(value)
150
- case value
151
- when Array
152
- value.each { |v| self.conditions = v }
153
- when Hash
154
- remove_conditions_from_protected_assignement(value).each do |condition, condition_value|
155
- next if [:conditions].include?(condition.to_sym) # protect sensitive methods
156
-
157
- # delete all blanks from mass assignments, forms submit blanks, blanks are meaningless
158
- # equals condition thinks everything is meaningful, and arrays can be pased
159
- new_condition_value = nil
160
- case condition_value
161
- when Array
162
- new_condition_value = condition_value.reject { |v| v == "" }
163
- next if new_condition_value.empty?
164
- new_condition_value = new_condition_value.first if new_condition_value.size == 1
165
- else
166
- next if condition_value == ""
167
- new_condition_value = condition_value
168
- end
169
-
170
- send("#{condition}=", new_condition_value)
171
- end
172
- else
173
- reset!
174
- @conditions = value
175
- end
176
- end
177
-
178
- # All of the active conditions (conditions that have been set)
179
- def conditions
180
- return @conditions if @conditions
181
-
182
- conditions_hash = {}
183
-
184
- association_objects.each do |association_object|
185
- relationship_conditions = association_object.conditions
186
- next if relationship_conditions.blank?
187
- conditions_hash[association_object.object_name] = relationship_conditions
188
- end
189
-
190
- condition_objects.each do |condition_object|
191
- next if condition_object.value_is_meaningless?
192
- conditions_hash[condition_object.object_name] = condition_object.value
193
- end
194
-
195
- conditions_hash
196
- end
197
-
198
- # Resets all of the conditions, including conditions set on associations
199
- def reset!
200
- objects.each { |object| eval("@#{object.object_name} = nil") }
201
- objects.clear
202
- end
203
-
204
- private
205
- def association_objects
206
- objects.select { |object| association?(object) }
207
- end
208
-
209
- def association?(object)
210
- object.class < Base && object.class != self.class
211
- end
212
-
213
- def condition_objects
214
- objects.select { |object| condition?(object) }
215
- end
216
-
217
- def condition?(object)
218
- object.class < Condition::Base
219
- end
220
-
221
- def objects
222
- @objects ||= []
223
- end
224
-
225
- def join_object_with_any?(object)
226
- return any? if !any.nil?
227
- if condition?(object) || group?(object)
228
- object.explicit_any?
229
- elsif association?(object)
230
- object.send(:join_object_with_any?, object.send(:objects).first)
231
- end
232
- end
233
-
234
- def remove_conditions_from_protected_assignement(conditions)
235
- return conditions if klass.accessible_conditions.nil? && klass.protected_conditions.nil?
236
- if klass.accessible_conditions
237
- conditions.reject { |condition, value| !klass.accessible_conditions.include?(condition.to_s) }
238
- elsif klass.protected_conditions
239
- conditions.reject { |condition, value| klass.protected_conditions.include?(condition.to_s) }
240
- end
241
- end
242
- end
243
- end
244
- end
@@ -1,74 +0,0 @@
1
- module Searchlogic
2
- module Conditions
3
- # = Groups
4
- #
5
- # Allows you to group conditions, similar to how you would group conditions with parenthesis in an SQL statement. See the "Group conditions" section in the READM for examples.
6
- module Groups
7
- def self.included(klass)
8
- klass.class_eval do
9
- alias_method_chain :auto_joins, :groups
10
- end
11
- end
12
-
13
- def auto_joins_with_groups
14
- auto_joins = auto_joins_without_groups
15
- auto_joins = auto_joins.is_a?(Array) ? auto_joins : [auto_joins].compact
16
-
17
- group_objects.each do |group|
18
- next if group.conditions.blank?
19
- group_joins = group.auto_joins
20
- next if group_joins.blank?
21
- group_joins = group_joins.is_a?(Array) ? group_joins : [group_joins]
22
- auto_joins += group_joins
23
- end
24
-
25
- auto_joins.blank? ? nil : (auto_joins.size == 1 ? auto_joins.first : auto_joins)
26
- end
27
-
28
- # Creates a new group object to set condition off of. See examples at top of class on how to use this.
29
- def group(conditions = nil, &block)
30
- obj = self.class.new
31
- obj.conditions = conditions unless conditions.nil?
32
- yield obj if block_given?
33
- objects << obj
34
- obj
35
- end
36
- alias_method :group=, :group
37
-
38
- def and_group(*args, &block)
39
- obj = group(*args, &block)
40
- obj.explicit_any = false
41
- obj
42
- end
43
- alias_method :and_group=, :and_group
44
-
45
- def or_group(*args, &block)
46
- obj = group(*args, &block)
47
- obj.explicit_any = true
48
- obj
49
- end
50
- alias_method :or_group=, :or_group
51
-
52
- def explicit_any=(value) # :nodoc:
53
- @explicit_any = value
54
- end
55
-
56
- def explicit_any # :nodoc
57
- @explicit_any
58
- end
59
-
60
- def explicit_any? # :nodoc:
61
- ["true", "1", "yes"].include? explicit_any.to_s
62
- end
63
-
64
- private
65
- def group_objects
66
- objects.select { |object| group?(object) }
67
- end
68
-
69
- def group?(object)
70
- object.class == self.class
71
- end
72
- end
73
- end
74
- end
@@ -1,286 +0,0 @@
1
- module Searchlogic
2
- module Conditions
3
- # = Magic Methods
4
- #
5
- # Handles all method magic, creating methods on the fly, etc. This is needed for modifiers.
6
- module MagicMethods
7
- def self.included(klass)
8
- klass.metaclass.class_eval do
9
- include ClassMethods
10
- attr_accessor :added_class_level_conditions, :added_column_equals_conditions, :added_associations
11
- end
12
-
13
- klass.class_eval do
14
- include InstanceMethods
15
- alias_method_chain :initialize, :magic_methods
16
- alias_method_chain :method_missing, :magic_methods
17
- end
18
- end
19
-
20
- module ClassMethods # :nodoc:
21
- def column_details
22
- return @column_details if @column_details
23
-
24
- @column_details = []
25
-
26
- klass.columns.each do |column|
27
- column_detail = {:column => column}
28
- column_detail[:aliases] = case column.type
29
- when :datetime, :time, :timestamp
30
- [column.name.gsub(/_at$/, "")]
31
- when :date
32
- [column.name.gsub(/_at$/, "")]
33
- else
34
- []
35
- end
36
-
37
- @column_details << column_detail
38
- end
39
-
40
- @column_details
41
- end
42
- end
43
-
44
- module InstanceMethods # :nodoc:
45
- def initialize_with_magic_methods(*args)
46
- add_associations!
47
- add_column_equals_conditions!
48
- add_class_level_conditions!
49
- initialize_without_magic_methods(*args)
50
- end
51
-
52
- private
53
- def add_associations!
54
- return true if self.class.added_associations
55
-
56
- klass.reflect_on_all_associations.each do |association|
57
- next if !association.options[:finder_sql].nil? # associations with finder_sql should not be added since conditions can not be chained to them, etc.
58
- self.class.class_eval <<-"end_eval", __FILE__, __LINE__
59
- def #{association.name}
60
- return @#{association.name} unless @#{association.name}.nil?
61
- @#{association.name} = Searchlogic::Conditions::Base.create_virtual_class(#{association.class_name}).new
62
- @#{association.name}.object_name = :#{association.name}
63
- @#{association.name}.protect = protect
64
- objects << @#{association.name}
65
- @#{association.name}
66
- end
67
-
68
- def #{association.name}=(conditions)
69
- @conditions = nil
70
- #{association.name}.conditions = conditions
71
- end
72
-
73
- def reset_#{association.name}!
74
- objects.delete(#{association.name})
75
- @#{association.name} = nil
76
- end
77
- end_eval
78
- end
79
-
80
- self.class.added_associations = true
81
- end
82
-
83
- def add_column_equals_conditions!
84
- return true if self.class.added_column_equals_conditions
85
- klass.column_names.each { |name| setup_condition(name) }
86
- self.class.added_column_equals_conditions = true
87
- end
88
-
89
- def add_class_level_conditions!
90
- return true if self.class.added_class_level_conditions
91
- class_level_conditions = self.class.conditions.select { |condition_class| !condition_class.condition_names_for_model.blank? }
92
- class_level_conditions.each do |condition_class|
93
- condition_class.condition_names_for_model.each_with_index do |condition_name, index|
94
- if index == 0
95
- add_condition!(condition_class, condition_name, :column => klass.columns_hash[klass.primary_key])
96
- else
97
- add_condition_alias!(condition_name, condition_class.condition_names_for_model.first)
98
- end
99
- end
100
- end
101
- self.class.added_class_level_conditions = true
102
- end
103
-
104
- def sanitize_method_name(name)
105
- name.gsub("=", "").gsub(/^(and|or)_/, "")
106
- end
107
-
108
- def extract_column_and_condition_from_method_name(name)
109
- name_parts = sanitize_method_name(name).split("_")
110
-
111
- condition_parts = []
112
- column = nil
113
- while column.nil? && name_parts.size > 0
114
- possible_column_name = name_parts.join("_")
115
-
116
- self.class.column_details.each do |column_detail|
117
- if column_detail[:column].name == possible_column_name || column_detail[:aliases].include?(possible_column_name)
118
- column = column_detail
119
- break
120
- end
121
- end
122
-
123
- condition_parts << name_parts.pop if !column
124
- end
125
-
126
- return if column.nil?
127
-
128
- condition_name = condition_parts.reverse.join("_")
129
- condition = nil
130
-
131
- # Find the real condition
132
- self.class.conditions.each do |condition_klass|
133
- if condition_klass.condition_names_for_column.include?(condition_name)
134
- condition = condition_klass
135
- break
136
- end
137
- end
138
-
139
- [column, condition]
140
- end
141
-
142
- def breakdown_method_name(name)
143
- column_detail, condition_klass = extract_column_and_condition_from_method_name(name)
144
- if !column_detail.nil? && !condition_klass.nil?
145
- # There were no modifiers
146
- return [[], column_detail, condition_klass]
147
- else
148
- # There might be modifiers
149
- name_parts = name.split("_of_")
150
- column_detail, condition_klass = extract_column_and_condition_from_method_name(name_parts.pop)
151
- if !column_detail.nil? && !condition_klass.nil?
152
- # There were modifiers, lets get their real names
153
- modifier_klasses = []
154
- name_parts.each do |modifier_name|
155
- size_before = modifier_klasses.size
156
- self.class.modifiers.each do |modifier_klass|
157
- if modifier_klass.modifier_names.include?(modifier_name)
158
- modifier_klasses << modifier_klass
159
- break
160
- end
161
- end
162
- return if modifier_klasses.size == size_before # there was an invalid modifer, return nil for everything and let it act as a nomethoderror
163
- end
164
-
165
- return [modifier_klasses, column_detail, condition_klass]
166
- end
167
- end
168
-
169
- nil
170
- end
171
-
172
- def build_method_name(modifier_klasses, column_name, condition_name)
173
- modifier_name_parts = []
174
- modifier_klasses.each { |modifier_klass| modifier_name_parts << modifier_klass.modifier_names.first }
175
- method_name_parts = []
176
- method_name_parts << modifier_name_parts.join("_of_") + "_of" unless modifier_name_parts.blank?
177
- method_name_parts << column_name
178
- method_name_parts << condition_name unless condition_name.blank?
179
- method_name_parts.join("_").underscore
180
- end
181
-
182
- def method_missing_with_magic_methods(name, *args, &block)
183
- if setup_condition(name)
184
- send(name, *args, &block)
185
- else
186
- method_missing_without_magic_methods(name, *args, &block)
187
- end
188
- end
189
-
190
- def setup_condition(name)
191
- modifier_klasses, column_detail, condition_klass = breakdown_method_name(name.to_s)
192
- if !column_detail.nil? && !condition_klass.nil?
193
- method_name = build_method_name(modifier_klasses, column_detail[:column].name, condition_klass.condition_names_for_column.first)
194
-
195
- if !added_condition?(method_name)
196
- column_type = column_sql = nil
197
- if !modifier_klasses.blank?
198
- # Find the column type
199
- column_type = modifier_klasses.first.return_type
200
-
201
- # Build the column sql
202
- column_sql = "{table}.{column}"
203
- modifier_klasses.each do |modifier_klass|
204
- next unless klass.connection.respond_to?(modifier_klass.adapter_method_name)
205
- column_sql = klass.connection.send(modifier_klass.adapter_method_name, column_sql)
206
- end
207
- end
208
-
209
- add_condition!(condition_klass, method_name, :column => column_detail[:column], :column_type => column_type, :column_sql_format => column_sql)
210
-
211
- ([column_detail[:column].name] + column_detail[:aliases]).each do |column_name|
212
- condition_klass.condition_names_for_column.each do |condition_name|
213
- alias_method_name = build_method_name(modifier_klasses, column_name, condition_name)
214
- add_condition_alias!(alias_method_name, method_name) unless added_condition?(alias_method_name)
215
- end
216
- end
217
- end
218
-
219
- alias_method_name = sanitize_method_name(name.to_s)
220
- add_condition_alias!(alias_method_name, method_name) unless added_condition?(alias_method_name)
221
-
222
- return true
223
- end
224
-
225
- false
226
- end
227
-
228
- def add_condition!(condition, name, options = {})
229
- options[:column] = options[:column].name if options[:column]
230
-
231
- self.class.class_eval <<-"end_eval", __FILE__, __LINE__
232
- def #{name}_object
233
- return @#{name} unless @#{name}.nil?
234
- @#{name} = #{condition.name}.new(klass, #{options.inspect})
235
- @#{name}.object_name = :#{name}
236
- objects << @#{name}
237
- @#{name}
238
- end
239
-
240
- def #{name}
241
- #{name}_object.value
242
- end
243
- alias_method :and_#{name}, :#{name}
244
- alias_method :or_#{name}, :#{name}
245
-
246
- def #{name}=(value)
247
- @conditions = nil
248
- #{name}_object.value = value
249
- value
250
- end
251
-
252
- def and_#{name}=(value)
253
- #{name}_object.explicit_any = false
254
- self.#{name} = value
255
- end
256
-
257
- def or_#{name}=(value)
258
- #{name}_object.explicit_any = true
259
- self.#{name} = value
260
- end
261
-
262
- def reset_#{name}!
263
- objects.delete(#{name}_object)
264
- @#{name} = nil
265
- end
266
- end_eval
267
- end
268
-
269
- def added_condition?(name)
270
- respond_to?("#{name}_object")
271
- end
272
-
273
- def add_condition_alias!(alias_name, name)
274
- self.class.class_eval do
275
- alias_method "#{alias_name}_object", "#{name}_object"
276
- alias_method alias_name, name
277
- alias_method "#{alias_name}=", "#{name}="
278
- alias_method "and_#{alias_name}=", "and_#{name}="
279
- alias_method "or_#{alias_name}=", "or_#{name}="
280
- alias_method "reset_#{alias_name}!", "reset_#{name}!"
281
- end
282
- end
283
- end
284
- end
285
- end
286
- end