ghazel-ar-extensions 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
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