activerecord-import 1.0.1 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +17 -14
- data/CHANGELOG.md +68 -2
- data/Gemfile +4 -1
- data/LICENSE +21 -56
- data/README.markdown +66 -70
- data/activerecord-import.gemspec +3 -3
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +0 -0
- data/gemfiles/6.0.gemfile +1 -0
- data/gemfiles/6.1.gemfile +1 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +7 -1
- data/lib/activerecord-import/adapters/mysql_adapter.rb +6 -6
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +9 -9
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +15 -19
- data/lib/activerecord-import/base.rb +8 -1
- data/lib/activerecord-import/import.rb +45 -20
- data/lib/activerecord-import/synchronize.rb +1 -1
- data/lib/activerecord-import/version.rb +1 -1
- data/test/import_test.rb +39 -0
- data/test/models/animal.rb +6 -0
- data/test/schema/postgresql_schema.rb +14 -0
- data/test/support/postgresql/import_examples.rb +31 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +10 -0
- data/test/support/shared_examples/recursive_import.rb +9 -0
- data/test/support/sqlite3/import_examples.rb +2 -15
- metadata +14 -11
data/activerecord-import.gemspec
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.email = ["zach.dennis@gmail.com"]
|
7
7
|
gem.summary = "Bulk insert extension for ActiveRecord"
|
8
8
|
gem.description = "A library for bulk inserting data using ActiveRecord."
|
9
|
-
gem.homepage = "
|
10
|
-
gem.license = "
|
9
|
+
gem.homepage = "https://github.com/zdennis/activerecord-import"
|
10
|
+
gem.license = "MIT"
|
11
11
|
|
12
12
|
gem.files = `git ls-files`.split($\)
|
13
13
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = ActiveRecord::Import::VERSION
|
18
18
|
|
19
|
-
gem.required_ruby_version = ">=
|
19
|
+
gem.required_ruby_version = ">= 2.0.0"
|
20
20
|
|
21
21
|
gem.add_runtime_dependency "activerecord", ">= 3.2"
|
22
22
|
gem.add_development_dependency "rake"
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
gem 'activerecord', '~> 6.0.0'
|
@@ -0,0 +1 @@
|
|
1
|
+
gem 'activerecord', '~> 6.1.0.alpha', github: "rails/rails"
|
@@ -46,7 +46,7 @@ module ActiveRecord::Import::AbstractAdapter
|
|
46
46
|
|
47
47
|
if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
|
48
48
|
post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:primary_key], options[:locking_column] )
|
49
|
-
elsif options[:on_duplicate_key_update]
|
49
|
+
elsif logger && options[:on_duplicate_key_update]
|
50
50
|
logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
|
51
51
|
end
|
52
52
|
|
@@ -59,6 +59,12 @@ module ActiveRecord::Import::AbstractAdapter
|
|
59
59
|
post_sql_statements
|
60
60
|
end
|
61
61
|
|
62
|
+
def increment_locking_column!(table_name, results, locking_column)
|
63
|
+
if locking_column.present?
|
64
|
+
results << "\"#{locking_column}\"=#{table_name}.\"#{locking_column}\"+1"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
62
68
|
def supports_on_duplicate_key_update?
|
63
69
|
false
|
64
70
|
end
|
@@ -56,9 +56,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
56
56
|
# in a single packet
|
57
57
|
def max_allowed_packet # :nodoc:
|
58
58
|
@max_allowed_packet ||= begin
|
59
|
-
result = execute( "
|
59
|
+
result = execute( "SELECT @@max_allowed_packet" )
|
60
60
|
# original Mysql gem responds to #fetch_row while Mysql2 responds to #first
|
61
|
-
val = result.respond_to?(:fetch_row) ? result.fetch_row[
|
61
|
+
val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
|
62
62
|
val.to_i
|
63
63
|
end
|
64
64
|
end
|
@@ -102,7 +102,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
102
102
|
qc = quote_column_name( column )
|
103
103
|
"#{table_name}.#{qc}=VALUES(#{qc})"
|
104
104
|
end
|
105
|
-
increment_locking_column!(
|
105
|
+
increment_locking_column!(table_name, results, locking_column)
|
106
106
|
results.join( ',' )
|
107
107
|
end
|
108
108
|
|
@@ -112,7 +112,7 @@ module ActiveRecord::Import::MysqlAdapter
|
|
112
112
|
qc2 = quote_column_name( column2 )
|
113
113
|
"#{table_name}.#{qc1}=VALUES( #{qc2} )"
|
114
114
|
end
|
115
|
-
increment_locking_column!(
|
115
|
+
increment_locking_column!(table_name, results, locking_column)
|
116
116
|
results.join( ',')
|
117
117
|
end
|
118
118
|
|
@@ -121,9 +121,9 @@ module ActiveRecord::Import::MysqlAdapter
|
|
121
121
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
|
122
122
|
end
|
123
123
|
|
124
|
-
def increment_locking_column!(
|
124
|
+
def increment_locking_column!(table_name, results, locking_column)
|
125
125
|
if locking_column.present?
|
126
|
-
results << "
|
126
|
+
results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -73,7 +73,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
73
73
|
if (options[:ignore] || options[:on_duplicate_key_ignore]) && !options[:on_duplicate_key_update] && !options[:recursive]
|
74
74
|
sql << sql_for_on_duplicate_key_ignore( table_name, options[:on_duplicate_key_ignore] )
|
75
75
|
end
|
76
|
-
elsif options[:on_duplicate_key_ignore] && !options[:on_duplicate_key_update]
|
76
|
+
elsif logger && options[:on_duplicate_key_ignore] && !options[:on_duplicate_key_update]
|
77
77
|
logger.warn "Ignoring on_duplicate_key_ignore because it is not supported by the database."
|
78
78
|
end
|
79
79
|
|
@@ -158,7 +158,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
158
158
|
qc = quote_column_name( column )
|
159
159
|
"#{qc}=EXCLUDED.#{qc}"
|
160
160
|
end
|
161
|
-
increment_locking_column!(results, locking_column)
|
161
|
+
increment_locking_column!(table_name, results, locking_column)
|
162
162
|
results.join( ',' )
|
163
163
|
end
|
164
164
|
|
@@ -168,7 +168,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
168
168
|
qc2 = quote_column_name( column2 )
|
169
169
|
"#{qc1}=EXCLUDED.#{qc2}"
|
170
170
|
end
|
171
|
-
increment_locking_column!(results, locking_column)
|
171
|
+
increment_locking_column!(table_name, results, locking_column)
|
172
172
|
results.join( ',' )
|
173
173
|
end
|
174
174
|
|
@@ -195,17 +195,17 @@ module ActiveRecord::Import::PostgreSQLAdapter
|
|
195
195
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('duplicate key')
|
196
196
|
end
|
197
197
|
|
198
|
-
def supports_on_duplicate_key_update?
|
199
|
-
|
198
|
+
def supports_on_duplicate_key_update?
|
199
|
+
database_version >= MIN_VERSION_FOR_UPSERT
|
200
200
|
end
|
201
201
|
|
202
202
|
def supports_setting_primary_key_of_imported_objects?
|
203
203
|
true
|
204
204
|
end
|
205
205
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
206
|
+
private
|
207
|
+
|
208
|
+
def database_version
|
209
|
+
defined?(postgresql_version) ? postgresql_version : super
|
210
210
|
end
|
211
211
|
end
|
@@ -9,16 +9,12 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
9
9
|
# Override our conformance to ActiveRecord::Import::ImportSupport interface
|
10
10
|
# to ensure that we only support import in supported version of SQLite.
|
11
11
|
# Which INSERT statements with multiple value sets was introduced in 3.7.11.
|
12
|
-
def supports_import?
|
13
|
-
|
14
|
-
true
|
15
|
-
else
|
16
|
-
false
|
17
|
-
end
|
12
|
+
def supports_import?
|
13
|
+
database_version >= MIN_VERSION_FOR_IMPORT
|
18
14
|
end
|
19
15
|
|
20
|
-
def supports_on_duplicate_key_update?
|
21
|
-
|
16
|
+
def supports_on_duplicate_key_update?
|
17
|
+
database_version >= MIN_VERSION_FOR_UPSERT
|
22
18
|
end
|
23
19
|
|
24
20
|
# +sql+ can be a single string or an array. If it is an array all
|
@@ -96,7 +92,7 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
96
92
|
|
97
93
|
# Returns a generated ON CONFLICT DO UPDATE statement given the passed
|
98
94
|
# in +args+.
|
99
|
-
def sql_for_on_duplicate_key_update(
|
95
|
+
def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
|
100
96
|
arg, primary_key, locking_column = args
|
101
97
|
arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
|
102
98
|
return unless arg.is_a?( Hash )
|
@@ -117,9 +113,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
117
113
|
|
118
114
|
sql << "#{conflict_target}DO UPDATE SET "
|
119
115
|
if columns.is_a?( Array )
|
120
|
-
sql << sql_for_on_duplicate_key_update_as_array( locking_column, columns )
|
116
|
+
sql << sql_for_on_duplicate_key_update_as_array( table_name, locking_column, columns )
|
121
117
|
elsif columns.is_a?( Hash )
|
122
|
-
sql << sql_for_on_duplicate_key_update_as_hash( locking_column, columns )
|
118
|
+
sql << sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, columns )
|
123
119
|
elsif columns.is_a?( String )
|
124
120
|
sql << columns
|
125
121
|
else
|
@@ -131,22 +127,22 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
131
127
|
sql
|
132
128
|
end
|
133
129
|
|
134
|
-
def sql_for_on_duplicate_key_update_as_array( locking_column, arr ) # :nodoc:
|
130
|
+
def sql_for_on_duplicate_key_update_as_array( table_name, locking_column, arr ) # :nodoc:
|
135
131
|
results = arr.map do |column|
|
136
132
|
qc = quote_column_name( column )
|
137
133
|
"#{qc}=EXCLUDED.#{qc}"
|
138
134
|
end
|
139
|
-
increment_locking_column!(results, locking_column)
|
135
|
+
increment_locking_column!(table_name, results, locking_column)
|
140
136
|
results.join( ',' )
|
141
137
|
end
|
142
138
|
|
143
|
-
def sql_for_on_duplicate_key_update_as_hash( locking_column, hsh ) # :nodoc:
|
139
|
+
def sql_for_on_duplicate_key_update_as_hash( table_name, locking_column, hsh ) # :nodoc:
|
144
140
|
results = hsh.map do |column1, column2|
|
145
141
|
qc1 = quote_column_name( column1 )
|
146
142
|
qc2 = quote_column_name( column2 )
|
147
143
|
"#{qc1}=EXCLUDED.#{qc2}"
|
148
144
|
end
|
149
|
-
increment_locking_column!(results, locking_column)
|
145
|
+
increment_locking_column!(table_name, results, locking_column)
|
150
146
|
results.join( ',' )
|
151
147
|
end
|
152
148
|
|
@@ -170,9 +166,9 @@ module ActiveRecord::Import::SQLite3Adapter
|
|
170
166
|
exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('duplicate key')
|
171
167
|
end
|
172
168
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
private
|
170
|
+
|
171
|
+
def database_version
|
172
|
+
defined?(sqlite_version) ? sqlite_version : super
|
177
173
|
end
|
178
174
|
end
|
@@ -13,6 +13,7 @@ module ActiveRecord::Import
|
|
13
13
|
when 'postgresql_makara' then 'postgresql'
|
14
14
|
when 'makara_postgis' then 'postgresql'
|
15
15
|
when 'postgis' then 'postgresql'
|
16
|
+
when 'cockroachdb' then 'postgresql'
|
16
17
|
else adapter
|
17
18
|
end
|
18
19
|
end
|
@@ -26,7 +27,13 @@ module ActiveRecord::Import
|
|
26
27
|
|
27
28
|
# Loads the import functionality for the passed in ActiveRecord connection
|
28
29
|
def self.load_from_connection_pool(connection_pool)
|
29
|
-
|
30
|
+
adapter =
|
31
|
+
if connection_pool.respond_to?(:db_config) # ActiveRecord >= 6.1
|
32
|
+
connection_pool.db_config.adapter
|
33
|
+
else
|
34
|
+
connection_pool.spec.config[:adapter]
|
35
|
+
end
|
36
|
+
require_adapter adapter
|
30
37
|
end
|
31
38
|
end
|
32
39
|
|
@@ -26,6 +26,7 @@ module ActiveRecord::Import #:nodoc:
|
|
26
26
|
class Validator
|
27
27
|
def initialize(klass, options = {})
|
28
28
|
@options = options
|
29
|
+
@validator_class = klass
|
29
30
|
init_validations(klass)
|
30
31
|
end
|
31
32
|
|
@@ -70,6 +71,8 @@ module ActiveRecord::Import #:nodoc:
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def valid_model?(model)
|
74
|
+
init_validations(model.class) unless model.class == @validator_class
|
75
|
+
|
73
76
|
validation_context = @options[:validate_with_context]
|
74
77
|
validation_context ||= (model.new_record? ? :create : :update)
|
75
78
|
current_context = model.send(:validation_context)
|
@@ -242,16 +245,17 @@ class ActiveRecord::Associations::CollectionAssociation
|
|
242
245
|
alias import bulk_import unless respond_to? :import
|
243
246
|
end
|
244
247
|
|
248
|
+
module ActiveRecord::Import::Connection
|
249
|
+
def establish_connection(args = nil)
|
250
|
+
conn = super(args)
|
251
|
+
ActiveRecord::Import.load_from_connection_pool connection_pool
|
252
|
+
conn
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
245
256
|
class ActiveRecord::Base
|
246
257
|
class << self
|
247
|
-
|
248
|
-
conn = establish_connection_without_activerecord_import(*args)
|
249
|
-
ActiveRecord::Import.load_from_connection_pool connection_pool
|
250
|
-
conn
|
251
|
-
end
|
252
|
-
|
253
|
-
alias establish_connection_without_activerecord_import establish_connection
|
254
|
-
alias establish_connection establish_connection_with_activerecord_import
|
258
|
+
prepend ActiveRecord::Import::Connection
|
255
259
|
|
256
260
|
# Returns true if the current database connection adapter
|
257
261
|
# supports import functionality, otherwise returns false.
|
@@ -543,7 +547,7 @@ class ActiveRecord::Base
|
|
543
547
|
alias import! bulk_import! unless ActiveRecord::Base.respond_to? :import!
|
544
548
|
|
545
549
|
def import_helper( *args )
|
546
|
-
options = { validate: true, timestamps: true }
|
550
|
+
options = { validate: true, timestamps: true, track_validation_failures: false }
|
547
551
|
options.merge!( args.pop ) if args.last.is_a? Hash
|
548
552
|
# making sure that current model's primary key is used
|
549
553
|
options[:primary_key] = primary_key
|
@@ -578,7 +582,7 @@ class ActiveRecord::Base
|
|
578
582
|
if respond_to?(:timestamp_attributes_for_update, true)
|
579
583
|
send(:timestamp_attributes_for_update).map(&:to_sym)
|
580
584
|
else
|
581
|
-
|
585
|
+
allocate.send(:timestamp_attributes_for_update_in_model)
|
582
586
|
end
|
583
587
|
end
|
584
588
|
|
@@ -627,7 +631,7 @@ class ActiveRecord::Base
|
|
627
631
|
end
|
628
632
|
# supports empty array
|
629
633
|
elsif args.last.is_a?( Array ) && args.last.empty?
|
630
|
-
return ActiveRecord::Import::Result.new([], 0, [])
|
634
|
+
return ActiveRecord::Import::Result.new([], 0, [], [])
|
631
635
|
# supports 2-element array and array
|
632
636
|
elsif args.size == 2 && args.first.is_a?( Array ) && args.last.is_a?( Array )
|
633
637
|
|
@@ -699,14 +703,18 @@ class ActiveRecord::Base
|
|
699
703
|
# keep track of the instance and the position it is currently at. if this fails
|
700
704
|
# validation we'll use the index to remove it from the array_of_attributes
|
701
705
|
arr.each_with_index do |hsh, i|
|
702
|
-
|
703
|
-
|
706
|
+
# utilize block initializer syntax to prevent failure when 'mass_assignment_sanitizer = :strict'
|
707
|
+
model = new do |m|
|
708
|
+
hsh.each_pair { |k, v| m[k] = v }
|
709
|
+
end
|
710
|
+
|
704
711
|
next if validator.valid_model?(model)
|
705
712
|
raise(ActiveRecord::RecordInvalid, model) if options[:raise_error]
|
713
|
+
|
706
714
|
array_of_attributes[i] = nil
|
707
715
|
failure = model.dup
|
708
716
|
failure.errors.send(:initialize_dup, model.errors)
|
709
|
-
failed_instances << failure
|
717
|
+
failed_instances << (options[:track_validation_failures] ? [i, failure] : failure )
|
710
718
|
end
|
711
719
|
array_of_attributes.compact!
|
712
720
|
end
|
@@ -804,7 +812,7 @@ class ActiveRecord::Base
|
|
804
812
|
result = connection.insert_many( [insert_sql, post_sql_statements].flatten,
|
805
813
|
batch_values,
|
806
814
|
options,
|
807
|
-
"#{model_name} Create Many
|
815
|
+
"#{model_name} Create Many" )
|
808
816
|
number_inserted += result.num_inserts
|
809
817
|
ids += result.ids
|
810
818
|
results += result.results
|
@@ -838,6 +846,19 @@ class ActiveRecord::Base
|
|
838
846
|
end
|
839
847
|
end
|
840
848
|
|
849
|
+
deserialize_value = lambda do |column, value|
|
850
|
+
column = columns_hash[column]
|
851
|
+
return value unless column
|
852
|
+
if respond_to?(:type_caster)
|
853
|
+
type = type_for_attribute(column.name)
|
854
|
+
type.deserialize(value)
|
855
|
+
elsif column.respond_to?(:type_cast_from_database)
|
856
|
+
column.type_cast_from_database(value)
|
857
|
+
else
|
858
|
+
value
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
841
862
|
if models.size == import_result.results.size
|
842
863
|
columns = Array(options[:returning])
|
843
864
|
single_column = "#{columns.first}=" if columns.size == 1
|
@@ -845,10 +866,12 @@ class ActiveRecord::Base
|
|
845
866
|
model = models[index]
|
846
867
|
|
847
868
|
if single_column
|
848
|
-
|
869
|
+
val = deserialize_value.call(columns.first, result)
|
870
|
+
model.send(single_column, val)
|
849
871
|
else
|
850
872
|
columns.each_with_index do |column, col_index|
|
851
|
-
|
873
|
+
val = deserialize_value.call(column, result[col_index])
|
874
|
+
model.send("#{column}=", val)
|
852
875
|
end
|
853
876
|
end
|
854
877
|
end
|
@@ -869,10 +892,12 @@ class ActiveRecord::Base
|
|
869
892
|
|
870
893
|
# Sync belongs_to association ids with foreign key field
|
871
894
|
def load_association_ids(model)
|
895
|
+
changed_columns = model.changed
|
872
896
|
association_reflections = model.class.reflect_on_all_associations(:belongs_to)
|
873
897
|
association_reflections.each do |association_reflection|
|
874
898
|
column_name = association_reflection.foreign_key
|
875
899
|
next if association_reflection.options[:polymorphic]
|
900
|
+
next if changed_columns.include?(column_name)
|
876
901
|
association = model.association(association_reflection.name)
|
877
902
|
association = association.target
|
878
903
|
next if association.blank? || model.public_send(column_name).present?
|
@@ -952,7 +977,7 @@ class ActiveRecord::Base
|
|
952
977
|
elsif column
|
953
978
|
if respond_to?(:type_caster) # Rails 5.0 and higher
|
954
979
|
type = type_for_attribute(column.name)
|
955
|
-
val = type.type == :boolean ? type.cast(val) : type.serialize(val)
|
980
|
+
val = !type.respond_to?(:subtype) && type.type == :boolean ? type.cast(val) : type.serialize(val)
|
956
981
|
connection_memo.quote(val)
|
957
982
|
elsif column.respond_to?(:type_cast_from_user) # Rails 4.2
|
958
983
|
connection_memo.quote(column.type_cast_from_user(val), column)
|
@@ -961,7 +986,7 @@ class ActiveRecord::Base
|
|
961
986
|
val = serialized_attributes[column.name].dump(val)
|
962
987
|
end
|
963
988
|
# Fixes #443 to support binary (i.e. bytea) columns on PG
|
964
|
-
val = column.type_cast(val) unless column.type.to_sym == :binary
|
989
|
+
val = column.type_cast(val) unless column.type && column.type.to_sym == :binary
|
965
990
|
connection_memo.quote(val, column)
|
966
991
|
end
|
967
992
|
else
|
@@ -980,7 +1005,7 @@ class ActiveRecord::Base
|
|
980
1005
|
timestamp_columns[:create] = timestamp_attributes_for_create_in_model
|
981
1006
|
timestamp_columns[:update] = timestamp_attributes_for_update_in_model
|
982
1007
|
else
|
983
|
-
instance =
|
1008
|
+
instance = allocate
|
984
1009
|
timestamp_columns[:create] = instance.send(:timestamp_attributes_for_create_in_model)
|
985
1010
|
timestamp_columns[:update] = instance.send(:timestamp_attributes_for_update_in_model)
|
986
1011
|
end
|
@@ -39,8 +39,8 @@ module ActiveRecord # :nodoc:
|
|
39
39
|
|
40
40
|
next unless matched_instance
|
41
41
|
|
42
|
-
instance.send :clear_aggregation_cache
|
43
42
|
instance.send :clear_association_cache
|
43
|
+
instance.send :clear_aggregation_cache if instance.respond_to?(:clear_aggregation_cache, true)
|
44
44
|
instance.instance_variable_set :@attributes, matched_instance.instance_variable_get(:@attributes)
|
45
45
|
|
46
46
|
if instance.respond_to?(:clear_changes_information)
|
data/test/import_test.rb
CHANGED
@@ -252,6 +252,16 @@ describe "#import" do
|
|
252
252
|
end
|
253
253
|
end
|
254
254
|
|
255
|
+
it "should index the failed instances by their poistion in the set if `track_failures` is true" do
|
256
|
+
index_offset = valid_values.length
|
257
|
+
results = Topic.import columns, valid_values + invalid_values, validate: true, track_validation_failures: true
|
258
|
+
assert_equal invalid_values.size, results.failed_instances.size
|
259
|
+
invalid_values.each_with_index do |value_set, index|
|
260
|
+
assert_equal index + index_offset, results.failed_instances[index].first
|
261
|
+
assert_equal value_set.first, results.failed_instances[index].last.title
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
255
265
|
it "should set ids in valid models if adapter supports setting primary key of imported objects" do
|
256
266
|
if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
|
257
267
|
Topic.import (invalid_models + valid_models), validate: true
|
@@ -900,4 +910,33 @@ describe "#import" do
|
|
900
910
|
end
|
901
911
|
end
|
902
912
|
end
|
913
|
+
describe "importing model with after_initialize callback" do
|
914
|
+
let(:columns) { %w(name size) }
|
915
|
+
let(:valid_values) { [%w("Deer", "Small"), %w("Monkey", "Medium")] }
|
916
|
+
let(:invalid_values) do
|
917
|
+
[
|
918
|
+
{ name: "giraffe", size: "Large" },
|
919
|
+
{ size: "Medium" } # name is missing
|
920
|
+
]
|
921
|
+
end
|
922
|
+
context "with validation checks turned off" do
|
923
|
+
it "should import valid data" do
|
924
|
+
Animal.import(columns, valid_values, validate: false)
|
925
|
+
assert_equal 2, Animal.count
|
926
|
+
end
|
927
|
+
it "should raise ArgumentError" do
|
928
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: false) }
|
929
|
+
end
|
930
|
+
end
|
931
|
+
|
932
|
+
context "with validation checks turned on" do
|
933
|
+
it "should import valid data" do
|
934
|
+
Animal.import(columns, valid_values, validate: true)
|
935
|
+
assert_equal 2, Animal.count
|
936
|
+
end
|
937
|
+
it "should raise ArgumentError" do
|
938
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: true) }
|
939
|
+
end
|
940
|
+
end
|
941
|
+
end
|
903
942
|
end
|