activerecord-import 1.0.1 → 1.0.7
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.
- 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
|