ghazel-ar-extensions 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/ChangeLog +145 -0
  2. data/README +169 -0
  3. data/Rakefile +61 -0
  4. data/config/database.yml +7 -0
  5. data/config/database.yml.template +7 -0
  6. data/config/mysql.schema +72 -0
  7. data/config/postgresql.schema +39 -0
  8. data/db/migrate/generic_schema.rb +97 -0
  9. data/db/migrate/mysql_schema.rb +32 -0
  10. data/db/migrate/oracle_schema.rb +5 -0
  11. data/db/migrate/version.rb +4 -0
  12. data/init.rb +31 -0
  13. data/lib/ar-extensions/adapters/abstract_adapter.rb +146 -0
  14. data/lib/ar-extensions/adapters/mysql.rb +10 -0
  15. data/lib/ar-extensions/adapters/oracle.rb +14 -0
  16. data/lib/ar-extensions/adapters/postgresql.rb +9 -0
  17. data/lib/ar-extensions/adapters/sqlite.rb +7 -0
  18. data/lib/ar-extensions/create_and_update/mysql.rb +7 -0
  19. data/lib/ar-extensions/create_and_update.rb +509 -0
  20. data/lib/ar-extensions/csv.rb +309 -0
  21. data/lib/ar-extensions/delete/mysql.rb +3 -0
  22. data/lib/ar-extensions/delete.rb +143 -0
  23. data/lib/ar-extensions/extensions.rb +513 -0
  24. data/lib/ar-extensions/finder_options/mysql.rb +6 -0
  25. data/lib/ar-extensions/finder_options.rb +275 -0
  26. data/lib/ar-extensions/finders.rb +94 -0
  27. data/lib/ar-extensions/foreign_keys.rb +70 -0
  28. data/lib/ar-extensions/fulltext/mysql.rb +44 -0
  29. data/lib/ar-extensions/fulltext.rb +62 -0
  30. data/lib/ar-extensions/import/mysql.rb +50 -0
  31. data/lib/ar-extensions/import/postgresql.rb +0 -0
  32. data/lib/ar-extensions/import/sqlite.rb +22 -0
  33. data/lib/ar-extensions/import.rb +348 -0
  34. data/lib/ar-extensions/insert_select/mysql.rb +7 -0
  35. data/lib/ar-extensions/insert_select.rb +178 -0
  36. data/lib/ar-extensions/synchronize.rb +30 -0
  37. data/lib/ar-extensions/temporary_table/mysql.rb +3 -0
  38. data/lib/ar-extensions/temporary_table.rb +131 -0
  39. data/lib/ar-extensions/union/mysql.rb +6 -0
  40. data/lib/ar-extensions/union.rb +204 -0
  41. data/lib/ar-extensions/util/sql_generation.rb +27 -0
  42. data/lib/ar-extensions/util/support_methods.rb +32 -0
  43. data/lib/ar-extensions/version.rb +9 -0
  44. data/lib/ar-extensions.rb +5 -0
  45. metadata +110 -0
@@ -0,0 +1,513 @@
1
+ require 'forwardable'
2
+
3
+ # ActiveRecord::Extensions provides additional functionality to the ActiveRecord
4
+ # ORM library created by DHH for Rails.
5
+ #
6
+ # It's main features include:
7
+ # * better finder support using a :conditions Hash for ActiveRecord::Base#find
8
+ # * better finder support using any object that responds to the to_sql method
9
+ # * mass data import functionality
10
+ # * a more modular design to extending ActiveRecord
11
+ #
12
+ #
13
+ # == Using Better Finder Hash Support
14
+ # Here are a few examples, please refer to the class documentation for each
15
+ # extensions:
16
+ #
17
+ # class Post < ActiveRecord::Base ; end
18
+ #
19
+ # Post.find( :all, :conditions=>{
20
+ # :title => "Title", # title='Title'
21
+ # :author_contains => "Zach", # author like '%Zach%'
22
+ # :author_starts_with => "Zach", # author like 'Zach%'
23
+ # :author_ends_with => "Dennis", # author like '%Zach'
24
+ # :published_at => (Date.now-30 .. Date.now), # published_at BETWEEN xxx AND xxx
25
+ # :rating => [ 4, 5, 6 ], # rating IN ( 4, 5, 6 )
26
+ # :rating_not_in => [ 7, 8, 9 ] # rating NOT IN( 4, 5, 6 )
27
+ # :rating_ne => 4, # rating != 4
28
+ # :rating_gt => 4, # rating > 4
29
+ # :rating_lt => 4, # rating < 4
30
+ # :content => /(a|b|c)/ # REGEXP '(a|b|c)'
31
+ # )
32
+ #
33
+ #
34
+ # == Create Your Own Finder Extension Example
35
+ # The following example shows you how-to create a robust and reliable
36
+ # finder extension which allows you to use Ranges in your :conditions Hash. This
37
+ # is the actual implementation in ActiveRecord::Extensions.
38
+ #
39
+ # class RangeExt
40
+ # NOT_IN_RGX = /^(.+)_(ne|not|not_in|not_between)$/
41
+ #
42
+ # def self.process( key, val, caller )
43
+ # return nil unless val.is_a?( Range )
44
+ # match_data = key.to_s.match( NOT_IN_RGX )
45
+ # key = match_data.captures[0] if match_data
46
+ # fieldname = caller.connection.quote_column_name( key )
47
+ # min = caller.connection.quote( val.first, caller.columns_hash[ key ] )
48
+ # max = caller.connection.quote( val.last, caller.columns_hash[ key ] )
49
+ # str = "#{caller.quoted_table_name}.#{fieldname} #{match_data ? 'NOT ' : '' } BETWEEN #{min} AND #{max}"
50
+ # Result.new( str, nil )
51
+ # end
52
+ #
53
+ #
54
+ # == Using to_sql Ducks In Your Find Methods!
55
+ # The below example shows you how-to utilize objects that respond_to the method +to_sql+ in
56
+ # your finds:
57
+ #
58
+ # class InsuranceClaim < ActiveRecord::Base ; end
59
+ #
60
+ # class InsuranceClaimAgeAndTypeQuery
61
+ # def to_sql
62
+ # "age_in_days BETWEEN 1 AND 60 AND claim_type IN( 'typea', 'typeb' )"
63
+ # end
64
+ # end
65
+ #
66
+ # claims = InsuranceClaim.find( :all, InsuranceClaimAgeAndTypeQuery.new )
67
+ #
68
+ # claims = InsuranceClaim.find( :all, :conditions=>{
69
+ # :claim_amount_gt => 30000,
70
+ # :age_and_type => InsuranceClaimAgeAndTypeQuery.new }
71
+ # )
72
+ #
73
+ # == Importing Lots of Data
74
+ #
75
+ # ActiveRecord executes a single INSERT statement for every call to 'create'
76
+ # and for every call to 'save' on a new model object. When you have only
77
+ # a handful of records to create or save this is not a big deal, but when
78
+ # you have hundreds, thousands or hundreds of thousands of records
79
+ # you need to have better performance.
80
+ #
81
+ # Below is an example of how to import the least amount of INSERT statements
82
+ # using mechanisms provided by your database vendor:
83
+ #
84
+ # class Student < ActiveRecord::Base ; end
85
+ #
86
+ # column_names = Student.columns.map{ |column| column.name }
87
+ # value_sets = some_method_to_load_data_from_csv_file( 'students.csv' )
88
+ # options = { :valudate => true }
89
+ #
90
+ # Student.import( column_names, value_sets, options )
91
+ #
92
+ # The +import+ functionality can be used even if there is not specific
93
+ # support for you vendor. This happens when a particular database vendor
94
+ # specific enhancement hasn't been added to ActiveRecord::Extensions.
95
+ # You can still use +import+ though because the +import+ functionality has
96
+ # been created with backwards compatibility. You may still get better
97
+ # performance using +import+, but you will definitely get no worse then
98
+ # ActiveRecord's create or save methods.
99
+ #
100
+ # See ActiveRecord::Base.import for more information and other ways to use
101
+ # this functionality.
102
+ #
103
+ # == Developers
104
+ # * Zach Dennis
105
+ # * Mark Van Holsytn
106
+ #
107
+ # == Homepage
108
+ # * Project Site: http://www.continuousthinking.com/tags/arext
109
+ # * Rubyforge Project: http://rubyforge.org/projects/arext
110
+ # * Anonymous SVN: svn checkout svn://rubyforge.org/var/svn/arext
111
+ #
112
+ module ActiveRecord::Extensions
113
+
114
+ Result = Struct.new( :sql, :value )
115
+
116
+ # ActiveRecored::Extensions::Registry is used to register finder extensions.
117
+ # Extensions are processed in last in first out order, like a stack.
118
+ class Registry # :nodoc:
119
+
120
+ def options( extension )
121
+ extension_arr = @registry.detect{ |arr| arr.first == extension }
122
+ return unless extension_arr
123
+ extension_arr.last
124
+ end
125
+
126
+ def registers?( extension ) # :nodoc:
127
+ @registry.detect{ |arr| arr.first == extension }
128
+ end
129
+
130
+ def register( extension, options ) # :nodoc:
131
+ @registry << [ extension, options ]
132
+ end
133
+
134
+ def initialize # :nodoc:
135
+ @registry = []
136
+ end
137
+
138
+ def process( field, value, caller ) # :nodoc:
139
+ current_adapter = caller.connection.adapter_name.downcase
140
+ @registry.reverse.each do |(extension,options)|
141
+ adapters = options[:adapters]
142
+ adapters.map!{ |e| e.to_s } unless adapters == :all
143
+ next if options[:adapters] != :all and adapters.grep( /#{current_adapter}/ ).empty?
144
+ if result=extension.process( field, value, caller )
145
+ return result
146
+ end
147
+ end
148
+ nil
149
+ end
150
+
151
+ end
152
+
153
+ class << self
154
+ extend Forwardable
155
+
156
+ def register( extension, options ) # :nodoc:
157
+ @registry ||= Registry.new
158
+ @registry.register( extension, options )
159
+ end
160
+
161
+ def_delegator :@registry, :process, :process
162
+ end
163
+
164
+
165
+ # ActiveRecord::Extension to translate an Array of values
166
+ # into the approriate IN( ... ) or NOT IN( ... ) SQL.
167
+ #
168
+ # == Examples
169
+ # Model.find :all, :conditions=>{ :id => [ 1,2,3 ] }
170
+ #
171
+ # # the following three calls are equivalent
172
+ # Model.find :all, :conditions=>{ :id_ne => [ 4,5,6 ] }
173
+ # Model.find :all, :conditions=>{ :id_not => [ 4,5,6 ] }
174
+ # Model.find :all, :conditions=>{ :id_not_in => [ 4,5,6 ] }
175
+ class ArrayExt
176
+
177
+ NOT_EQUAL_RGX = /(.+)_(ne|not|not_in)/
178
+
179
+ def self.process( key, val, caller )
180
+ if val.is_a?( Array )
181
+ match_data = key.to_s.match( NOT_EQUAL_RGX )
182
+ key = match_data.captures[0] if match_data
183
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} " +
184
+ (match_data ? 'NOT ' : '') + "IN( ? )"
185
+ return Result.new( str, val )
186
+ end
187
+ nil
188
+ end
189
+
190
+ end
191
+
192
+
193
+ # ActiveRecord::Extension to translate Hash keys which end in
194
+ # +_lt+, +_lte+, +_gt+, or +_gte+ with the approriate <, <=, >,
195
+ # or >= symbols.
196
+ # * +_lt+ - denotes less than
197
+ # * +_gt+ - denotes greater than
198
+ # * +_lte+ - denotes less than or equal to
199
+ # * +_gte+ - denotes greater than or equal to
200
+ #
201
+ # == Examples
202
+ # Model.find :all, :conditions=>{ 'number_gt'=>100 }
203
+ # Model.find :all, :conditions=>{ 'number_lt'=>100 }
204
+ # Model.find :all, :conditions=>{ 'number_gte'=>100 }
205
+ # Model.find :all, :conditions=>{ 'number_lte'=>100 }
206
+ class Comparison
207
+
208
+ SUFFIX_MAP = { 'eq'=>'=', 'lt'=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>=', 'ne'=>'!=', 'not'=>'!=' }
209
+ ACCEPTABLE_COMPARISONS = [ String, Numeric, Time, DateTime, Date ]
210
+
211
+ def self.process( key, val, caller )
212
+ process_without_suffix( key, val, caller ) || process_with_suffix( key, val, caller )
213
+ end
214
+
215
+ def self.process_without_suffix( key, val, caller )
216
+ return nil unless caller.columns_hash.has_key?( key )
217
+ if val.nil?
218
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} is ?"
219
+ else
220
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )}=?"
221
+ end
222
+ Result.new( str, val )
223
+ end
224
+
225
+ def self.process_with_suffix( key, val, caller )
226
+ return nil unless ACCEPTABLE_COMPARISONS.find{ |klass| val.is_a?(klass) }
227
+ SUFFIX_MAP.each_pair do |k,v|
228
+ match_data = key.to_s.match( /(.+)_#{k}$/ )
229
+ if match_data
230
+ fieldname = match_data.captures[0]
231
+ return nil unless caller.columns_hash.has_key?( fieldname )
232
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( fieldname )} " +
233
+ "#{v} #{caller.connection.quote( val, caller.columns_hash[ fieldname ] )} "
234
+ return Result.new( str, nil )
235
+ end
236
+ end
237
+ nil
238
+ end
239
+
240
+ end
241
+
242
+
243
+ # ActiveRecord::Extension to translate Hash keys which end in
244
+ # +_like+ or +_contains+ with the approriate LIKE keyword
245
+ # used in SQL.
246
+ #
247
+ # == Examples
248
+ # # the below two examples are equivalent
249
+ # Model.find :all, :conditions=>{ :name_like => 'John' }
250
+ # Model.find :all, :conditions=>{ :name_contains => 'John' }
251
+ #
252
+ # Model.find :all, :conditions=>{ :name_starts_with => 'J' }
253
+ # Model.find :all, :conditions=>{ :name_ends_with => 'n' }
254
+ class Like
255
+ LIKE_RGX = /(.+)_(like|contains)$/
256
+ STARTS_WITH_RGX = /(.+)_starts_with$/
257
+ ENDS_WITH_RGX = /(.+)_ends_with$/
258
+ def self.process( key, val, caller )
259
+ values = case val
260
+ when NilClass
261
+ []
262
+ when Array
263
+ val
264
+ else
265
+ [val]
266
+ end
267
+ case key.to_s
268
+ when LIKE_RGX
269
+ str = values.collect do |v|
270
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE " +
271
+ "#{caller.connection.quote( '%%' + v + '%%', caller.columns_hash[ $1 ] )} "
272
+ end
273
+ when STARTS_WITH_RGX
274
+ str = values.collect do |v|
275
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE " +
276
+ "#{caller.connection.quote( v + '%%', caller.columns_hash[ $1 ] )} "
277
+ end
278
+ when ENDS_WITH_RGX
279
+ str = values.collect do |v|
280
+ "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( $1 )} LIKE " +
281
+ "#{caller.connection.quote( '%%' + v, caller.columns_hash[ $1 ] )} "
282
+ end
283
+ else
284
+ return nil
285
+ end
286
+
287
+ str = str.join(' OR ')
288
+ result_values = []
289
+ str.gsub!(/'((%%)?([^\?]*\?[^%]*|[^%]*%[^%]*)(%%)?)'/) do |match|
290
+ result_values << $2
291
+ '?'
292
+ end
293
+ result_values = nil if result_values.empty?
294
+ return Result.new(str , result_values)
295
+ end
296
+ end
297
+
298
+
299
+ # ActiveRecord::Extension to translate a ruby Range object into SQL's BETWEEN ... AND ...
300
+ # or NOT BETWEEN ... AND ... . This works on Ranges of Numbers, Dates, Times, etc.
301
+ #
302
+ # == Examples
303
+ # # the following two statements are identical because of how Ranges treat .. and ...
304
+ # Model.find :all, :conditions=>{ :id => ( 1 .. 2 ) }
305
+ # Model.find :all, :conditions=>{ :id => ( 1 ... 2 ) }
306
+ #
307
+ # # the following four statements are identical, this finds NOT BETWEEN matches
308
+ # Model.find :all, :conditions=>{ :id_ne => ( 4 .. 6 ) }
309
+ # Model.find :all, :conditions=>{ :id_not => ( 4 .. 6 ) }
310
+ # Model.find :all, :conditions=>{ :id_not_in => ( 4 ..6 ) }
311
+ # Model.find :all, :conditions=>{ :id_not_between => ( 4 .. 6 ) }
312
+ #
313
+ # # a little more creative, working with date ranges
314
+ # Model.find :all, :conditions=>{ :created_on => (Date.now-30 .. Date.now) }
315
+ class RangeExt
316
+ NOT_IN_RGX = /^(.+)_(ne|not|not_in|not_between)$/
317
+
318
+ def self.process( key, val, caller )
319
+ if val.is_a?( Range )
320
+ match_data = key.to_s.match( NOT_IN_RGX )
321
+ key = match_data.captures[0] if match_data
322
+ fieldname = caller.connection.quote_column_name( key )
323
+ min = caller.connection.quote( val.first, caller.columns_hash[ key ] )
324
+ max = caller.connection.quote( val.last, caller.columns_hash[ key ] )
325
+ str = if val.exclude_end?
326
+ "#{match_data ? 'NOT ' : '' }(#{caller.quoted_table_name}.#{fieldname} >= #{min} AND #{caller.quoted_table_name}.#{fieldname} < #{max})"
327
+ else
328
+ "#{caller.quoted_table_name}.#{fieldname} #{match_data ? 'NOT ' : '' } BETWEEN #{min} AND #{max}"
329
+ end
330
+
331
+ return Result.new( str, nil )
332
+ end
333
+ nil
334
+ end
335
+
336
+ end
337
+
338
+ # A base class for database vendor specific Regexp implementations. This is meant to be
339
+ # subclassed only because of the helper method(s) it provides.
340
+ class RegexpBase
341
+
342
+ NOT_EQUAL_RGX = /^(.+)_(ne|not|does_not_match)$/
343
+
344
+ # A result class which provides an easy interface.
345
+ class RegexpResult
346
+ attr_reader :fieldname, :negate
347
+
348
+ def initialize( fieldname, negate=false )
349
+ @fieldname, @negate = fieldname, negate
350
+ end
351
+
352
+ def negate?
353
+ negate ? true : false
354
+ end
355
+ end
356
+
357
+ # Given the passed in +str+ and +caller+ this will return a RegexpResult object
358
+ # which gives the database quoted fieldname/column and can tell you whether or not
359
+ # the original +str+ is indicating a negated regular expression.
360
+ #
361
+ # == Examples
362
+ # r = RegexpBase.field_result( 'id' )
363
+ # r.fieldname => # 'id'
364
+ # r.negate? => # false
365
+ #
366
+ # r = RegexpBase.field_result( 'id_ne' )
367
+ # r.fieldname => # 'id'
368
+ # r.negate? => # true
369
+ #
370
+ # r = RegexpBase.field_result( 'id_not' )
371
+ # r.fieldname => # 'id'
372
+ # r.negate? => # true
373
+ #
374
+ # r = RegexpBase.field_result( 'id_does_not_match' )
375
+ # r.fieldname => # 'id'
376
+ # r.negate? => # true
377
+ def self.field_result( str, caller )
378
+ negate = false
379
+ if match_data=str.to_s.match( NOT_EQUAL_RGX )
380
+ negate = true
381
+ str = match_data.captures[0]
382
+ end
383
+ fieldname = caller.connection.quote_column_name( str )
384
+ RegexpResult.new( fieldname, negate )
385
+ end
386
+
387
+ end
388
+
389
+
390
+ # ActiveRecord::Extension for implementing Regexp implementation for MySQL.
391
+ # See documention for RegexpBase.
392
+ class MySQLRegexp < RegexpBase
393
+
394
+ def self.process( key, val, caller )
395
+ return nil unless val.is_a?( Regexp )
396
+ r = field_result( key, caller )
397
+ Result.new( "#{caller.quoted_table_name}.#{r.fieldname} #{r.negate? ? 'NOT ':''} REGEXP ?", val )
398
+ end
399
+
400
+ end
401
+
402
+
403
+ # ActiveRecord::Extension for implementing Regexp implementation for PostgreSQL.
404
+ # See documention for RegexpBase.
405
+ #
406
+ # Note: this doesn't support case insensitive matches.
407
+ class PostgreSQLRegexp < RegexpBase
408
+
409
+ def self.process( key, val, caller )
410
+ return nil unless val.is_a?( Regexp )
411
+ r = field_result( key, caller )
412
+ return Result.new( "#{caller.quoted_table_name}.#{r.fieldname} #{r.negate? ? '!~ ':'~'} ?", val )
413
+ end
414
+
415
+ end
416
+
417
+ # ActiveRecord::Extension for implementing Regexp implementation for Oracle.
418
+ # See documention for RegexpBase.
419
+ #
420
+ class OracleRegexp < RegexpBase
421
+
422
+ def self.process( key, val, caller )
423
+ return nil unless val.is_a?( Regexp )
424
+ r = field_result( key, caller )
425
+ return Result.new( "#{r.negate? ? ' NOT ':''} REGEXP_LIKE(#{caller.quoted_table_name}.#{r.fieldname} , ?)", val )
426
+ end
427
+
428
+ end
429
+
430
+
431
+ # ActiveRecord::Extension for implementing Regexp implementation for MySQL.
432
+ # See documention for RegexpBase.
433
+ class SqliteRegexp < RegexpBase
434
+ class_inheritable_accessor :connections
435
+ self.connections = []
436
+
437
+ def self.add_rlike_function( connection )
438
+ self.connections << connection
439
+ unless connection.respond_to?( 'sqlite_regexp_support?' )
440
+ class << connection
441
+ def sqlite_regexp_support? ; true ; end
442
+ end
443
+ connection.instance_eval( '@connection' ).create_function( 'rlike', 3 ) do |func, a, b, negate|
444
+ if negate =~ /true/
445
+ func.set_result 1 if a.to_s !~ /#{b}/
446
+ else
447
+ func.set_result 1 if a.to_s =~ /#{b}/
448
+ end
449
+ end
450
+ end
451
+ end
452
+
453
+ def self.process( key, val, caller )
454
+ return nil unless val.is_a?( Regexp )
455
+ r = field_result( key, caller )
456
+ unless self.connections.include?( caller.connection )
457
+ add_rlike_function( caller.connection )
458
+ end
459
+ Result.new( "rlike( #{r.fieldname}, ?, '#{r.negate?}' )", val )
460
+ end
461
+
462
+ end
463
+
464
+ class DatetimeSupport
465
+ SUFFIX_MAP = { 'eq'=>'=', 'lt'=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>=', 'ne'=>'!=', 'not'=>'!=' }
466
+
467
+ def self.process( key, val, caller )
468
+ return unless val.is_a?( Time )
469
+ process_without_suffix( key, val, caller ) || process_with_suffix( key, val, caller )
470
+ end
471
+
472
+ def self.process_without_suffix( key, val, caller )
473
+ return nil unless caller.columns_hash.has_key?( key )
474
+ if val.nil?
475
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )} IS NULL"
476
+ else
477
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( key )}=" +
478
+ "#{caller.connection.quote( val.to_s(:db), caller.columns_hash[ key ] )} "
479
+ end
480
+ Result.new( str, nil )
481
+ end
482
+
483
+ def self.process_with_suffix( key, val, caller )
484
+ SUFFIX_MAP.each_pair do |k,v|
485
+ match_data = key.to_s.match( /(.+)_#{k}$/ )
486
+ if match_data
487
+ fieldname = match_data.captures[0]
488
+ return nil unless caller.columns_hash.has_key?( fieldname )
489
+ str = "#{caller.quoted_table_name}.#{caller.connection.quote_column_name( fieldname )} " +
490
+ "#{v} #{caller.connection.quote( val.to_s(:db), caller.columns_hash[ fieldname ] )} "
491
+ return Result.new( str, nil )
492
+ end
493
+ end
494
+ nil
495
+ end
496
+
497
+
498
+ end
499
+
500
+
501
+ register Comparison, :adapters=>:all
502
+ register ArrayExt, :adapters=>:all
503
+ register Like, :adapters=>:all
504
+ register RangeExt, :adapters=>:all
505
+ register MySQLRegexp, :adapters=>[ :mysql ]
506
+ register PostgreSQLRegexp, :adapters=>[ :postgresql ]
507
+ register SqliteRegexp, :adapters =>[ :sqlite ]
508
+ register OracleRegexp, :adapters =>[ :oracle ]
509
+ register DatetimeSupport, :adapters =>[ :mysql, :sqlite, :oracle ]
510
+ end
511
+
512
+
513
+
@@ -0,0 +1,6 @@
1
+ # Although the finder options actually override ActiveRecord::Base functionality instead of
2
+ # connector functionality, the methods are included here to keep the syntax of 0.8.0 intact
3
+ #
4
+ # To include finder options, use:
5
+ # require 'ar-extensions/finder_options/mysql.rb'
6
+ ActiveRecord::Base.send :include, ActiveRecord::Extensions::FinderOptions