activerecord-import 0.23.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/test.yaml +107 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +214 -4
- data/Gemfile +11 -9
- data/LICENSE +21 -56
- data/README.markdown +574 -22
- data/Rakefile +2 -1
- data/activerecord-import.gemspec +4 -4
- data/benchmarks/benchmark.rb +5 -1
- data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +0 -0
- data/gemfiles/5.0.gemfile +1 -0
- data/gemfiles/5.1.gemfile +1 -0
- data/gemfiles/5.2.gemfile +2 -2
- data/gemfiles/6.0.gemfile +2 -0
- data/gemfiles/6.1.gemfile +2 -0
- data/gemfiles/7.0.gemfile +1 -0
- data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +4 -4
- data/lib/activerecord-import/adapters/abstract_adapter.rb +7 -1
- data/lib/activerecord-import/adapters/mysql_adapter.rb +8 -11
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +14 -16
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +125 -8
- data/lib/activerecord-import/base.rb +9 -1
- data/lib/activerecord-import/import.rb +269 -123
- data/lib/activerecord-import/synchronize.rb +2 -2
- data/lib/activerecord-import/value_sets_parser.rb +2 -0
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import.rb +1 -0
- data/test/adapters/makara_postgis.rb +1 -0
- data/test/{travis → github}/database.yml +3 -1
- data/test/import_test.rb +138 -8
- data/test/makara_postgis/import_test.rb +8 -0
- data/test/models/animal.rb +6 -0
- data/test/models/card.rb +3 -0
- data/test/models/customer.rb +6 -0
- data/test/models/deck.rb +6 -0
- data/test/models/order.rb +6 -0
- data/test/models/playing_card.rb +2 -0
- data/test/models/user.rb +3 -1
- data/test/models/user_token.rb +4 -0
- data/test/schema/generic_schema.rb +30 -0
- data/test/schema/mysql2_schema.rb +19 -0
- data/test/schema/postgresql_schema.rb +16 -0
- data/test/schema/sqlite3_schema.rb +13 -0
- data/test/support/factories.rb +8 -8
- data/test/support/generate.rb +6 -6
- data/test/support/mysql/import_examples.rb +12 -0
- data/test/support/postgresql/import_examples.rb +100 -2
- data/test/support/shared_examples/on_duplicate_key_update.rb +54 -0
- data/test/support/shared_examples/recursive_import.rb +74 -4
- data/test/support/sqlite3/import_examples.rb +189 -25
- data/test/test_helper.rb +28 -3
- metadata +37 -18
- data/.travis.yml +0 -62
- data/gemfiles/3.2.gemfile +0 -2
- data/gemfiles/4.0.gemfile +0 -2
- data/gemfiles/4.1.gemfile +0 -2
- data/test/schema/mysql_schema.rb +0 -16
@@ -39,8 +39,8 @@ module ActiveRecord # :nodoc:
|
|
39
39
|
|
40
40
|
next unless matched_instance
|
41
41
|
|
42
|
-
instance.
|
43
|
-
instance.send :
|
42
|
+
instance.instance_variable_set :@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/lib/activerecord-import.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
ENV["ARE_DB"] = "postgis"
|
@@ -1,7 +1,8 @@
|
|
1
1
|
common: &common
|
2
2
|
username: root
|
3
|
-
password:
|
3
|
+
password: root
|
4
4
|
encoding: utf8
|
5
|
+
collation: utf8_general_ci
|
5
6
|
host: localhost
|
6
7
|
database: activerecord_import_test
|
7
8
|
|
@@ -37,6 +38,7 @@ oracle:
|
|
37
38
|
postgresql: &postgresql
|
38
39
|
<<: *common
|
39
40
|
username: postgres
|
41
|
+
password: postgres
|
40
42
|
adapter: postgresql
|
41
43
|
min_messages: warning
|
42
44
|
|
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 []
|
@@ -164,7 +169,17 @@ describe "#import" do
|
|
164
169
|
assert_difference "Dictionary.count", +1 do
|
165
170
|
Dictionary.import dictionaries
|
166
171
|
end
|
167
|
-
assert_equal "Dictionary", Dictionary.
|
172
|
+
assert_equal "Dictionary", Dictionary.last.type
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should import arrays successfully" do
|
176
|
+
columns = [:author_name, :title]
|
177
|
+
values = [["Noah Webster", "Webster's Dictionary"]]
|
178
|
+
|
179
|
+
assert_difference "Dictionary.count", +1 do
|
180
|
+
Dictionary.import columns, values
|
181
|
+
end
|
182
|
+
assert_equal "Dictionary", Dictionary.last.type
|
168
183
|
end
|
169
184
|
end
|
170
185
|
|
@@ -210,9 +225,9 @@ describe "#import" do
|
|
210
225
|
end
|
211
226
|
|
212
227
|
it "should ignore uniqueness validators" do
|
213
|
-
Topic.import columns, valid_values
|
228
|
+
Topic.import columns, valid_values
|
214
229
|
assert_difference "Topic.count", +2 do
|
215
|
-
Topic.import columns, valid_values
|
230
|
+
Topic.import columns, valid_values
|
216
231
|
end
|
217
232
|
end
|
218
233
|
|
@@ -247,6 +262,16 @@ describe "#import" do
|
|
247
262
|
end
|
248
263
|
end
|
249
264
|
|
265
|
+
it "should index the failed instances by their poistion in the set if `track_failures` is true" do
|
266
|
+
index_offset = valid_values.length
|
267
|
+
results = Topic.import columns, valid_values + invalid_values, validate: true, track_validation_failures: true
|
268
|
+
assert_equal invalid_values.size, results.failed_instances.size
|
269
|
+
invalid_values.each_with_index do |value_set, index|
|
270
|
+
assert_equal index + index_offset, results.failed_instances[index].first
|
271
|
+
assert_equal value_set.first, results.failed_instances[index].last.title
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
250
275
|
it "should set ids in valid models if adapter supports setting primary key of imported objects" do
|
251
276
|
if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
|
252
277
|
Topic.import (invalid_models + valid_models), validate: true
|
@@ -294,6 +319,36 @@ describe "#import" do
|
|
294
319
|
end
|
295
320
|
end
|
296
321
|
end
|
322
|
+
|
323
|
+
context "with uniqueness validators included" do
|
324
|
+
it "should not import duplicate records" do
|
325
|
+
Topic.import columns, valid_values
|
326
|
+
assert_no_difference "Topic.count" do
|
327
|
+
Topic.import columns, valid_values, validate_uniqueness: true
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "when validatoring presence of belongs_to association" do
|
333
|
+
it "should not import records without foreign key" do
|
334
|
+
assert_no_difference "UserToken.count" do
|
335
|
+
UserToken.import [:token], [['12345abcdef67890']]
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should import records with foreign key" do
|
340
|
+
assert_difference "UserToken.count", +1 do
|
341
|
+
UserToken.import [:user_name, :token], [%w("Bob", "12345abcdef67890")]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should not mutate the defined validations" do
|
346
|
+
UserToken.import [:user_name, :token], [%w("Bob", "12345abcdef67890")]
|
347
|
+
ut = UserToken.new
|
348
|
+
ut.valid?
|
349
|
+
assert_includes ut.errors.messages, :user
|
350
|
+
end
|
351
|
+
end
|
297
352
|
end
|
298
353
|
|
299
354
|
context "without :validation option" do
|
@@ -360,6 +415,15 @@ describe "#import" do
|
|
360
415
|
assert_equal 3, result.num_inserts if Topic.supports_import?
|
361
416
|
end
|
362
417
|
end
|
418
|
+
|
419
|
+
it "should accept and call an optional callable to run after each batch" do
|
420
|
+
lambda_called = 0
|
421
|
+
|
422
|
+
my_proc = ->(_row_count, _batches, _batch, _duration) { lambda_called += 1 }
|
423
|
+
Topic.import Build(10, :topics), batch_size: 4, batch_progress: my_proc
|
424
|
+
|
425
|
+
assert_equal 3, lambda_called
|
426
|
+
end
|
363
427
|
end
|
364
428
|
|
365
429
|
context "with :synchronize option" do
|
@@ -490,11 +554,15 @@ describe "#import" do
|
|
490
554
|
|
491
555
|
context "when the timestamps columns are present" do
|
492
556
|
setup do
|
493
|
-
@existing_book = Book.create(title: "Fell", author_name: "Curry", publisher: "Bayer", created_at: 2.years.ago.utc, created_on: 2.years.ago.utc)
|
494
|
-
ActiveRecord
|
557
|
+
@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)
|
558
|
+
if ActiveRecord.respond_to?(:default_timezone)
|
559
|
+
ActiveRecord.default_timezone = :utc
|
560
|
+
else
|
561
|
+
ActiveRecord::Base.default_timezone = :utc
|
562
|
+
end
|
495
563
|
Timecop.freeze(time) do
|
496
564
|
assert_difference "Book.count", +2 do
|
497
|
-
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]]
|
565
|
+
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]]
|
498
566
|
end
|
499
567
|
end
|
500
568
|
@new_book, @existing_book = Book.last 2
|
@@ -523,6 +591,23 @@ describe "#import" do
|
|
523
591
|
it "should set the updated_on column for new records" do
|
524
592
|
assert_in_delta time.to_i, @new_book.updated_on.to_i, 1.second
|
525
593
|
end
|
594
|
+
|
595
|
+
it "should not set the updated_at column for existing records" do
|
596
|
+
assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.updated_at.strftime("%Y:%d")
|
597
|
+
end
|
598
|
+
|
599
|
+
it "should not set the updated_on column for existing records" do
|
600
|
+
assert_equal 2.years.ago.utc.strftime("%Y:%d"), @existing_book.updated_on.strftime("%Y:%d")
|
601
|
+
end
|
602
|
+
|
603
|
+
it "should not set the updated_at column on models if changed" do
|
604
|
+
timestamp = Time.now.utc
|
605
|
+
books = [
|
606
|
+
Book.new(author_name: "Foo", title: "Baz", created_at: timestamp, updated_at: timestamp)
|
607
|
+
]
|
608
|
+
Book.import books
|
609
|
+
assert_equal timestamp.strftime("%Y:%d"), Book.last.updated_at.strftime("%Y:%d")
|
610
|
+
end
|
526
611
|
end
|
527
612
|
|
528
613
|
context "when a custom time zone is set" do
|
@@ -567,7 +652,7 @@ describe "#import" do
|
|
567
652
|
|
568
653
|
context "importing through an association scope" do
|
569
654
|
{ has_many: :chapters, polymorphic: :discounts }.each do |association_type, association|
|
570
|
-
book =
|
655
|
+
book = FactoryBot.create :book
|
571
656
|
scope = book.public_send association
|
572
657
|
klass = { chapters: Chapter, discounts: Discount }[association]
|
573
658
|
column = { chapters: :title, discounts: :amount }[association]
|
@@ -590,18 +675,34 @@ describe "#import" do
|
|
590
675
|
assert_equal [val1, val2], scope.map(&column).sort
|
591
676
|
end
|
592
677
|
|
678
|
+
context "for cards and decks" do
|
679
|
+
it "works when the polymorphic name is different than base class name" do
|
680
|
+
deck = Deck.create(id: 1, name: 'test')
|
681
|
+
deck.cards.import [:id, :deck_type], [[1, 'PlayingCard']]
|
682
|
+
assert_equal deck.cards.first.deck_type, "PlayingCard"
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
593
686
|
it "works importing array of hashes" do
|
594
687
|
scope.import [{ column => val1 }, { column => val2 }]
|
595
688
|
|
596
689
|
assert_equal [val1, val2], scope.map(&column).sort
|
597
690
|
end
|
598
691
|
end
|
692
|
+
|
693
|
+
it "works with a non-standard association primary key" do
|
694
|
+
user = User.create(id: 1, name: 'Solomon')
|
695
|
+
user.user_tokens.import [:id, :token], [[5, '12345abcdef67890']]
|
696
|
+
|
697
|
+
token = UserToken.find(5)
|
698
|
+
assert_equal 'Solomon', token.user_name
|
699
|
+
end
|
599
700
|
end
|
600
701
|
end
|
601
702
|
|
602
703
|
context "importing model with polymorphic belongs_to" do
|
603
704
|
it "works without error" do
|
604
|
-
book =
|
705
|
+
book = FactoryBot.create :book
|
605
706
|
discount = Discount.new(discountable: book)
|
606
707
|
|
607
708
|
Discount.import([discount])
|
@@ -840,4 +941,33 @@ describe "#import" do
|
|
840
941
|
end
|
841
942
|
end
|
842
943
|
end
|
944
|
+
describe "importing model with after_initialize callback" do
|
945
|
+
let(:columns) { %w(name size) }
|
946
|
+
let(:valid_values) { [%w("Deer", "Small"), %w("Monkey", "Medium")] }
|
947
|
+
let(:invalid_values) do
|
948
|
+
[
|
949
|
+
{ name: "giraffe", size: "Large" },
|
950
|
+
{ size: "Medium" } # name is missing
|
951
|
+
]
|
952
|
+
end
|
953
|
+
context "with validation checks turned off" do
|
954
|
+
it "should import valid data" do
|
955
|
+
Animal.import(columns, valid_values, validate: false)
|
956
|
+
assert_equal 2, Animal.count
|
957
|
+
end
|
958
|
+
it "should raise ArgumentError" do
|
959
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: false) }
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
context "with validation checks turned on" do
|
964
|
+
it "should import valid data" do
|
965
|
+
Animal.import(columns, valid_values, validate: true)
|
966
|
+
assert_equal 2, Animal.count
|
967
|
+
end
|
968
|
+
it "should raise ArgumentError" do
|
969
|
+
assert_raise(ArgumentError) { Animal.import(invalid_values, validate: true) }
|
970
|
+
end
|
971
|
+
end
|
972
|
+
end
|
843
973
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
|
3
|
+
|
4
|
+
should_support_postgresql_import_functionality
|
5
|
+
|
6
|
+
if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
|
7
|
+
should_support_postgresql_upsert_functionality
|
8
|
+
end
|
data/test/models/card.rb
ADDED
data/test/models/deck.rb
ADDED
data/test/models/user.rb
CHANGED
@@ -52,6 +52,20 @@ ActiveRecord::Schema.define do
|
|
52
52
|
t.string :name
|
53
53
|
end
|
54
54
|
|
55
|
+
create_table :cards, force: :cascade do |t|
|
56
|
+
t.string :name
|
57
|
+
t.string :deck_type
|
58
|
+
t.integer :deck_id
|
59
|
+
end
|
60
|
+
|
61
|
+
create_table :decks, force: :cascade do |t|
|
62
|
+
t.string :name
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :playing_cards, force: :cascade do |t|
|
66
|
+
t.string :name
|
67
|
+
end
|
68
|
+
|
55
69
|
create_table :books, force: :cascade do |t|
|
56
70
|
t.string :title, null: false
|
57
71
|
t.string :publisher, null: false, default: 'Default Publisher'
|
@@ -164,6 +178,11 @@ ActiveRecord::Schema.define do
|
|
164
178
|
t.integer :lock_version, null: false, default: 0
|
165
179
|
end
|
166
180
|
|
181
|
+
create_table :user_tokens, force: :cascade do |t|
|
182
|
+
t.string :user_name, null: false
|
183
|
+
t.string :token, null: false
|
184
|
+
end
|
185
|
+
|
167
186
|
create_table :accounts, force: :cascade do |t|
|
168
187
|
t.string :name, null: false
|
169
188
|
t.integer :lock, null: false, default: 0
|
@@ -186,4 +205,15 @@ ActiveRecord::Schema.define do
|
|
186
205
|
);
|
187
206
|
).split.join(' ').strip
|
188
207
|
end
|
208
|
+
|
209
|
+
create_table :customers, force: :cascade do |t|
|
210
|
+
t.integer :account_id
|
211
|
+
t.string :name
|
212
|
+
end
|
213
|
+
|
214
|
+
create_table :orders, force: :cascade do |t|
|
215
|
+
t.integer :account_id
|
216
|
+
t.integer :customer_id
|
217
|
+
t.integer :amount
|
218
|
+
end
|
189
219
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :books, force: :cascade do |t|
|
3
|
+
t.string :title, null: false
|
4
|
+
t.virtual :upper_title, type: :string, as: "upper(`title`)" if t.respond_to?(:virtual)
|
5
|
+
t.string :publisher, null: false, default: 'Default Publisher'
|
6
|
+
t.string :author_name, null: false
|
7
|
+
t.datetime :created_at
|
8
|
+
t.datetime :created_on
|
9
|
+
t.datetime :updated_at
|
10
|
+
t.datetime :updated_on
|
11
|
+
t.date :publish_date
|
12
|
+
t.integer :topic_id
|
13
|
+
t.integer :tag_id
|
14
|
+
t.integer :publisher_id
|
15
|
+
t.boolean :for_sale, default: true
|
16
|
+
t.integer :status, default: 0
|
17
|
+
t.string :type
|
18
|
+
end
|
19
|
+
end
|
@@ -3,11 +3,24 @@ ActiveRecord::Schema.define do
|
|
3
3
|
execute('CREATE extension IF NOT EXISTS "pgcrypto";')
|
4
4
|
execute('CREATE extension IF NOT EXISTS "uuid-ossp";')
|
5
5
|
|
6
|
+
# create ENUM if it does not exist yet
|
7
|
+
begin
|
8
|
+
execute('CREATE TYPE vendor_type AS ENUM (\'wholesaler\', \'retailer\');')
|
9
|
+
rescue ActiveRecord::StatementInvalid => e
|
10
|
+
# since PostgreSQL does not support IF NOT EXISTS when creating a TYPE,
|
11
|
+
# rescue the error and check the error class
|
12
|
+
raise unless e.cause.is_a? PG::DuplicateObject
|
13
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'wholesaler\';')
|
14
|
+
execute('ALTER TYPE vendor_type ADD VALUE IF NOT EXISTS \'retailer\';')
|
15
|
+
end
|
16
|
+
|
6
17
|
create_table :vendors, id: :uuid, force: :cascade do |t|
|
7
18
|
t.string :name, null: true
|
19
|
+
t.text :hours
|
8
20
|
t.text :preferences
|
9
21
|
|
10
22
|
if t.respond_to?(:json)
|
23
|
+
t.json :pure_json_data
|
11
24
|
t.json :data
|
12
25
|
else
|
13
26
|
t.text :data
|
@@ -20,6 +33,7 @@ ActiveRecord::Schema.define do
|
|
20
33
|
end
|
21
34
|
|
22
35
|
if t.respond_to?(:jsonb)
|
36
|
+
t.jsonb :pure_jsonb_data
|
23
37
|
t.jsonb :settings
|
24
38
|
t.jsonb :json_data, null: false, default: {}
|
25
39
|
else
|
@@ -27,6 +41,8 @@ ActiveRecord::Schema.define do
|
|
27
41
|
t.text :json_data
|
28
42
|
end
|
29
43
|
|
44
|
+
t.column :vendor_type, :vendor_type
|
45
|
+
|
30
46
|
t.datetime :created_at
|
31
47
|
t.datetime :updated_at
|
32
48
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
ActiveRecord::Schema.define do
|
2
|
+
create_table :alarms, force: true do |t|
|
3
|
+
t.column :device_id, :integer, null: false
|
4
|
+
t.column :alarm_type, :integer, null: false
|
5
|
+
t.column :status, :integer, null: false
|
6
|
+
t.column :metadata, :text
|
7
|
+
t.column :secret_key, :binary
|
8
|
+
t.datetime :created_at
|
9
|
+
t.datetime :updated_at
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :alarms, [:device_id, :alarm_type], unique: true, where: 'status <> 0'
|
13
|
+
end
|
data/test/support/factories.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
FactoryBot.define do
|
2
2
|
sequence(:book_title) { |n| "Book #{n}" }
|
3
3
|
sequence(:chapter_title) { |n| "Chapter #{n}" }
|
4
4
|
sequence(:end_note) { |n| "Endnote #{n}" }
|
@@ -9,7 +9,7 @@ FactoryGirl.define do
|
|
9
9
|
|
10
10
|
factory :invalid_topic, class: "Topic" do
|
11
11
|
sequence(:title) { |n| "Title #{n}" }
|
12
|
-
author_name nil
|
12
|
+
author_name { nil }
|
13
13
|
end
|
14
14
|
|
15
15
|
factory :topic do
|
@@ -27,7 +27,7 @@ FactoryGirl.define do
|
|
27
27
|
|
28
28
|
trait :with_rule do
|
29
29
|
after(:build) do |question|
|
30
|
-
question.build_rule(
|
30
|
+
question.build_rule(FactoryBot.attributes_for(:rule))
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -40,21 +40,21 @@ FactoryGirl.define do
|
|
40
40
|
factory :topic_with_book, parent: :topic do
|
41
41
|
after(:build) do |topic|
|
42
42
|
2.times do
|
43
|
-
book = topic.books.build(title:
|
43
|
+
book = topic.books.build(title: FactoryBot.generate(:book_title), author_name: 'Stephen King')
|
44
44
|
3.times do
|
45
|
-
book.chapters.build(title:
|
45
|
+
book.chapters.build(title: FactoryBot.generate(:chapter_title))
|
46
46
|
end
|
47
47
|
|
48
48
|
4.times do
|
49
|
-
book.end_notes.build(note:
|
49
|
+
book.end_notes.build(note: FactoryBot.generate(:end_note))
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
55
|
factory :book do
|
56
|
-
title 'Tortilla Flat'
|
57
|
-
author_name 'John Steinbeck'
|
56
|
+
title { 'Tortilla Flat' }
|
57
|
+
author_name { 'John Steinbeck' }
|
58
58
|
end
|
59
59
|
|
60
60
|
factory :car do
|
data/test/support/generate.rb
CHANGED
@@ -2,28 +2,28 @@ class ActiveSupport::TestCase
|
|
2
2
|
def Build(*args) # rubocop:disable Style/MethodName
|
3
3
|
n = args.shift if args.first.is_a?(Numeric)
|
4
4
|
factory = args.shift
|
5
|
-
|
5
|
+
factory_bot_args = args.shift || {}
|
6
6
|
|
7
7
|
if n
|
8
8
|
[].tap do |collection|
|
9
|
-
n.times.each { collection <<
|
9
|
+
n.times.each { collection << FactoryBot.build(factory.to_s.singularize.to_sym, factory_bot_args) }
|
10
10
|
end
|
11
11
|
else
|
12
|
-
|
12
|
+
FactoryBot.build(factory.to_s.singularize.to_sym, factory_bot_args)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def Generate(*args) # rubocop:disable Style/MethodName
|
17
17
|
n = args.shift if args.first.is_a?(Numeric)
|
18
18
|
factory = args.shift
|
19
|
-
|
19
|
+
factory_bot_args = args.shift || {}
|
20
20
|
|
21
21
|
if n
|
22
22
|
[].tap do |collection|
|
23
|
-
n.times.each { collection <<
|
23
|
+
n.times.each { collection << FactoryBot.create(factory.to_s.singularize.to_sym, factory_bot_args) }
|
24
24
|
end
|
25
25
|
else
|
26
|
-
|
26
|
+
FactoryBot.create(factory.to_s.singularize.to_sym, factory_bot_args)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -82,5 +82,17 @@ def should_support_mysql_import_functionality
|
|
82
82
|
assert_equal "Chad Fowler", topics.last.author_name, "wrong author!"
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
if ENV['AR_VERSION'].to_f >= 5.1
|
87
|
+
context "with virtual columns" do
|
88
|
+
let(:books) { [Book.new(author_name: "foo", title: "bar")] }
|
89
|
+
|
90
|
+
it "ignores virtual columns and creates record" do
|
91
|
+
assert_difference "Book.count", +1 do
|
92
|
+
Book.import books
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
85
97
|
end
|
86
98
|
end
|