searchgasm 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGELOG.rdoc +8 -0
  2. data/Manifest +42 -3
  3. data/README.rdoc +101 -82
  4. data/TODO.rdoc +1 -3
  5. data/lib/searchgasm/active_record/connection_adapters/mysql_adapter.rb +143 -6
  6. data/lib/searchgasm/active_record/connection_adapters/postgresql_adapter.rb +148 -0
  7. data/lib/searchgasm/active_record/connection_adapters/sqlite_adapter.rb +54 -0
  8. data/lib/searchgasm/condition/base.rb +59 -86
  9. data/lib/searchgasm/condition/begins_with.rb +3 -8
  10. data/lib/searchgasm/condition/blank.rb +5 -5
  11. data/lib/searchgasm/condition/ends_with.rb +3 -8
  12. data/lib/searchgasm/condition/equals.rb +4 -3
  13. data/lib/searchgasm/condition/greater_than.rb +3 -14
  14. data/lib/searchgasm/condition/greater_than_or_equal_to.rb +3 -14
  15. data/lib/searchgasm/condition/keywords.rb +3 -8
  16. data/lib/searchgasm/condition/less_than.rb +3 -14
  17. data/lib/searchgasm/condition/less_than_or_equal_to.rb +3 -14
  18. data/lib/searchgasm/condition/like.rb +15 -0
  19. data/lib/searchgasm/condition/nil.rb +5 -5
  20. data/lib/searchgasm/condition/not_begin_with.rb +17 -0
  21. data/lib/searchgasm/condition/not_end_with.rb +17 -0
  22. data/lib/searchgasm/condition/{does_not_equal.rb → not_equal.rb} +5 -4
  23. data/lib/searchgasm/condition/not_have_keywords.rb +17 -0
  24. data/lib/searchgasm/condition/not_like.rb +17 -0
  25. data/lib/searchgasm/condition/tree.rb +4 -5
  26. data/lib/searchgasm/conditions/base.rb +218 -72
  27. data/lib/searchgasm/modifiers/absolute.rb +15 -0
  28. data/lib/searchgasm/modifiers/acos.rb +11 -0
  29. data/lib/searchgasm/modifiers/asin.rb +11 -0
  30. data/lib/searchgasm/modifiers/atan.rb +11 -0
  31. data/lib/searchgasm/modifiers/base.rb +27 -0
  32. data/lib/searchgasm/modifiers/ceil.rb +15 -0
  33. data/lib/searchgasm/modifiers/char_length.rb +15 -0
  34. data/lib/searchgasm/modifiers/cos.rb +15 -0
  35. data/lib/searchgasm/modifiers/cot.rb +15 -0
  36. data/lib/searchgasm/modifiers/day_of_month.rb +15 -0
  37. data/lib/searchgasm/modifiers/day_of_week.rb +15 -0
  38. data/lib/searchgasm/modifiers/day_of_year.rb +15 -0
  39. data/lib/searchgasm/modifiers/degrees.rb +11 -0
  40. data/lib/searchgasm/modifiers/exp.rb +15 -0
  41. data/lib/searchgasm/modifiers/floor.rb +15 -0
  42. data/lib/searchgasm/modifiers/hex.rb +11 -0
  43. data/lib/searchgasm/modifiers/hour.rb +11 -0
  44. data/lib/searchgasm/modifiers/log.rb +15 -0
  45. data/lib/searchgasm/modifiers/log10.rb +11 -0
  46. data/lib/searchgasm/modifiers/log2.rb +11 -0
  47. data/lib/searchgasm/modifiers/md5.rb +11 -0
  48. data/lib/searchgasm/modifiers/microseconds.rb +11 -0
  49. data/lib/searchgasm/modifiers/milliseconds.rb +11 -0
  50. data/lib/searchgasm/modifiers/minute.rb +15 -0
  51. data/lib/searchgasm/modifiers/month.rb +15 -0
  52. data/lib/searchgasm/modifiers/octal.rb +15 -0
  53. data/lib/searchgasm/modifiers/radians.rb +11 -0
  54. data/lib/searchgasm/modifiers/round.rb +11 -0
  55. data/lib/searchgasm/modifiers/second.rb +15 -0
  56. data/lib/searchgasm/modifiers/sign.rb +11 -0
  57. data/lib/searchgasm/modifiers/sin.rb +11 -0
  58. data/lib/searchgasm/modifiers/square_root.rb +15 -0
  59. data/lib/searchgasm/modifiers/tan.rb +15 -0
  60. data/lib/searchgasm/modifiers/week.rb +11 -0
  61. data/lib/searchgasm/modifiers/year.rb +11 -0
  62. data/lib/searchgasm/shared/utilities.rb +0 -10
  63. data/lib/searchgasm/version.rb +2 -2
  64. data/lib/searchgasm.rb +15 -19
  65. data/searchgasm.gemspec +86 -9
  66. data/test/libs/ordered_hash.rb +9 -0
  67. data/test/test_condition_base.rb +21 -47
  68. data/test/test_condition_types.rb +44 -44
  69. data/test/test_conditions_base.rb +34 -21
  70. data/test/test_helper.rb +1 -0
  71. data/test/test_search_conditions.rb +1 -1
  72. metadata +85 -8
  73. data/lib/searchgasm/condition/contains.rb +0 -20
  74. data/lib/searchgasm/condition/during_evening.rb +0 -32
@@ -13,36 +13,51 @@ module Searchgasm
13
13
  class << self
14
14
  attr_accessor :added_klass_conditions, :added_column_conditions, :added_associations
15
15
 
16
- # Registers a condition as an available condition for a column or a class.
16
+ def column_details # :nodoc:
17
+ return @column_details if @column_details
18
+
19
+ @column_details = []
20
+
21
+ klass.columns.each do |column|
22
+ column_detail = {:column => column}
23
+ column_detail[:aliases] = case column.type
24
+ when :datetime, :time, :timestamp
25
+ [column.name.gsub(/_at$/, "")]
26
+ when :date
27
+ [column.name.gsub(/_at$/, "")]
28
+ else
29
+ []
30
+ end
31
+
32
+ @column_details << column_detail
33
+ end
34
+
35
+ @column_details
36
+ end
37
+
38
+ # 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.
17
39
  #
18
40
  # === Example
19
41
  #
20
- # config/initializers/searchgasm.rb
42
+ # # config/initializers/searchgasm.rb
21
43
  # # Actual function for MySQL databases only
22
44
  # class SoundsLike < Searchgasm::Condition::Base
23
- # class << self
24
- # # I pass you the column, you tell me what you want the method to be called.
25
- # # If you don't want to add this condition for that column, return nil
26
- # # It defaults to "#{column.name}_sounds_like" (using the class name). So if thats what you want you don't even need to do this.
27
- # def name_for_column(column)
28
- # super
29
- # end
30
- #
31
- # # Only do this if you want aliases for your condition
32
- # def aliases_for_column(column)
33
- # ["#{column.name}_sounds", "#{column.name}_similar_to"]
34
- # end
45
+ # # The name of the conditions. By default its the name of the class, if you want alternate or alias conditions just add them on.
46
+ # # If you don't want to add aliases you don't even need to define this method
47
+ # def self.name_for_column(column)
48
+ # super
35
49
  # end
36
50
  #
37
51
  # # You can return an array or a string. NOT a hash, because all of these conditions
38
52
  # # need to eventually get merged together. The array or string can be anything you would put in
39
- # # the :conditions option for ActiveRecord::Base.find(). Also, for a list of methods / variables you can use check out earchgasm::Condition::Base
53
+ # # the :conditions option for ActiveRecord::Base.find(). Also notice the column_sql variable. This is essentail
54
+ # # for applying modifiers and should be used in your conditions wherever you want the column.
40
55
  # def to_conditions(value)
41
- # ["#{quoted_table_name}.#{quoted_column_name} SOUNDS LIKE ?", value]
56
+ # ["#{column_sql} SOUNDS LIKE ?", value]
42
57
  # end
43
58
  # end
44
59
  #
45
- # Searchgasm::Seearch::Conditions.register_condition(SoundsLikeCondition)
60
+ # Searchgasm::Seearch::Conditions.register_condition(SoundsLike)
46
61
  def register_condition(condition_class)
47
62
  raise(ArgumentError, "You can only register conditions that extend Searchgasm::Condition::Base") unless condition_class.ancestors.include?(Searchgasm::Condition::Base)
48
63
  conditions << condition_class unless conditions.include?(condition_class)
@@ -53,6 +68,57 @@ module Searchgasm
53
68
  @@conditions ||= []
54
69
  end
55
70
 
71
+ # Registers a modifier as an available modifier for each column.
72
+ #
73
+ # === Example
74
+ #
75
+ # # config/initializers/searchgasm.rb
76
+ # class Ceil < Searchgasm::Modifiers::Base
77
+ # # The name of the modifier. By default its the name of the class, if you want alternate or alias modifiers just add them on.
78
+ # # If you don't want to add aliases you don't even need to define this method
79
+ # def self.modifier_names
80
+ # super + ["round_up"]
81
+ # end
82
+ #
83
+ # # The name of the method in the connection adapters (see below). By default its the name of your class suffixed with "_sql".
84
+ # # So in this example it would be "ceil_sql". Unless you want to change that you don't need to define this method.
85
+ # def self.adapter_method_name
86
+ # super
87
+ # end
88
+ #
89
+ # # This is the type of value returned from the modifier. This is neccessary for typcasting values for the modifier when
90
+ # # applied to a column
91
+ # def self.return_type
92
+ # :integer
93
+ # end
94
+ # end
95
+ #
96
+ # Searchgasm::Seearch::Conditions.register_modifiers(Ceil)
97
+ #
98
+ # 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
99
+ # add in the function to ActiveRecord::ConnectionAdapters::AbstractAdapter, otherwise you need to add them to each
100
+ # individually: ActiveRecord::ConnectionAdapters::MysqlAdapter, ActiveRecord::ConnectionAdapters::PostgreSQLAdapter, ActiveRecord::ConnectionAdapters::SQLiteAdapter
101
+ #
102
+ # 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"
103
+ #
104
+ # module CeilAdapterMethod
105
+ # def ceil_sql(column_name)
106
+ # "CEIL(#{column_name})"
107
+ # end
108
+ # end
109
+ #
110
+ # ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, CeilAdapterMethod)
111
+ # # ... include for the rest of the adapters
112
+ def register_modifier(modifier_class)
113
+ raise(ArgumentError, "You can only register conditions that extend Searchgasm::Modifiers::Base") unless modifier_class.ancestors.include?(Searchgasm::Modifiers::Base)
114
+ modifiers << modifier_class unless modifiers.include?(modifier_class)
115
+ end
116
+
117
+ # A list of available modifier classes
118
+ def modifiers
119
+ @@modifiers ||= []
120
+ end
121
+
56
122
  # A list of all associations created, used for caching and performance
57
123
  def association_names
58
124
  @association_names ||= []
@@ -82,8 +148,6 @@ module Searchgasm
82
148
  end
83
149
 
84
150
  def initialize(init_conditions = {})
85
- add_klass_conditions!
86
- add_column_conditions!
87
151
  add_associations!
88
152
  self.conditions = init_conditions
89
153
  end
@@ -95,7 +159,7 @@ module Searchgasm
95
159
  # search.conditions.any = true # will join all conditions with "or", you can also set this to "true", "1", or "yes"
96
160
  # search.conditions.any = false # will join all conditions with "and"
97
161
  def any=(value)
98
- associations.each { |association| association.any = value }
162
+ associations.each { |name, association| association.any = value }
99
163
  @any = value
100
164
  end
101
165
 
@@ -111,7 +175,7 @@ module Searchgasm
111
175
  # A list of joins to use when searching, includes relationships
112
176
  def auto_joins
113
177
  j = []
114
- associations.each do |association|
178
+ associations.each do |name, association|
115
179
  next if association.conditions.blank?
116
180
  association_joins = association.auto_joins
117
181
  j << (association_joins.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_joins})
@@ -126,16 +190,15 @@ module Searchgasm
126
190
  # Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
127
191
  def sanitize
128
192
  return @conditions if @conditions
129
- merge_conditions(*(objects.collect { |object| object.sanitize } << {:any => any}))
193
+ merge_conditions(*(objects.collect { |name, object| object.sanitize } << {:any => any}))
130
194
  end
131
195
 
132
196
  # Allows you to set the conditions via a hash.
133
197
  def conditions=(value)
134
198
  case value
135
- when Hash
199
+ when Hash
136
200
  assert_valid_conditions(value)
137
201
  remove_conditions_from_protected_assignement(value).each do |condition, condition_value|
138
- next if meaningless?(condition_value) # ignore blanks on mass assignments
139
202
  send("#{condition}=", condition_value)
140
203
  end
141
204
  else
@@ -150,14 +213,14 @@ module Searchgasm
150
213
  return if objects.blank?
151
214
 
152
215
  conditions_hash = {}
153
- objects.each do |object|
216
+ objects.each do |name, object|
154
217
  if object.class < Searchgasm::Conditions::Base
155
218
  relationship_conditions = object.conditions
156
219
  next if relationship_conditions.blank?
157
220
  conditions_hash[object.relationship_name.to_sym] = relationship_conditions
158
221
  else
159
- next unless object.explicitly_set_value?
160
- conditions_hash[object.name.to_sym] = object.value
222
+ next if object.meaningless_value?
223
+ conditions_hash[name] = object.value
161
224
  end
162
225
  end
163
226
  conditions_hash
@@ -172,62 +235,150 @@ module Searchgasm
172
235
 
173
236
  self.class.class_eval <<-"end_eval", __FILE__, __LINE__
174
237
  def #{association.name}
175
- if @#{association.name}.nil?
176
- @#{association.name} = Searchgasm::Conditions::Base.create_virtual_class(#{association.class_name}).new
177
- @#{association.name}.relationship_name = "#{association.name}"
178
- @#{association.name}.protect = protect
179
- objects << @#{association.name}
238
+ if objects[:#{association.name}].nil?
239
+ objects[:#{association.name}] = Searchgasm::Conditions::Base.create_virtual_class(#{association.class_name}).new
240
+ objects[:#{association.name}].relationship_name = "#{association.name}"
241
+ objects[:#{association.name}].protect = protect
180
242
  end
181
- @#{association.name}
243
+ objects[:#{association.name}]
182
244
  end
183
245
 
184
246
  def #{association.name}=(conditions); @conditions = nil; #{association.name}.conditions = conditions; end
185
- def reset_#{association.name}!; objects.delete(#{association.name}); @#{association.name} = nil; end
247
+ def reset_#{association.name}!; objects.delete(:#{association.name}); end
186
248
  end_eval
187
249
  end
188
250
 
189
251
  self.class.added_associations = true
190
252
  end
191
253
 
192
- def add_column_conditions!
193
- return true if self.class.added_column_conditions
254
+ def extract_column_and_condition_from_method_name(name)
255
+ name_parts = name.gsub("=", "").split("_")
194
256
 
195
- klass.columns.each do |column|
196
- self.class.conditions.each do |condition_klass|
197
- name = condition_klass.name_for_column(column)
198
- next if name.blank?
199
- add_condition!(condition_klass, name, column)
200
- condition_klass.aliases_for_column(column).each { |alias_name| add_condition_alias!(alias_name, name) }
257
+ condition_parts = []
258
+ column = nil
259
+ while column.nil? && name_parts.size > 0
260
+ possible_column_name = name_parts.join("_")
261
+
262
+ self.class.column_details.each do |column_detail|
263
+ if column_detail[:column].name == possible_column_name || column_detail[:aliases].include?(possible_column_name)
264
+ column = column_detail
265
+ break
266
+ end
201
267
  end
268
+
269
+ condition_parts << name_parts.pop if !column
202
270
  end
203
271
 
204
- self.class.added_column_conditions = true
272
+ return if column.nil?
273
+
274
+ condition_name = condition_parts.reverse.join("_")
275
+ condition = nil
276
+
277
+ # Find the real condition
278
+ self.class.conditions.each do |condition_klass|
279
+ if condition_klass.condition_names_for_column.include?(condition_name)
280
+ condition = condition_klass
281
+ break
282
+ end
283
+ end
284
+
285
+ [column, condition]
205
286
  end
206
287
 
207
- def add_condition!(condition, name, column = nil)
288
+ def breakdown_method_name(name)
289
+ column_detail, condition_klass = extract_column_and_condition_from_method_name(name)
290
+ if !column_detail.nil? && !condition_klass.nil?
291
+ # There were no modifiers
292
+ return [[], column_detail, condition_klass]
293
+ else
294
+ # There might be modifiers
295
+ name_parts = name.split("_of_")
296
+ column_detail, condition_klass = extract_column_and_condition_from_method_name(name_parts.pop)
297
+ if !column_detail.nil? && !condition_klass.nil?
298
+ # There were modifiers, lets get their real names
299
+ modifier_klasses = []
300
+ name_parts.each do |modifier_name|
301
+ size_before = modifier_klasses.size
302
+ self.class.modifiers.each do |modifier_klass|
303
+ if modifier_klass.modifier_names.include?(modifier_name)
304
+ modifier_klasses << modifier_klass
305
+ break
306
+ end
307
+ end
308
+ return if modifier_klasses.size == size_before # there was an invalid modifer, return nil for everything and let it act as a nomethoderror
309
+ end
310
+
311
+ return [modifier_klasses, column_detail, condition_klass]
312
+ end
313
+ end
314
+
315
+ nil
316
+ end
317
+
318
+ def build_method_name(modifier_klasses, column_name, condition_name)
319
+ modifier_name_parts = []
320
+ modifier_klasses.each { |modifier_klass| modifier_name_parts << modifier_klass.modifier_names.first }
321
+ method_name_parts = []
322
+ method_name_parts << modifier_name_parts.join("_of_") unless modifier_name_parts.blank?
323
+ method_name_parts << column_name
324
+ method_name_parts << condition_name
325
+ method_name_parts.join("_")
326
+ end
327
+
328
+ def method_missing(name, *args, &block)
329
+ modifier_klasses, column_detail, condition_klass = breakdown_method_name(name.to_s)
330
+ if !column_detail.nil? && !condition_klass.nil?
331
+ method_name = build_method_name(modifier_klasses, column_detail[:column].name, condition_klass.condition_names_for_column.first)
332
+ column_type = column_sql = nil
333
+ if !modifier_klasses.blank?
334
+ # Find the column type
335
+ column_type = modifier_klasses.first.return_type
336
+
337
+ # Build the column sql
338
+ column_sql = "#{klass.connection.quote_table_name(klass.table_name)}.#{klass.connection.quote_column_name(column_detail[:column].name)}"
339
+ modifier_klasses.each do |modifier_klass|
340
+ next unless klass.connection.respond_to?(modifier_klass.adapter_method_name)
341
+ column_sql = klass.connection.send(modifier_klass.adapter_method_name, column_sql)
342
+ end
343
+ end
344
+
345
+ add_condition!(condition_klass, method_name, column_detail[:column], column_type, column_sql)
346
+
347
+ ([column_detail[:column].name] + column_detail[:aliases]).each do |column_name|
348
+ condition_klass.condition_names_for_column.each do |condition_name|
349
+ alias_method_name = build_method_name(modifier_klasses, column_name, condition_name)
350
+ add_condition_alias!(alias_method_name, method_name) unless alias_method_name == method_name
351
+ end
352
+ end
353
+
354
+ send(method_name + (name.to_s =~ /=$/ ? "=" : ""), *args, &block)
355
+ else
356
+ super
357
+ end
358
+ end
359
+
360
+ def add_condition!(condition, name, column = nil, column_type = nil, column_sql = nil)
208
361
  self.class.condition_names << name
209
362
 
210
363
  self.class.class_eval <<-"end_eval", __FILE__, __LINE__
211
364
  def #{name}_object
212
- if @#{name}.nil?
213
- @#{name} = #{condition.name}.new(klass#{column.nil? ? "" : ", \"#{column.name}\""})
214
- objects << @#{name}
365
+ if objects[:#{name}].nil?
366
+ objects[:#{name}] = #{condition.name}.new(klass, #{column.blank? ? "nil" : "klass.columns_hash['#{column.name}']"}, #{column_type.blank? ? "nil" : "\"#{column_type}\""}, #{column_sql.blank? ? "nil" : "\"#{column_sql.gsub('"', '\"')}\""})
215
367
  end
216
- @#{name}
368
+ objects[:#{name}]
217
369
  end
218
370
 
219
371
  def #{name}; #{name}_object.value; end
220
372
 
221
373
  def #{name}=(value)
222
- if meaningless?(value) && #{name}_object.class.ignore_meaningless?
223
- reset_#{name}!
224
- else
225
- @conditions = nil
226
- #{name}_object.value = value
227
- end
374
+ @conditions = nil
375
+
376
+ #{name}_object.value = value
377
+ reset_#{name}! if #{name}_object.meaningless_value?
378
+ value
228
379
  end
229
380
 
230
- def reset_#{name}!; objects.delete(#{name}_object); @#{name} = nil; end
381
+ def reset_#{name}!; objects.delete(:#{name}); end
231
382
  end_eval
232
383
  end
233
384
 
@@ -235,38 +386,33 @@ module Searchgasm
235
386
  self.class.condition_names << alias_name
236
387
 
237
388
  self.class.class_eval do
389
+ alias_method "#{alias_name}_object", "#{name}_object"
238
390
  alias_method alias_name, name
239
391
  alias_method "#{alias_name}=", "#{name}="
392
+ alias_method "reset_#{alias_name}!", "reset_#{name}!"
240
393
  end
241
394
  end
242
395
 
243
- def add_klass_conditions!
244
- return true if self.class.added_klass_conditions
245
-
246
- self.class.conditions.each do |condition|
247
- name = condition.name_for_klass(klass)
248
- next if name.blank?
249
- add_condition!(condition, name)
250
- condition.aliases_for_klass(klass).each { |alias_name| add_condition_alias!(alias_name, name) }
251
- end
252
-
253
- self.class.added_klass_conditions = true
254
- end
255
-
256
396
  def assert_valid_conditions(conditions)
257
- conditions.stringify_keys.fast_assert_valid_keys(self.class.condition_names + self.class.association_names + ["any"])
397
+ conditions.each do |condition, value|
398
+ raise(ArgumentError, "The #{condition} condition is not a valid condition") if !(self.class.condition_names + self.class.association_names + ["any"]).include?(condition.to_s) && respond_to?(condition)
399
+ end
258
400
  end
259
401
 
260
402
  def associations
261
- objects.select { |object| object.class < ::Searchgasm::Conditions::Base }
403
+ associations = {}
404
+ objects.each do |name, object|
405
+ associations[name] = object if object.class < ::Searchgasm::Conditions::Base
406
+ end
407
+ associations
262
408
  end
263
409
 
264
410
  def objects
265
- @objects ||= []
411
+ @objects ||= {}
266
412
  end
267
413
 
268
414
  def reset_objects!
269
- objects.each { |object| object.class < ::Searchgasm::Conditions::Base ? eval("@#{object.relationship_name} = nil") : eval("@#{object.name} = nil") }
415
+ objects.each { |name, object| object.class < ::Searchgasm::Conditions::Base ? eval("@#{object.relationship_name} = nil") : eval("@#{name} = nil") }
270
416
  objects.clear
271
417
  end
272
418
 
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Absolute < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["abs"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Acos < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Asin < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Atan < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Base
4
+ class << self
5
+ # A convenience method for the name of this modifier
6
+ def modifier_name
7
+ name.split("::").last.underscore
8
+ end
9
+
10
+ # The various names for the modifier. The first in the array is the "main" name, the rest are just aliases to the "main" name
11
+ def modifier_names
12
+ [modifier_name]
13
+ end
14
+
15
+ # The method in the connection adapter that creates the SQL for the modifier
16
+ def adapter_method_name
17
+ "#{modifier_name}_sql"
18
+ end
19
+
20
+ # The type of value returned from the SQL. A class the extends this MUST define this method.
21
+ def return_type
22
+ raise "You did not specify a return type for the #{modifier_name} modifier. Please specify if it is an :integer, :string, etc."
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Ceil < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["round_up"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class CharLength < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["length"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Cos < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["cosine"]
7
+ end
8
+
9
+ def return_type
10
+ :float
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Cot < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["cotangent"]
7
+ end
8
+
9
+ def return_type
10
+ :float
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class DayOfMonth < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["dom"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class DayOfWeek < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["dow"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class DayOfYear < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["doy"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Degrees < Base
4
+ class << self
5
+ def return_type
6
+ :float
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Exp < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["exponential"]
7
+ end
8
+
9
+ def return_type
10
+ :float
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Floor < Base
4
+ class << self
5
+ def modifier_names
6
+ super + ["round_down"]
7
+ end
8
+
9
+ def return_type
10
+ :integer
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Searchgasm
2
+ module Modifiers
3
+ class Hex < Base
4
+ class << self
5
+ def return_type
6
+ :string
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end