activerecord-import 0.17.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +40 -23
- data/CHANGELOG.md +315 -1
- data/Gemfile +23 -13
- data/LICENSE +21 -56
- data/README.markdown +564 -33
- data/Rakefile +2 -1
- data/activerecord-import.gemspec +3 -3
- data/benchmarks/lib/cli_parser.rb +2 -1
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +0 -0
- data/gemfiles/5.1.gemfile +2 -0
- data/gemfiles/5.2.gemfile +2 -0
- data/gemfiles/6.0.gemfile +2 -0
- data/gemfiles/6.1.gemfile +1 -0
- data/lib/activerecord-import.rb +2 -15
- data/lib/activerecord-import/adapters/abstract_adapter.rb +9 -3
- data/lib/activerecord-import/adapters/mysql_adapter.rb +17 -11
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +68 -20
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +128 -9
- data/lib/activerecord-import/base.rb +12 -7
- data/lib/activerecord-import/import.rb +514 -166
- data/lib/activerecord-import/synchronize.rb +2 -2
- data/lib/activerecord-import/value_sets_parser.rb +16 -0
- data/lib/activerecord-import/version.rb +1 -1
- data/test/adapters/makara_postgis.rb +1 -0
- data/test/import_test.rb +274 -23
- data/test/makara_postgis/import_test.rb +8 -0
- data/test/models/account.rb +3 -0
- data/test/models/animal.rb +6 -0
- data/test/models/bike_maker.rb +7 -0
- data/test/models/tag.rb +1 -1
- data/test/models/topic.rb +14 -0
- data/test/models/user.rb +3 -0
- data/test/models/user_token.rb +4 -0
- data/test/schema/generic_schema.rb +30 -8
- data/test/schema/mysql2_schema.rb +19 -0
- data/test/schema/postgresql_schema.rb +18 -0
- data/test/schema/sqlite3_schema.rb +13 -0
- data/test/support/factories.rb +9 -8
- data/test/support/generate.rb +6 -6
- data/test/support/mysql/import_examples.rb +14 -2
- data/test/support/postgresql/import_examples.rb +220 -1
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +15 -9
- data/test/support/shared_examples/on_duplicate_key_update.rb +271 -8
- data/test/support/shared_examples/recursive_import.rb +91 -21
- data/test/support/sqlite3/import_examples.rb +189 -25
- data/test/synchronize_test.rb +8 -0
- data/test/test_helper.rb +24 -3
- data/test/value_sets_bytes_parser_test.rb +13 -2
- metadata +32 -13
- data/test/schema/mysql_schema.rb +0 -16
@@ -31,7 +31,7 @@ module ActiveRecord # :nodoc:
|
|
31
31
|
|
32
32
|
klass = instances.first.class
|
33
33
|
|
34
|
-
fresh_instances = klass.where(conditions).order(order)
|
34
|
+
fresh_instances = klass.unscoped.where(conditions).order(order)
|
35
35
|
instances.each do |instance|
|
36
36
|
matched_instance = fresh_instances.detect do |fresh_instance|
|
37
37
|
keys.all? { |key| fresh_instance.send(key) == instance.send(key) }
|
@@ -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)
|
@@ -1,4 +1,14 @@
|
|
1
|
+
require 'active_support/core_ext/array'
|
2
|
+
|
1
3
|
module ActiveRecord::Import
|
4
|
+
class ValueSetTooLargeError < StandardError
|
5
|
+
attr_reader :size
|
6
|
+
def initialize(msg = "Value set exceeds max size", size = 0)
|
7
|
+
@size = size
|
8
|
+
super(msg)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
2
12
|
class ValueSetsBytesParser
|
3
13
|
attr_reader :reserved_bytes, :max_bytes, :values
|
4
14
|
|
@@ -18,6 +28,12 @@ module ActiveRecord::Import
|
|
18
28
|
current_size = 0
|
19
29
|
values.each_with_index do |val, i|
|
20
30
|
comma_bytes = arr.size
|
31
|
+
insert_size = reserved_bytes + val.bytesize
|
32
|
+
|
33
|
+
if insert_size > max_bytes
|
34
|
+
raise ValueSetTooLargeError.new("#{insert_size} bytes exceeds the max allowed for an insert [#{@max_bytes}]", insert_size)
|
35
|
+
end
|
36
|
+
|
21
37
|
bytes_thus_far = reserved_bytes + current_size + val.bytesize + comma_bytes
|
22
38
|
if bytes_thus_far <= max_bytes
|
23
39
|
current_size += val.bytesize
|
@@ -0,0 +1 @@
|
|
1
|
+
ENV["ARE_DB"] = "postgis"
|
data/test/import_test.rb
CHANGED
@@ -17,6 +17,11 @@ describe "#import" do
|
|
17
17
|
assert_equal error.message, "Last argument should be a two dimensional array '[[]]'. First element in array was a String"
|
18
18
|
end
|
19
19
|
|
20
|
+
it "warns you that you're passing more data than you ought to" do
|
21
|
+
error = assert_raise(ArgumentError) { Topic.import %w(title author_name), [['Author #1', 'Book #1', 0]] }
|
22
|
+
assert_equal error.message, "Number of values (8) exceeds number of columns (7)"
|
23
|
+
end
|
24
|
+
|
20
25
|
it "should not produce an error when importing empty arrays" do
|
21
26
|
assert_nothing_raised do
|
22
27
|
Topic.import []
|
@@ -86,23 +91,73 @@ describe "#import" do
|
|
86
91
|
assert_nil t.author_email_address
|
87
92
|
end
|
88
93
|
end
|
89
|
-
end
|
90
94
|
|
91
|
-
|
92
|
-
|
93
|
-
|
95
|
+
context "with extra keys" do
|
96
|
+
let(:values) do
|
97
|
+
[
|
98
|
+
{ title: "LDAP", author_name: "Jerry Carter" },
|
99
|
+
{ title: "Rails Recipes", author_name: "Chad Fowler", author_email_address: "cfowler@test.com" } # author_email_address is unknown
|
100
|
+
]
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should fail when column names are not specified" do
|
104
|
+
err = assert_raises ArgumentError do
|
105
|
+
Topic.import values, validate: false
|
106
|
+
end
|
107
|
+
|
108
|
+
assert err.message.include? 'Extra keys: [:author_email_address]'
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should succeed when column names are specified" do
|
112
|
+
assert_difference "Topic.count", +2 do
|
113
|
+
Topic.import columns, values, validate: false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "with missing keys" do
|
119
|
+
let(:values) do
|
120
|
+
[
|
121
|
+
{ title: "LDAP", author_name: "Jerry Carter" },
|
122
|
+
{ title: "Rails Recipes" } # author_name is missing
|
123
|
+
]
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should fail when column names are not specified" do
|
127
|
+
err = assert_raises ArgumentError do
|
128
|
+
Topic.import values, validate: false
|
129
|
+
end
|
130
|
+
|
131
|
+
assert err.message.include? 'Missing keys: [:author_name]'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should fail on missing hash key from specified column names" do
|
135
|
+
err = assert_raises ArgumentError do
|
136
|
+
Topic.import %i(author_name), values, validate: false
|
137
|
+
end
|
94
138
|
|
95
|
-
|
96
|
-
Tag.import tags
|
139
|
+
assert err.message.include? 'Missing keys: [:author_name]'
|
97
140
|
end
|
98
141
|
end
|
142
|
+
end
|
143
|
+
|
144
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
145
|
+
describe "with composite primary keys" do
|
146
|
+
it "should import models successfully" do
|
147
|
+
tags = [Tag.new(tag_id: 1, publisher_id: 1, tag: 'Mystery')]
|
148
|
+
|
149
|
+
assert_difference "Tag.count", +1 do
|
150
|
+
Tag.import tags
|
151
|
+
end
|
152
|
+
end
|
99
153
|
|
100
|
-
|
101
|
-
|
102
|
-
|
154
|
+
it "should import array of values successfully" do
|
155
|
+
columns = [:tag_id, :publisher_id, :tag]
|
156
|
+
values = [[1, 1, 'Mystery'], [2, 1, 'Science']]
|
103
157
|
|
104
|
-
|
105
|
-
|
158
|
+
assert_difference "Tag.count", +2 do
|
159
|
+
Tag.import columns, values, validate: false
|
160
|
+
end
|
106
161
|
end
|
107
162
|
end
|
108
163
|
end
|
@@ -119,12 +174,12 @@ describe "#import" do
|
|
119
174
|
end
|
120
175
|
|
121
176
|
context "with :validation option" do
|
122
|
-
let(:columns) { %w(title author_name) }
|
123
|
-
let(:valid_values) { [["LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
|
124
|
-
let(:valid_values_with_context) { [[1111, "Jerry Carter"], [2222, "Chad Fowler"]] }
|
125
|
-
let(:invalid_values) { [["The RSpec Book", ""], ["Agile+UX", ""]] }
|
126
|
-
let(:valid_models) { valid_values.map { |title, author_name| Topic.new(title: title, author_name: author_name) } }
|
127
|
-
let(:invalid_models) { invalid_values.map { |title, author_name| Topic.new(title: title, author_name: author_name) } }
|
177
|
+
let(:columns) { %w(title author_name content) }
|
178
|
+
let(:valid_values) { [["LDAP", "Jerry Carter", "Putting Directories to Work."], ["Rails Recipes", "Chad Fowler", "A trusted collection of solutions."]] }
|
179
|
+
let(:valid_values_with_context) { [[1111, "Jerry Carter", "1111"], [2222, "Chad Fowler", "2222"]] }
|
180
|
+
let(:invalid_values) { [["The RSpec Book", "David Chelimsky", "..."], ["Agile+UX", "", "All about Agile in UX."]] }
|
181
|
+
let(:valid_models) { valid_values.map { |title, author_name, content| Topic.new(title: title, author_name: author_name, content: content) } }
|
182
|
+
let(:invalid_models) { invalid_values.map { |title, author_name, content| Topic.new(title: title, author_name: author_name, content: content) } }
|
128
183
|
|
129
184
|
context "with validation checks turned off" do
|
130
185
|
it "should import valid data" do
|
@@ -159,6 +214,22 @@ describe "#import" do
|
|
159
214
|
end
|
160
215
|
end
|
161
216
|
|
217
|
+
it "should ignore uniqueness validators" do
|
218
|
+
Topic.import columns, valid_values
|
219
|
+
assert_difference "Topic.count", +2 do
|
220
|
+
Topic.import columns, valid_values
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should not alter the callback chain of the model" do
|
225
|
+
attributes = columns.zip(valid_values.first).to_h
|
226
|
+
topic = Topic.new attributes
|
227
|
+
Topic.import [topic], validate: true
|
228
|
+
duplicate_topic = Topic.new attributes
|
229
|
+
Topic.import [duplicate_topic], validate: true
|
230
|
+
assert duplicate_topic.invalid?
|
231
|
+
end
|
232
|
+
|
162
233
|
it "should not import invalid data" do
|
163
234
|
assert_no_difference "Topic.count" do
|
164
235
|
Topic.import columns, invalid_values, validate: true
|
@@ -181,8 +252,18 @@ describe "#import" do
|
|
181
252
|
end
|
182
253
|
end
|
183
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
|
+
|
184
265
|
it "should set ids in valid models if adapter supports setting primary key of imported objects" do
|
185
|
-
if ActiveRecord::Base.
|
266
|
+
if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
|
186
267
|
Topic.import (invalid_models + valid_models), validate: true
|
187
268
|
assert_nil invalid_models[0].id
|
188
269
|
assert_nil invalid_models[1].id
|
@@ -192,7 +273,7 @@ describe "#import" do
|
|
192
273
|
end
|
193
274
|
|
194
275
|
it "should set ActiveRecord timestamps in valid models if adapter supports setting primary key of imported objects" do
|
195
|
-
if ActiveRecord::Base.
|
276
|
+
if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
|
196
277
|
Timecop.freeze(Time.at(0)) do
|
197
278
|
Topic.import (invalid_models + valid_models), validate: true
|
198
279
|
end
|
@@ -215,6 +296,48 @@ describe "#import" do
|
|
215
296
|
end
|
216
297
|
assert_equal 0, Topic.where(title: invalid_values.map(&:first)).count
|
217
298
|
end
|
299
|
+
|
300
|
+
it "should run callbacks" do
|
301
|
+
assert_no_difference "Topic.count" do
|
302
|
+
Topic.import columns, [["invalid", "Jerry Carter"]], validate: true
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
it "should call validation methods" do
|
307
|
+
assert_no_difference "Topic.count" do
|
308
|
+
Topic.import columns, [["validate_failed", "Jerry Carter"]], validate: true
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context "with uniqueness validators included" do
|
314
|
+
it "should not import duplicate records" do
|
315
|
+
Topic.import columns, valid_values
|
316
|
+
assert_no_difference "Topic.count" do
|
317
|
+
Topic.import columns, valid_values, validate_uniqueness: true
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "when validatoring presence of belongs_to association" do
|
323
|
+
it "should not import records without foreign key" do
|
324
|
+
assert_no_difference "UserToken.count" do
|
325
|
+
UserToken.import [:token], [['12345abcdef67890']]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should import records with foreign key" do
|
330
|
+
assert_difference "UserToken.count", +1 do
|
331
|
+
UserToken.import [:user_name, :token], [%w("Bob", "12345abcdef67890")]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should not mutate the defined validations" do
|
336
|
+
UserToken.import [:user_name, :token], [%w("Bob", "12345abcdef67890")]
|
337
|
+
ut = UserToken.new
|
338
|
+
ut.valid?
|
339
|
+
assert_includes ut.errors.messages, :user
|
340
|
+
end
|
218
341
|
end
|
219
342
|
end
|
220
343
|
|
@@ -282,6 +405,15 @@ describe "#import" do
|
|
282
405
|
assert_equal 3, result.num_inserts if Topic.supports_import?
|
283
406
|
end
|
284
407
|
end
|
408
|
+
|
409
|
+
it "should accept and call an optional callable to run after each batch" do
|
410
|
+
lambda_called = 0
|
411
|
+
|
412
|
+
my_proc = ->(_row_count, _batches, _batch, _duration) { lambda_called += 1 }
|
413
|
+
Topic.import Build(10, :topics), batch_size: 4, batch_progress: my_proc
|
414
|
+
|
415
|
+
assert_equal 3, lambda_called
|
416
|
+
end
|
285
417
|
end
|
286
418
|
|
287
419
|
context "with :synchronize option" do
|
@@ -290,7 +422,7 @@ describe "#import" do
|
|
290
422
|
|
291
423
|
it "doesn't reload any data (doesn't work)" do
|
292
424
|
Topic.import new_topics, synchronize: new_topics
|
293
|
-
if Topic.
|
425
|
+
if Topic.supports_setting_primary_key_of_imported_objects?
|
294
426
|
assert new_topics.all?(&:persisted?), "Records should have been reloaded"
|
295
427
|
else
|
296
428
|
assert new_topics.all?(&:new_record?), "No record should have been reloaded"
|
@@ -412,11 +544,11 @@ describe "#import" do
|
|
412
544
|
|
413
545
|
context "when the timestamps columns are present" do
|
414
546
|
setup do
|
415
|
-
@existing_book = Book.create(title: "Fell", author_name: "Curry", publisher: "Bayer", created_at: 2.years.ago.utc, created_on: 2.years.ago.utc)
|
547
|
+
@existing_book = Book.create(title: "Fell", author_name: "Curry", publisher: "Bayer", created_at: 2.years.ago.utc, created_on: 2.years.ago.utc, updated_at: 2.years.ago.utc, updated_on: 2.years.ago.utc)
|
416
548
|
ActiveRecord::Base.default_timezone = :utc
|
417
549
|
Timecop.freeze(time) do
|
418
550
|
assert_difference "Book.count", +2 do
|
419
|
-
Book.import %w(title author_name publisher created_at created_on), [["LDAP", "Big Bird", "Del Rey", nil, nil], [@existing_book.title, @existing_book.author_name, @existing_book.publisher, @existing_book.created_at, @existing_book.created_on]]
|
551
|
+
Book.import %w(title author_name publisher created_at created_on updated_at updated_on), [["LDAP", "Big Bird", "Del Rey", nil, nil, nil, nil], [@existing_book.title, @existing_book.author_name, @existing_book.publisher, @existing_book.created_at, @existing_book.created_on, @existing_book.updated_at, @existing_book.updated_on]]
|
420
552
|
end
|
421
553
|
end
|
422
554
|
@new_book, @existing_book = Book.last 2
|
@@ -445,6 +577,23 @@ describe "#import" do
|
|
445
577
|
it "should set the updated_on column for new records" do
|
446
578
|
assert_in_delta time.to_i, @new_book.updated_on.to_i, 1.second
|
447
579
|
end
|
580
|
+
|
581
|
+
it "should not set the updated_at column for existing records" do
|
582
|
+
assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.updated_at.strftime("%Y:%d")
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should not set the updated_on column for existing records" do
|
586
|
+
assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.updated_on.strftime("%Y:%d")
|
587
|
+
end
|
588
|
+
|
589
|
+
it "should not set the updated_at column on models if changed" do
|
590
|
+
timestamp = Time.now.utc
|
591
|
+
books = [
|
592
|
+
Book.new(author_name: "Foo", title: "Baz", created_at: timestamp, updated_at: timestamp)
|
593
|
+
]
|
594
|
+
Book.import books
|
595
|
+
assert_equal timestamp.strftime("%Y:%d"), Book.last.updated_at.strftime("%Y:%d")
|
596
|
+
end
|
448
597
|
end
|
449
598
|
|
450
599
|
context "when a custom time zone is set" do
|
@@ -489,7 +638,7 @@ describe "#import" do
|
|
489
638
|
|
490
639
|
context "importing through an association scope" do
|
491
640
|
{ has_many: :chapters, polymorphic: :discounts }.each do |association_type, association|
|
492
|
-
book =
|
641
|
+
book = FactoryBot.create :book
|
493
642
|
scope = book.public_send association
|
494
643
|
klass = { chapters: Chapter, discounts: Discount }[association]
|
495
644
|
column = { chapters: :title, discounts: :amount }[association]
|
@@ -511,10 +660,35 @@ describe "#import" do
|
|
511
660
|
|
512
661
|
assert_equal [val1, val2], scope.map(&column).sort
|
513
662
|
end
|
663
|
+
|
664
|
+
it "works importing array of hashes" do
|
665
|
+
scope.import [{ column => val1 }, { column => val2 }]
|
666
|
+
|
667
|
+
assert_equal [val1, val2], scope.map(&column).sort
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
it "works with a non-standard association primary key" do
|
672
|
+
user = User.create(id: 1, name: 'Solomon')
|
673
|
+
user.user_tokens.import [:id, :token], [[5, '12345abcdef67890']]
|
674
|
+
|
675
|
+
token = UserToken.find(5)
|
676
|
+
assert_equal 'Solomon', token.user_name
|
514
677
|
end
|
515
678
|
end
|
516
679
|
end
|
517
680
|
|
681
|
+
context "importing model with polymorphic belongs_to" do
|
682
|
+
it "works without error" do
|
683
|
+
book = FactoryBot.create :book
|
684
|
+
discount = Discount.new(discountable: book)
|
685
|
+
|
686
|
+
Discount.import([discount])
|
687
|
+
|
688
|
+
assert_equal 1, Discount.count
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
518
692
|
context 'When importing models with Enum fields' do
|
519
693
|
it 'should be able to import enum fields' do
|
520
694
|
Book.delete_all if Book.count > 0
|
@@ -589,6 +763,30 @@ describe "#import" do
|
|
589
763
|
end
|
590
764
|
end
|
591
765
|
|
766
|
+
context 'importing arrays of values with boolean fields' do
|
767
|
+
let(:columns) { [:author_name, :title, :for_sale] }
|
768
|
+
|
769
|
+
it 'should be able to coerce integers as boolean fields' do
|
770
|
+
Book.delete_all if Book.count > 0
|
771
|
+
values = [['Author #1', 'Book #1', 0], ['Author #2', 'Book #2', 1]]
|
772
|
+
assert_difference "Book.count", +2 do
|
773
|
+
Book.import columns, values
|
774
|
+
end
|
775
|
+
assert_equal false, Book.first.for_sale
|
776
|
+
assert_equal true, Book.last.for_sale
|
777
|
+
end
|
778
|
+
|
779
|
+
it 'should be able to coerce strings as boolean fields' do
|
780
|
+
Book.delete_all if Book.count > 0
|
781
|
+
values = [['Author #1', 'Book #1', 'false'], ['Author #2', 'Book #2', 'true']]
|
782
|
+
assert_difference "Book.count", +2 do
|
783
|
+
Book.import columns, values
|
784
|
+
end
|
785
|
+
assert_equal false, Book.first.for_sale
|
786
|
+
assert_equal true, Book.last.for_sale
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
592
790
|
describe "importing when model has default_scope" do
|
593
791
|
it "doesn't import the default scope values" do
|
594
792
|
assert_difference "Widget.unscoped.count", +2 do
|
@@ -638,6 +836,16 @@ describe "#import" do
|
|
638
836
|
assert_equal(data.as_json, Widget.find_by_w_id(9).json_data)
|
639
837
|
end
|
640
838
|
|
839
|
+
it "imports serialized values from saved records" do
|
840
|
+
Widget.import [:w_id, :json_data], [[1, data]]
|
841
|
+
assert_equal data.as_json, Widget.last.json_data
|
842
|
+
|
843
|
+
w = Widget.last
|
844
|
+
w.w_id = 2
|
845
|
+
Widget.import([w])
|
846
|
+
assert_equal data.as_json, Widget.last.json_data
|
847
|
+
end
|
848
|
+
|
641
849
|
context "with a store" do
|
642
850
|
it "imports serialized attributes set using accessors" do
|
643
851
|
vendors = [Vendor.new(name: 'Vendor 1', color: 'blue')]
|
@@ -696,5 +904,48 @@ describe "#import" do
|
|
696
904
|
end
|
697
905
|
end
|
698
906
|
end
|
907
|
+
|
908
|
+
context "with objects that respond to .to_sql as values" do
|
909
|
+
let(:columns) { %w(title author_name) }
|
910
|
+
let(:valid_values) { [["LDAP", Book.select("'Jerry Carter'").limit(1)], ["Rails Recipes", Book.select("'Chad Fowler'").limit(1)]] }
|
911
|
+
|
912
|
+
it "should import data" do
|
913
|
+
assert_difference "Topic.count", +2 do
|
914
|
+
Topic.import! columns, valid_values
|
915
|
+
topics = Topic.all
|
916
|
+
assert_equal "Jerry Carter", topics.first.author_name
|
917
|
+
assert_equal "Chad Fowler", topics.last.author_name
|
918
|
+
end
|
919
|
+
end
|
920
|
+
end
|
921
|
+
end
|
922
|
+
describe "importing model with after_initialize callback" do
|
923
|
+
let(:columns) { %w(name size) }
|
924
|
+
let(:valid_values) { [%w("Deer", "Small"), %w("Monkey", "Medium")] }
|
925
|
+
let(:invalid_values) do
|
926
|
+
[
|
927
|
+
{ name: "giraffe", size: "Large" },
|
928
|
+
{ size: "Medium" } # name is missing
|
929
|
+
]
|
930
|
+
end
|
931
|
+
context "with validation checks turned off" do
|
932
|
+
it "should import valid data" do
|
933
|
+
Animal.import(columns, valid_values, validate: false)
|
934
|
+
assert_equal 2, Animal.count
|
935
|
+
end
|
936
|
+
it "should raise ArgumentError" do
|
937
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: false) }
|
938
|
+
end
|
939
|
+
end
|
940
|
+
|
941
|
+
context "with validation checks turned on" do
|
942
|
+
it "should import valid data" do
|
943
|
+
Animal.import(columns, valid_values, validate: true)
|
944
|
+
assert_equal 2, Animal.count
|
945
|
+
end
|
946
|
+
it "should raise ArgumentError" do
|
947
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: true) }
|
948
|
+
end
|
949
|
+
end
|
699
950
|
end
|
700
951
|
end
|