ar-extensions 0.6.0 → 0.7.0
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.
- 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
|