activerecord-import 0.23.0 → 1.4.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.
Files changed (58) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yaml +107 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +214 -4
  5. data/Gemfile +11 -9
  6. data/LICENSE +21 -56
  7. data/README.markdown +574 -22
  8. data/Rakefile +2 -1
  9. data/activerecord-import.gemspec +4 -4
  10. data/benchmarks/benchmark.rb +5 -1
  11. data/benchmarks/schema/{mysql_schema.rb → mysql2_schema.rb} +0 -0
  12. data/gemfiles/5.0.gemfile +1 -0
  13. data/gemfiles/5.1.gemfile +1 -0
  14. data/gemfiles/5.2.gemfile +2 -2
  15. data/gemfiles/6.0.gemfile +2 -0
  16. data/gemfiles/6.1.gemfile +2 -0
  17. data/gemfiles/7.0.gemfile +1 -0
  18. data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +4 -4
  19. data/lib/activerecord-import/adapters/abstract_adapter.rb +7 -1
  20. data/lib/activerecord-import/adapters/mysql_adapter.rb +8 -11
  21. data/lib/activerecord-import/adapters/postgresql_adapter.rb +14 -16
  22. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +125 -8
  23. data/lib/activerecord-import/base.rb +9 -1
  24. data/lib/activerecord-import/import.rb +269 -123
  25. data/lib/activerecord-import/synchronize.rb +2 -2
  26. data/lib/activerecord-import/value_sets_parser.rb +2 -0
  27. data/lib/activerecord-import/version.rb +1 -1
  28. data/lib/activerecord-import.rb +1 -0
  29. data/test/adapters/makara_postgis.rb +1 -0
  30. data/test/{travis → github}/database.yml +3 -1
  31. data/test/import_test.rb +138 -8
  32. data/test/makara_postgis/import_test.rb +8 -0
  33. data/test/models/animal.rb +6 -0
  34. data/test/models/card.rb +3 -0
  35. data/test/models/customer.rb +6 -0
  36. data/test/models/deck.rb +6 -0
  37. data/test/models/order.rb +6 -0
  38. data/test/models/playing_card.rb +2 -0
  39. data/test/models/user.rb +3 -1
  40. data/test/models/user_token.rb +4 -0
  41. data/test/schema/generic_schema.rb +30 -0
  42. data/test/schema/mysql2_schema.rb +19 -0
  43. data/test/schema/postgresql_schema.rb +16 -0
  44. data/test/schema/sqlite3_schema.rb +13 -0
  45. data/test/support/factories.rb +8 -8
  46. data/test/support/generate.rb +6 -6
  47. data/test/support/mysql/import_examples.rb +12 -0
  48. data/test/support/postgresql/import_examples.rb +100 -2
  49. data/test/support/shared_examples/on_duplicate_key_update.rb +54 -0
  50. data/test/support/shared_examples/recursive_import.rb +74 -4
  51. data/test/support/sqlite3/import_examples.rb +189 -25
  52. data/test/test_helper.rb +28 -3
  53. metadata +37 -18
  54. data/.travis.yml +0 -62
  55. data/gemfiles/3.2.gemfile +0 -2
  56. data/gemfiles/4.0.gemfile +0 -2
  57. data/gemfiles/4.1.gemfile +0 -2
  58. 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.send :clear_aggregation_cache
43
- instance.send :clear_association_cache
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)
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/array'
2
+
1
3
  module ActiveRecord::Import
2
4
  class ValueSetTooLargeError < StandardError
3
5
  attr_reader :size
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.23.0".freeze
3
+ VERSION = "1.4.0".freeze
4
4
  end
5
5
  end
@@ -1,4 +1,5 @@
1
1
  # rubocop:disable Style/FileName
2
+ require "active_support/lazy_load_hooks"
2
3
 
3
4
  ActiveSupport.on_load(:active_record) do
4
5
  require "activerecord-import/base"
@@ -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.first.type
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, validate: true
228
+ Topic.import columns, valid_values
214
229
  assert_difference "Topic.count", +2 do
215
- Topic.import columns, valid_values, validate: true
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::Base.default_timezone = :utc
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 = FactoryGirl.create :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 = FactoryGirl.create :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
@@ -0,0 +1,6 @@
1
+ class Animal < ActiveRecord::Base
2
+ after_initialize :validate_name_presence, if: :new_record?
3
+ def validate_name_presence
4
+ raise ArgumentError if name.nil?
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class Card < ActiveRecord::Base
2
+ belongs_to :deck, polymorphic: true
3
+ end
@@ -0,0 +1,6 @@
1
+ class Customer < ActiveRecord::Base
2
+ has_many :orders,
3
+ inverse_of: :customer,
4
+ primary_key: %i(account_id id),
5
+ foreign_key: %i(account_id customer_id)
6
+ end
@@ -0,0 +1,6 @@
1
+ class Deck < ActiveRecord::Base
2
+ has_many :cards
3
+ def self.polymorphic_name
4
+ "PlayingCard"
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class Order < ActiveRecord::Base
2
+ belongs_to :customer,
3
+ inverse_of: :orders,
4
+ primary_key: %i(account_id id),
5
+ foreign_key: %i(account_id customer_id)
6
+ end
@@ -0,0 +1,2 @@
1
+ class PlayingCard < ActiveRecord::Base
2
+ end
data/test/models/user.rb CHANGED
@@ -1 +1,3 @@
1
- class User < ActiveRecord::Base; end
1
+ class User < ActiveRecord::Base
2
+ has_many :user_tokens, primary_key: :name, foreign_key: :user_name
3
+ end
@@ -0,0 +1,4 @@
1
+ class UserToken < ActiveRecord::Base
2
+ belongs_to :user, primary_key: :name, foreign_key: :user_name
3
+ validates :user, presence: true
4
+ end
@@ -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
@@ -1,4 +1,4 @@
1
- FactoryGirl.define do
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(FactoryGirl.attributes_for(: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: FactoryGirl.generate(:book_title), author_name: 'Stephen King')
43
+ book = topic.books.build(title: FactoryBot.generate(:book_title), author_name: 'Stephen King')
44
44
  3.times do
45
- book.chapters.build(title: FactoryGirl.generate(:chapter_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: FactoryGirl.generate(:end_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
@@ -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
- factory_girl_args = args.shift || {}
5
+ factory_bot_args = args.shift || {}
6
6
 
7
7
  if n
8
8
  [].tap do |collection|
9
- n.times.each { collection << FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args) }
9
+ n.times.each { collection << FactoryBot.build(factory.to_s.singularize.to_sym, factory_bot_args) }
10
10
  end
11
11
  else
12
- FactoryGirl.build(factory.to_s.singularize.to_sym, factory_girl_args)
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
- factory_girl_args = args.shift || {}
19
+ factory_bot_args = args.shift || {}
20
20
 
21
21
  if n
22
22
  [].tap do |collection|
23
- n.times.each { collection << FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args) }
23
+ n.times.each { collection << FactoryBot.create(factory.to_s.singularize.to_sym, factory_bot_args) }
24
24
  end
25
25
  else
26
- FactoryGirl.create(factory.to_s.singularize.to_sym, factory_girl_args)
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