ar-extensions 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +8 -0
- data/README +9 -0
- data/Rakefile +1 -1
- data/db/migrate/generic_schema.rb +3 -0
- data/db/migrate/mysql_schema.rb +3 -0
- data/db/migrate/version.rb +1 -1
- data/lib/ar-extensions/adapters/abstract_adapter.rb +4 -3
- data/lib/ar-extensions/adapters/oracle.rb +9 -0
- data/lib/ar-extensions/extensions.rb +18 -4
- data/lib/ar-extensions/finders.rb +1 -1
- data/lib/ar-extensions/import.rb +100 -25
- data/lib/ar-extensions/version.rb +1 -1
- metadata +3 -2
data/ChangeLog
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
2007-07-20 zdennis <zdennis@dhcp-22.atomicobject.localnet>
|
2
|
+
|
3
|
+
* Added patch from Michael Flester to fix bug with created_at/updated_at fields to use proper timezone.
|
4
|
+
|
5
|
+
2007-07-18 zdennis <zdennis@dhcp-22.atomicobject.localnet>
|
6
|
+
|
7
|
+
* Added patch for Oracle support from Michael Flester.
|
8
|
+
|
1
9
|
2007-05-05 zdennis <zdennis@elijah.local>
|
2
10
|
|
3
11
|
* Added ActiveRecord::Base::AbstractAdapter#synchronize method which allows a mass re-synchronization of ActiveRecord instances. This is similar to ActiveRecord::Base#reload except that it works on multiple instances at a time rather then one. This sends one query for all instances rather then 1 per instance reloaded.
|
data/README
CHANGED
@@ -9,6 +9,15 @@ Email: zach.dennis@gmail.com
|
|
9
9
|
- For How-To information see http://www.continuousthinking.com/tags/arext
|
10
10
|
- For project information and feedback please consult http://rubyforge.org/projects/arext/
|
11
11
|
- For release information please see below
|
12
|
+
|
13
|
+
AciveRecord::Extensions 0.7.0
|
14
|
+
-----------------------------
|
15
|
+
July 20th, 2007
|
16
|
+
- Fixes timezone issue (thanks Michael Flester)
|
17
|
+
- Adds better finder and import support for Oracle (thanks Michael Flester)
|
18
|
+
- Committed patch to fix MySQL query padding, thanks to Gabe da Silveira
|
19
|
+
- Added functionality for MySQL to work with created_on, created_at, updated_on or updated_at fields
|
20
|
+
- Added more test coverage for import functionality
|
12
21
|
|
13
22
|
ActiveRecord::Extensions 0.6.0
|
14
23
|
------------------------------
|
data/Rakefile
CHANGED
@@ -49,6 +49,9 @@ ActiveRecord::Schema.define do
|
|
49
49
|
t.column :publisher, :string, :null=>false
|
50
50
|
t.column :author_name, :string, :null=>false
|
51
51
|
t.column :created_at, :time
|
52
|
+
t.column :created_on, :datetime
|
53
|
+
t.column :updated_at, :time
|
54
|
+
t.column :updated_on, :datetime
|
52
55
|
t.column :topic_id, :integer
|
53
56
|
end
|
54
57
|
|
data/db/migrate/mysql_schema.rb
CHANGED
@@ -20,6 +20,9 @@ ActiveRecord::Schema.define do
|
|
20
20
|
t.column :publisher, :string, :null=>false
|
21
21
|
t.column :author_name, :string, :null=>false
|
22
22
|
t.column :created_at, :datetime
|
23
|
+
t.column :created_on, :datetime
|
24
|
+
t.column :updated_at, :datetime
|
25
|
+
t.column :updated_on, :datetime
|
23
26
|
t.column :topic_id, :integer
|
24
27
|
end
|
25
28
|
execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )"
|
data/db/migrate/version.rb
CHANGED
@@ -2,7 +2,8 @@ module ActiveRecord # :nodoc:
|
|
2
2
|
module ConnectionAdapters # :nodoc:
|
3
3
|
class AbstractAdapter # :nodoc:
|
4
4
|
NO_MAX_PACKET = 0
|
5
|
-
|
5
|
+
QUERY_OVERHEAD = 8 #This was shown to be true for MySQL, but it's not clear where the overhead is from.
|
6
|
+
|
6
7
|
# +sql+ can be a single string or an array. If it is an array all
|
7
8
|
# elements that are in position >= 1 will be appended to the final SQL.
|
8
9
|
def insert_many( sql, values, *args ) # :nodoc:
|
@@ -15,7 +16,7 @@ module ActiveRecord # :nodoc:
|
|
15
16
|
[ sql.shift, sql.join( ' ' ) ]
|
16
17
|
end
|
17
18
|
|
18
|
-
sql_size = base_sql.size + post_sql.size
|
19
|
+
sql_size = QUERY_OVERHEAD + base_sql.size + post_sql.size
|
19
20
|
|
20
21
|
# the number of bytes the requested insert statement values will take up
|
21
22
|
values_in_bytes = self.class.sum_sizes( *values )
|
@@ -41,7 +42,7 @@ module ActiveRecord # :nodoc:
|
|
41
42
|
insert( sql2insert, *args )
|
42
43
|
end
|
43
44
|
end
|
44
|
-
|
45
|
+
|
45
46
|
number_of_inserts
|
46
47
|
end
|
47
48
|
|
@@ -72,7 +72,7 @@ require 'forwardable'
|
|
72
72
|
#
|
73
73
|
# == Importing Lots of Data
|
74
74
|
#
|
75
|
-
# ActiveRecord executes a single INSERT statement for every
|
75
|
+
# ActiveRecord executes a single INSERT statement for every call to 'create'
|
76
76
|
# and for every call to 'save' on a new model object. When you have only
|
77
77
|
# a handful of records to create or save this is not a big deal, but when
|
78
78
|
# you have hundreds, thousands or hundreds of thousands of records
|
@@ -203,8 +203,8 @@ module ActiveRecord::Extensions
|
|
203
203
|
# Model.find :all, :conditions=>{ 'number_lt'=>100 }
|
204
204
|
# Model.find :all, :conditions=>{ 'number_gte'=>100 }
|
205
205
|
# Model.find :all, :conditions=>{ 'number_lte'=>100 }
|
206
|
-
class Comparison
|
207
|
-
|
206
|
+
class Comparison
|
207
|
+
|
208
208
|
SUFFIX_MAP = { 'eq'=>'=', 'lt'=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>=', 'ne'=>'!=', 'not'=>'!=' }
|
209
209
|
|
210
210
|
def self.process( key, val, caller )
|
@@ -394,6 +394,19 @@ module ActiveRecord::Extensions
|
|
394
394
|
end
|
395
395
|
|
396
396
|
end
|
397
|
+
|
398
|
+
# ActiveRecord::Extension for implementing Regexp implementation for Oracle.
|
399
|
+
# See documention for RegexpBase.
|
400
|
+
#
|
401
|
+
class OracleRegexp < RegexpBase
|
402
|
+
|
403
|
+
def self.process( key, val, caller )
|
404
|
+
return nil unless val.is_a?( Regexp )
|
405
|
+
r = field_result( key, caller )
|
406
|
+
return Result.new( "#{r.negate? ? ' NOT ':''} REGEXP_LIKE(#{caller.table_name}.#{r.fieldname} , ?)", val )
|
407
|
+
end
|
408
|
+
|
409
|
+
end
|
397
410
|
|
398
411
|
|
399
412
|
# ActiveRecord::Extension for implementing Regexp implementation for MySQL.
|
@@ -473,7 +486,8 @@ end
|
|
473
486
|
register MySQLRegexp, :adapters=>[ :mysql ]
|
474
487
|
register PostgreSQLRegexp, :adapters=>[ :postgresql ]
|
475
488
|
register SqliteRegexp, :adapters =>[ :sqlite ]
|
476
|
-
register
|
489
|
+
register OracleRegexp, :adapters =>[ :oracle ]
|
490
|
+
register DatetimeSupport, :adapters =>[ :mysql, :sqlite, :oracle ]
|
477
491
|
end
|
478
492
|
|
479
493
|
|
@@ -24,7 +24,7 @@ class ActiveRecord::Base
|
|
24
24
|
arg = sanitize_sql_by_way_of_duck_typing( arg )
|
25
25
|
elsif arg.is_a?( Hash )
|
26
26
|
arg = sanitize_sql_from_hash( arg )
|
27
|
-
elsif arg.size == 2 and arg.first.is_a?( String ) and arg.last.is_a?( Hash )
|
27
|
+
elsif arg.is_a?( Array ) and arg.size == 2 and arg.first.is_a?( String ) and arg.last.is_a?( Hash )
|
28
28
|
arg = sanitize_sql_from_string_and_hash( arg )
|
29
29
|
end
|
30
30
|
sanitize_sql_orig( arg )
|
data/lib/ar-extensions/import.rb
CHANGED
@@ -18,6 +18,16 @@ end
|
|
18
18
|
|
19
19
|
class ActiveRecord::Base
|
20
20
|
class << self
|
21
|
+
|
22
|
+
# use tz as set in ActiveRecord::Base
|
23
|
+
tproc = @@default_timezone == :utc ? lambda { Time.now.utc } : lambda { Time.now }
|
24
|
+
AREXT_RAILS_COLUMNS = {
|
25
|
+
:create => { "created_on" => tproc ,
|
26
|
+
"created_at" => tproc },
|
27
|
+
:update => { "updated_on" => tproc ,
|
28
|
+
"updated_at" => tproc }
|
29
|
+
}
|
30
|
+
AREXT_RAILS_COLUMN_NAMES = AREXT_RAILS_COLUMNS[:create].keys + AREXT_RAILS_COLUMNS[:update].keys
|
21
31
|
|
22
32
|
# Returns true if the current database connection adapter
|
23
33
|
# supports import functionality, otherwise returns false.
|
@@ -26,7 +36,7 @@ class ActiveRecord::Base
|
|
26
36
|
rescue NoMethodError
|
27
37
|
false
|
28
38
|
end
|
29
|
-
|
39
|
+
|
30
40
|
# Returns true if the current database connection adapter
|
31
41
|
# supports on duplicate key update functionality, otherwise
|
32
42
|
# returns false.
|
@@ -132,7 +142,13 @@ class ActiveRecord::Base
|
|
132
142
|
#
|
133
143
|
# BlogPost.import columns, attributes, :on_duplicate_key_update=>{ :title => :title }
|
134
144
|
#
|
145
|
+
# = Returns
|
146
|
+
# This returns an object which responds to +failed_instances+ and +num_inserts+.
|
147
|
+
# * failed_instances - an array of objects that fails validation and were not committed to the database. An empty array if no validation is performed.
|
148
|
+
# * num_inserts - the number of insert statements it took to import the data
|
135
149
|
def import( *args )
|
150
|
+
@logger = Logger.new(STDOUT)
|
151
|
+
@logger.level = Logger::DEBUG
|
136
152
|
options = { :validate=>true }
|
137
153
|
options.merge!( args.pop ) if args.last.is_a? Hash
|
138
154
|
|
@@ -144,15 +160,18 @@ class ActiveRecord::Base
|
|
144
160
|
else
|
145
161
|
models = args.first
|
146
162
|
column_names = self.column_names.dup
|
147
|
-
column_names.delete( self.primary_key ) unless options[ :on_duplicate_key_update ]
|
148
163
|
end
|
149
164
|
|
150
|
-
array_of_attributes
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
165
|
+
array_of_attributes = []
|
166
|
+
models.each do |model|
|
167
|
+
# this next line breaks sqlite.so with a segmentation fault
|
168
|
+
# if model.new_record? || options[:on_duplicate_key_update]
|
169
|
+
attributes = []
|
170
|
+
column_names.each do |name|
|
171
|
+
attributes << model.send( "#{name}_before_type_cast" )
|
172
|
+
end
|
173
|
+
array_of_attributes << attributes
|
174
|
+
# end
|
156
175
|
end
|
157
176
|
# supports 2-element array and array
|
158
177
|
elsif args.size == 2 and args.first.is_a?( Array ) and args.last.is_a?( Array )
|
@@ -160,22 +179,37 @@ class ActiveRecord::Base
|
|
160
179
|
else
|
161
180
|
raise ArgumentError.new( "Invalid arguments!" )
|
162
181
|
end
|
163
|
-
|
182
|
+
|
183
|
+
# Force the primary key col into the insert if it's not
|
184
|
+
# on the list and we are using a sequence and stuff a nil
|
185
|
+
# value for it into each row so the sequencer will fire later
|
186
|
+
if !column_names.include?(primary_key) && sequence_name && connection.prefetch_primary_key?
|
187
|
+
column_names << primary_key
|
188
|
+
array_of_attributes.each { |a| a << nil }
|
189
|
+
end
|
190
|
+
|
164
191
|
is_validating = options.delete( :validate )
|
165
|
-
|
192
|
+
|
166
193
|
# dup the passed in array so we don't modify it unintentionally
|
167
194
|
array_of_attributes = array_of_attributes.dup
|
168
|
-
|
195
|
+
|
196
|
+
# record timestamps unless disabled in ActiveRecord::Base
|
197
|
+
if record_timestamps
|
198
|
+
add_special_rails_stamps column_names, array_of_attributes, options
|
199
|
+
end
|
200
|
+
|
201
|
+
return_obj = if is_validating
|
169
202
|
import_with_validations( column_names, array_of_attributes, options )
|
170
203
|
else
|
171
|
-
import_without_validations_or_callbacks( column_names, array_of_attributes, options )
|
204
|
+
num_inserts = import_without_validations_or_callbacks( column_names, array_of_attributes, options )
|
205
|
+
OpenStruct.new :failed_instances=>[], :num_inserts=>num_inserts
|
172
206
|
end
|
173
|
-
|
174
207
|
if options[:synchronize]
|
175
208
|
synchronize( options[:synchronize] )
|
176
209
|
end
|
177
|
-
|
178
|
-
|
210
|
+
|
211
|
+
return_obj.num_inserts = 0 if return_obj.num_inserts.nil?
|
212
|
+
return_obj
|
179
213
|
end
|
180
214
|
|
181
215
|
# TODO import_from_table needs to be implemented.
|
@@ -184,7 +218,9 @@ class ActiveRecord::Base
|
|
184
218
|
|
185
219
|
# Imports the passed in +column_names+ and +array_of_attributes+
|
186
220
|
# given the passed in +options+ Hash with validations. Returns an
|
187
|
-
#
|
221
|
+
# object with the methods +failed_instances+ and +num_inserts+.
|
222
|
+
# +failed_instances+ is an array of instances that failed validations.
|
223
|
+
# +num_inserts+ is the number of inserts it took to import the data. See
|
188
224
|
# ActiveRecord::Base.import for more information on
|
189
225
|
# +column_names+, +array_of_attributes+ and +options+.
|
190
226
|
def import_with_validations( column_names, array_of_attributes, options={} )
|
@@ -192,10 +228,10 @@ class ActiveRecord::Base
|
|
192
228
|
|
193
229
|
# create instances for each of our column/value sets
|
194
230
|
arr = validations_array_for_column_names_and_attributes( column_names, array_of_attributes )
|
195
|
-
|
231
|
+
|
196
232
|
# keep track of the instance and the position it is currently at. if this fails
|
197
233
|
# validation we'll use the index to remove it from the array_of_attributes
|
198
|
-
arr.each_with_index do |hsh,i|
|
234
|
+
arr.each_with_index do |hsh,i|
|
199
235
|
instance = new( hsh )
|
200
236
|
if not instance.valid?
|
201
237
|
array_of_attributes[ i ] = nil
|
@@ -204,10 +240,8 @@ class ActiveRecord::Base
|
|
204
240
|
end
|
205
241
|
array_of_attributes.compact!
|
206
242
|
|
207
|
-
|
208
|
-
|
209
|
-
end
|
210
|
-
failed_instances
|
243
|
+
num_inserts = array_of_attributes.empty? ? 0 : import_without_validations_or_callbacks( column_names, array_of_attributes, options )
|
244
|
+
OpenStruct.new :failed_instances=>failed_instances, :num_inserts => num_inserts
|
211
245
|
end
|
212
246
|
|
213
247
|
# Imports the passed in +column_names+ and +array_of_attributes+
|
@@ -224,15 +258,20 @@ class ActiveRecord::Base
|
|
224
258
|
if not supports_import?
|
225
259
|
columns_sql = "(" + escaped_column_names.join( ',' ) + ")"
|
226
260
|
insert_statements, values = [], []
|
261
|
+
number_inserted = 0
|
227
262
|
array_of_attributes.each do |arr|
|
228
263
|
my_values = []
|
229
264
|
arr.each_with_index do |val,j|
|
230
|
-
|
265
|
+
if sequence_name && column_names[j] == primary_key && val.nil?
|
266
|
+
my_values << "#{sequence_name}.nextval"
|
267
|
+
else
|
268
|
+
my_values << connection.quote( val, columns[j] )
|
269
|
+
end
|
231
270
|
end
|
232
271
|
insert_statements << "INSERT INTO #{self.table_name} #{columns_sql} VALUES(" + my_values.join( ',' ) + ")"
|
233
|
-
|
272
|
+
number_inserted = number_inserted + connection.execute( insert_statements.last )
|
234
273
|
end
|
235
|
-
return
|
274
|
+
return number_inserted
|
236
275
|
else
|
237
276
|
|
238
277
|
# generate the sql
|
@@ -255,6 +294,42 @@ class ActiveRecord::Base
|
|
255
294
|
|
256
295
|
|
257
296
|
private
|
297
|
+
|
298
|
+
|
299
|
+
def add_special_rails_stamps( column_names, array_of_attributes, options )
|
300
|
+
AREXT_RAILS_COLUMNS[:create].each_pair do |key, blk|
|
301
|
+
if self.column_names.include?(key)
|
302
|
+
value = blk.call
|
303
|
+
if index=column_names.index(key)
|
304
|
+
# replace every instance of the array of attributes with our value
|
305
|
+
array_of_attributes.each{ |arr| arr[index] = value }
|
306
|
+
else
|
307
|
+
column_names << key
|
308
|
+
array_of_attributes.each { |arr| arr << value }
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
AREXT_RAILS_COLUMNS[:update].each_pair do |key, blk|
|
314
|
+
if self.column_names.include?(key)
|
315
|
+
value = blk.call
|
316
|
+
if index=column_names.index(key)
|
317
|
+
# replace every instance of the array of attributes with our value
|
318
|
+
array_of_attributes.each{ |arr| arr[index] = value }
|
319
|
+
else
|
320
|
+
column_names << key
|
321
|
+
array_of_attributes.each { |arr| arr << value }
|
322
|
+
end
|
323
|
+
|
324
|
+
if options[:on_duplicate_key_update]
|
325
|
+
options[:on_duplicate_key_update] << key.to_sym if options[:on_duplicate_key_update].is_a?(Array)
|
326
|
+
options[:on_duplicate_key_update][key.to_sym] = key.to_sym if options[:on_duplicate_key_update].is_a?(Hash)
|
327
|
+
else
|
328
|
+
options[:on_duplicate_key_update] = [ key.to_sym ]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
258
333
|
|
259
334
|
# Returns an Array of Hashes for the passed in +column_names+ and +array_of_attributes+.
|
260
335
|
def validations_array_for_column_names_and_attributes( column_names, array_of_attributes ) # :nodoc:
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ar-extensions
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.7.0
|
7
|
+
date: 2007-07-21 00:00:00 -04:00
|
8
8
|
summary: Extends ActiveRecord functionality.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- config/postgresql.schema
|
45
45
|
- lib/ar-extensions/adapters/abstract_adapter.rb
|
46
46
|
- lib/ar-extensions/adapters/mysql_adapter.rb
|
47
|
+
- lib/ar-extensions/adapters/oracle.rb
|
47
48
|
- lib/ar-extensions/adapters/postgresql.rb
|
48
49
|
- lib/ar-extensions/csv.rb
|
49
50
|
- lib/ar-extensions/extensions.rb
|