activerecord-import 1.5.1 → 1.7.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +29 -2
- data/.gitignore +4 -0
- data/.rubocop_todo.yml +4 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +9 -1
- data/README.markdown +25 -1
- data/Rakefile +1 -0
- data/lib/activerecord-import/active_record/adapters/trilogy_adapter.rb +8 -0
- data/lib/activerecord-import/adapters/trilogy_adapter.rb +7 -0
- data/lib/activerecord-import/import.rb +20 -4
- data/lib/activerecord-import/version.rb +1 -1
- data/lib/activerecord-import.rb +0 -1
- data/test/adapters/trilogy.rb +9 -0
- data/test/database.yml.sample +5 -0
- data/test/github/database.yml +4 -0
- data/test/models/author.rb +7 -0
- data/test/models/book.rb +5 -2
- data/test/models/composite_book.rb +19 -0
- data/test/models/composite_chapter.rb +9 -0
- data/test/models/customer.rb +12 -4
- data/test/models/order.rb +11 -4
- data/test/models/tag.rb +6 -1
- data/test/models/tag_alias.rb +5 -1
- data/test/schema/postgresql_schema.rb +34 -0
- data/test/support/postgresql/import_examples.rb +12 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +28 -0
- data/test/support/shared_examples/recursive_import.rb +66 -0
- data/test/test_helper.rb +3 -1
- data/test/trilogy/import_test.rb +7 -0
- metadata +18 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5aeed63fcbc9ad647ab901931265d2765baf5d0e5a1e9d2cf2c68af32dd5e1be
|
4
|
+
data.tar.gz: 47480cc7244aada4bc69ba28043e8ab85ae1cece4420db4453400425a5773126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f6b5749dd6ce81784f7016c6227a6d0f5047c59a45beb13616853a89599ac507241012a92c3e4f9996c7a465d4d926e0e8d1183af1854f7a0c8a05e2e59219f
|
7
|
+
data.tar.gz: 72b00e7a5c61af3923b618ebe0d96f945a7af145f38ec5312962220b925e1dfc918da6028bb48d8ff7842c32584b34b06cec8fd8288dbb83212ca035f7a16c08
|
data/.github/workflows/test.yaml
CHANGED
@@ -16,11 +16,25 @@ jobs:
|
|
16
16
|
--health-interval 10s
|
17
17
|
--health-timeout 5s
|
18
18
|
--health-retries 5
|
19
|
+
mysql:
|
20
|
+
image: mysql:5.7
|
21
|
+
ports:
|
22
|
+
- 3306:3306
|
23
|
+
env:
|
24
|
+
MYSQL_ROOT_PASSWORD: root
|
25
|
+
MYSQL_USER: github
|
26
|
+
MYSQL_PASSWORD: github
|
27
|
+
MYSQL_DATABASE: activerecord_import_test
|
28
|
+
options: >-
|
29
|
+
--health-cmd "mysqladmin ping -h localhost"
|
30
|
+
--health-interval 10s
|
31
|
+
--health-timeout 5s
|
32
|
+
--health-retries 5
|
19
33
|
strategy:
|
20
34
|
fail-fast: false
|
21
35
|
matrix:
|
22
36
|
ruby:
|
23
|
-
- 3.
|
37
|
+
- 3.3
|
24
38
|
env:
|
25
39
|
- AR_VERSION: '7.1'
|
26
40
|
RUBYOPT: --enable-frozen-string-literal
|
@@ -29,6 +43,15 @@ jobs:
|
|
29
43
|
- AR_VERSION: 6.1
|
30
44
|
RUBYOPT: --enable-frozen-string-literal
|
31
45
|
include:
|
46
|
+
- ruby: 3.2
|
47
|
+
env:
|
48
|
+
AR_VERSION: '7.1'
|
49
|
+
- ruby: 3.2
|
50
|
+
env:
|
51
|
+
AR_VERSION: '7.0'
|
52
|
+
- ruby: 3.2
|
53
|
+
env:
|
54
|
+
AR_VERSION: 6.1
|
32
55
|
- ruby: 3.1
|
33
56
|
env:
|
34
57
|
AR_VERSION: '7.1'
|
@@ -44,7 +67,7 @@ jobs:
|
|
44
67
|
- ruby: '3.0'
|
45
68
|
env:
|
46
69
|
AR_VERSION: 6.1
|
47
|
-
- ruby: jruby
|
70
|
+
- ruby: jruby-9.4.5.0
|
48
71
|
env:
|
49
72
|
AR_VERSION: '7.0'
|
50
73
|
- ruby: 2.7
|
@@ -81,6 +104,7 @@ jobs:
|
|
81
104
|
with:
|
82
105
|
ruby-version: ${{ matrix.ruby }}
|
83
106
|
bundler-cache: true
|
107
|
+
rubygems: latest
|
84
108
|
- name: Set up databases
|
85
109
|
run: |
|
86
110
|
sudo /etc/init.d/mysql start
|
@@ -110,6 +134,9 @@ jobs:
|
|
110
134
|
run: |
|
111
135
|
bundle exec rake test:spatialite
|
112
136
|
bundle exec rake test:sqlite3
|
137
|
+
- name: Run trilogy tests
|
138
|
+
if: ${{ matrix.env.AR_VERSION >= '7.0' && !startsWith(matrix.ruby, 'jruby') }}
|
139
|
+
run: bundle exec rake test:trilogy
|
113
140
|
lint:
|
114
141
|
runs-on: ubuntu-latest
|
115
142
|
env:
|
data/.gitignore
CHANGED
data/.rubocop_todo.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## Changes in 1.7.0
|
2
|
+
|
3
|
+
### New Features
|
4
|
+
|
5
|
+
* Add support for ActiveRecord 7.1 composite primary keys. Thanks to @fragkakis via \##837.
|
6
|
+
* Add support for upserting associations when doing recursive imports. Thanks to @ramblex via \##778.
|
7
|
+
|
8
|
+
## Changes in 1.6.0
|
9
|
+
|
10
|
+
### New Features
|
11
|
+
|
12
|
+
* Add trilogy adapter support. Thanks to @zmariscal via \##825.
|
13
|
+
|
14
|
+
### Fixes
|
15
|
+
|
16
|
+
* Use the locking_enabled? method provided by activerecord to decide whether the lock field should be updated. Thanks to @dombesz via \##822.
|
17
|
+
|
1
18
|
## Changes in 1.5.1
|
2
19
|
|
3
20
|
### Fixes
|
data/Gemfile
CHANGED
@@ -26,6 +26,10 @@ platforms :ruby do
|
|
26
26
|
gem "sqlite3", "~> #{sqlite3_version}"
|
27
27
|
# seamless_database_pool requires Ruby ~> 2.0
|
28
28
|
gem "seamless_database_pool", "~> 1.0.20" if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0')
|
29
|
+
gem "trilogy" if version >= 6.0
|
30
|
+
if version >= 6.0 && version <= 7.0
|
31
|
+
gem "activerecord-trilogy-adapter"
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
platforms :jruby do
|
@@ -37,7 +41,11 @@ platforms :jruby do
|
|
37
41
|
end
|
38
42
|
|
39
43
|
# Support libs
|
40
|
-
|
44
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0.0")
|
45
|
+
gem "factory_bot"
|
46
|
+
else
|
47
|
+
gem "factory_bot", "~> 5", "< 6.4.5"
|
48
|
+
end
|
41
49
|
gem "timecop"
|
42
50
|
gem "chronic"
|
43
51
|
gem "mocha", "~> 2.1.0"
|
data/README.markdown
CHANGED
@@ -265,7 +265,7 @@ Book.import books, recursive: true
|
|
265
265
|
Key | Options | Default | Description
|
266
266
|
------------------------- | --------------------- | ------------------ | -----------
|
267
267
|
:validate | `true`/`false` | `true` | Whether or not to run `ActiveRecord` validations (uniqueness skipped). This option will always be true when using `import!`.
|
268
|
-
:validate_uniqueness | `true`/`false` | `false` | Whether or not to run uniqueness validations
|
268
|
+
:validate_uniqueness | `true`/`false` | `false` | Whether or not to run ActiveRecord uniqueness validations. Beware this will incur an sql query per-record (N+1 queries). (requires `>= v0.27.0`).
|
269
269
|
:validate_with_context | `Symbol` |`:create`/`:update` | Allows passing an ActiveModel validation context for each model. Default is `:create` for new records and `:update` for existing ones.
|
270
270
|
:track_validation_failures| `true`/`false` | `false` | When this is set to true, `failed_instances` will be an array of arrays, with each inner array having the form `[:index_in_dataset, :object_with_errors]`
|
271
271
|
:on_duplicate_key_ignore | `true`/`false` | `false` | Allows skipping records with duplicate keys. See [here](#duplicate-key-ignore) for more details.
|
@@ -274,6 +274,7 @@ Key | Options | Default | Descrip
|
|
274
274
|
:synchronize | `Array` | N/A | An array of ActiveRecord instances. This synchronizes existing instances in memory with updates from the import.
|
275
275
|
:timestamps | `true`/`false` | `true` | Enables/disables timestamps on imported records.
|
276
276
|
:recursive | `true`/`false` | `false` | Imports has_many/has_one associations (PostgreSQL only).
|
277
|
+
:recursive_on_duplicate_key_update | `Hash` | N/A | Allows upsert logic to be used for recursive associations. The hash key is the association name and the value has the same options as `:on_duplicate_key_update`. See [here](#duplicate-key-update) for more details.
|
277
278
|
:batch_size | `Integer` | total # of records | Max number of records to insert per import
|
278
279
|
:raise_error | `true`/`false` | `false` | Raises an exception at the first invalid record. This means there will not be a result object returned. The `import!` method is a shortcut for this.
|
279
280
|
:all_or_none | `true`/`false` | `false` | Will not import any records if there is a record with validation errors.
|
@@ -364,6 +365,29 @@ book.reload.title # => "Book1" (stayed the same)
|
|
364
365
|
book.reload.author # => "Bob Barker" (changed)
|
365
366
|
```
|
366
367
|
|
368
|
+
PostgreSQL Using partial indexes
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
book = Book.create! title: "Book1", author: "George Orwell", published_at: Time.now
|
372
|
+
book.author = "Bob Barker"
|
373
|
+
|
374
|
+
# in migration
|
375
|
+
execute <<-SQL
|
376
|
+
CREATE INDEX books_published_at_index ON books (published_at) WHERE published_at IS NOT NULL;
|
377
|
+
SQL
|
378
|
+
|
379
|
+
# PostgreSQL version
|
380
|
+
Book.import [book], on_duplicate_key_update: {
|
381
|
+
conflict_target: [:id],
|
382
|
+
index_predicate: "published_at IS NOT NULL",
|
383
|
+
columns: [:author]
|
384
|
+
}
|
385
|
+
|
386
|
+
book.reload.title # => "Book1" (stayed the same)
|
387
|
+
book.reload.author # => "Bob Barker" (changed)
|
388
|
+
book.reload.published_at # => 2017-10-09 (stayed the same)
|
389
|
+
```
|
390
|
+
|
367
391
|
PostgreSQL Using constraints
|
368
392
|
|
369
393
|
```ruby
|
data/Rakefile
CHANGED
@@ -557,7 +557,7 @@ class ActiveRecord::Base
|
|
557
557
|
options.merge!( args.pop ) if args.last.is_a? Hash
|
558
558
|
# making sure that current model's primary key is used
|
559
559
|
options[:primary_key] = primary_key
|
560
|
-
options[:locking_column] = locking_column if
|
560
|
+
options[:locking_column] = locking_column if locking_enabled?
|
561
561
|
|
562
562
|
is_validating = options[:validate_with_context].present? ? true : options[:validate]
|
563
563
|
validator = ActiveRecord::Import::Validator.new(self, options)
|
@@ -857,6 +857,15 @@ class ActiveRecord::Base
|
|
857
857
|
|
858
858
|
private
|
859
859
|
|
860
|
+
def associated_options(options, associated_class)
|
861
|
+
return options unless options.key?(:recursive_on_duplicate_key_update)
|
862
|
+
|
863
|
+
table_name = associated_class.arel_table.name.to_sym
|
864
|
+
options.merge(
|
865
|
+
on_duplicate_key_update: options[:recursive_on_duplicate_key_update][table_name]
|
866
|
+
)
|
867
|
+
end
|
868
|
+
|
860
869
|
def set_attributes_and_mark_clean(models, import_result, timestamps, options)
|
861
870
|
return if models.nil?
|
862
871
|
models -= import_result.failed_instances
|
@@ -941,7 +950,7 @@ class ActiveRecord::Base
|
|
941
950
|
association = association.target
|
942
951
|
next if association.blank? || model.public_send(column_name).present?
|
943
952
|
|
944
|
-
association_primary_key = Array(association_reflection.association_primary_key)[column_index]
|
953
|
+
association_primary_key = Array(association_reflection.association_primary_key.tr("[]:", "").split(", "))[column_index]
|
945
954
|
model.public_send("#{column_name}=", association.send(association_primary_key))
|
946
955
|
end
|
947
956
|
end
|
@@ -963,7 +972,11 @@ class ActiveRecord::Base
|
|
963
972
|
|
964
973
|
associated_objects_by_class.each_value do |associations|
|
965
974
|
associations.each_value do |associated_records|
|
966
|
-
|
975
|
+
next if associated_records.empty?
|
976
|
+
|
977
|
+
associated_class = associated_records.first.class
|
978
|
+
associated_class.bulk_import(associated_records,
|
979
|
+
associated_options(options, associated_class))
|
967
980
|
end
|
968
981
|
end
|
969
982
|
end
|
@@ -996,7 +1009,10 @@ class ActiveRecord::Base
|
|
996
1009
|
|
997
1010
|
changed_objects = association.select { |a| a.new_record? || a.changed? }
|
998
1011
|
changed_objects.each do |child|
|
999
|
-
|
1012
|
+
Array(association_reflection.inverse_of&.foreign_key || association_reflection.foreign_key).each_with_index do |column, index|
|
1013
|
+
child.public_send("#{column}=", Array(model.id)[index])
|
1014
|
+
end
|
1015
|
+
|
1000
1016
|
# For polymorphic associations
|
1001
1017
|
association_name = if model.class.respond_to?(:polymorphic_name)
|
1002
1018
|
model.class.polymorphic_name
|
data/lib/activerecord-import.rb
CHANGED
data/test/database.yml.sample
CHANGED
data/test/github/database.yml
CHANGED
data/test/models/book.rb
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
class Book < ActiveRecord::Base
|
4
4
|
belongs_to :topic, inverse_of: :books
|
5
|
-
|
6
|
-
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :tag, foreign_key: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
7
|
+
else
|
8
|
+
belongs_to :tag, query_constraints: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
9
|
+
end
|
7
10
|
has_many :chapters, inverse_of: :book
|
8
11
|
has_many :discounts, as: :discountable
|
9
12
|
has_many :end_notes, inverse_of: :book
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CompositeBook < ActiveRecord::Base
|
4
|
+
self.primary_key = %i[id author_id]
|
5
|
+
belongs_to :author
|
6
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
7
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
8
|
+
has_many :composite_chapters, inverse_of: :composite_book,
|
9
|
+
foreign_key: [:id, :author_id]
|
10
|
+
end
|
11
|
+
else
|
12
|
+
has_many :composite_chapters, inverse_of: :composite_book,
|
13
|
+
query_constraints: [:id, :author_id]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.sequence_name
|
17
|
+
"composite_book_id_seq"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CompositeChapter < ActiveRecord::Base
|
4
|
+
if ENV['AR_VERSION'].to_f >= 7.1
|
5
|
+
belongs_to :composite_book, inverse_of: :composite_chapters,
|
6
|
+
query_constraints: [:composite_book_id, :author_id]
|
7
|
+
end
|
8
|
+
validates :title, presence: true
|
9
|
+
end
|
data/test/models/customer.rb
CHANGED
@@ -2,9 +2,17 @@
|
|
2
2
|
|
3
3
|
class Customer < ActiveRecord::Base
|
4
4
|
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
has_many :orders,
|
7
|
+
inverse_of: :customer,
|
8
|
+
primary_key: %i(account_id id),
|
9
|
+
foreign_key: %i(account_id customer_id)
|
10
|
+
else
|
11
|
+
has_many :orders,
|
12
|
+
inverse_of: :customer,
|
13
|
+
primary_key: %i(account_id id),
|
14
|
+
query_constraints: %i(account_id customer_id)
|
15
|
+
end
|
16
|
+
|
9
17
|
end
|
10
18
|
end
|
data/test/models/order.rb
CHANGED
@@ -2,9 +2,16 @@
|
|
2
2
|
|
3
3
|
class Order < ActiveRecord::Base
|
4
4
|
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :customer,
|
7
|
+
inverse_of: :orders,
|
8
|
+
primary_key: %i(account_id id),
|
9
|
+
foreign_key: %i(account_id customer_id)
|
10
|
+
else
|
11
|
+
belongs_to :customer,
|
12
|
+
inverse_of: :orders,
|
13
|
+
primary_key: %i(account_id id),
|
14
|
+
query_constraints: %i(account_id customer_id)
|
15
|
+
end
|
9
16
|
end
|
10
17
|
end
|
data/test/models/tag.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Tag < ActiveRecord::Base
|
4
|
-
|
4
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
5
|
+
self.primary_keys = :tag_id, :publisher_id unless ENV["SKIP_COMPOSITE_PK"]
|
6
|
+
else
|
7
|
+
self.primary_key = [:tag_id, :publisher_id] unless ENV["SKIP_COMPOSITE_PK"]
|
8
|
+
end
|
9
|
+
self.primary_key = [:tag_id, :publisher_id] unless ENV["SKIP_COMPOSITE_PK"]
|
5
10
|
has_many :books, inverse_of: :tag
|
6
11
|
has_many :tag_aliases, inverse_of: :tag
|
7
12
|
end
|
data/test/models/tag_alias.rb
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
class TagAlias < ActiveRecord::Base
|
4
4
|
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
-
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
belongs_to :tag, foreign_key: [:tag_id, :parent_id], required: true
|
7
|
+
else
|
8
|
+
belongs_to :tag, query_constraints: [:tag_id, :parent_id], required: true
|
9
|
+
end
|
6
10
|
end
|
7
11
|
end
|
@@ -57,4 +57,38 @@ ActiveRecord::Schema.define do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
add_index :alarms, [:device_id, :alarm_type], unique: true, where: 'status <> 0'
|
60
|
+
|
61
|
+
unless ENV["SKIP_COMPOSITE_PK"]
|
62
|
+
create_table :authors, force: :cascade do |t|
|
63
|
+
t.string :name
|
64
|
+
end
|
65
|
+
|
66
|
+
execute %(
|
67
|
+
DROP SEQUENCE IF EXISTS composite_book_id_seq CASCADE;
|
68
|
+
CREATE SEQUENCE composite_book_id_seq
|
69
|
+
AS integer
|
70
|
+
START WITH 1
|
71
|
+
INCREMENT BY 1
|
72
|
+
NO MINVALUE
|
73
|
+
NO MAXVALUE
|
74
|
+
CACHE 1;
|
75
|
+
|
76
|
+
DROP TABLE IF EXISTS composite_books;
|
77
|
+
CREATE TABLE composite_books (
|
78
|
+
id bigint DEFAULT nextval('composite_book_id_seq'::regclass) NOT NULL,
|
79
|
+
title character varying,
|
80
|
+
author_id bigint
|
81
|
+
);
|
82
|
+
|
83
|
+
ALTER TABLE ONLY composite_books ADD CONSTRAINT fk_rails_040a418131 FOREIGN KEY (author_id) REFERENCES authors(id);
|
84
|
+
).split.join(' ').strip
|
85
|
+
end
|
86
|
+
|
87
|
+
create_table :composite_chapters, force: :cascade do |t|
|
88
|
+
t.string :title
|
89
|
+
t.integer :composite_book_id, null: false
|
90
|
+
t.integer :author_id, null: false
|
91
|
+
t.datetime :created_at
|
92
|
+
t.datetime :updated_at
|
93
|
+
end
|
60
94
|
end
|
@@ -351,6 +351,18 @@ def should_support_postgresql_import_functionality
|
|
351
351
|
assert_equal db_customer.orders.last, db_order
|
352
352
|
assert_not_equal db_order.customer_id, nil
|
353
353
|
end
|
354
|
+
|
355
|
+
it "should import models with auto-incrementing ID successfully" do
|
356
|
+
author = Author.create!(name: "Foo Barson")
|
357
|
+
|
358
|
+
books = []
|
359
|
+
2.times do |i|
|
360
|
+
books << CompositeBook.new(author_id: author.id, title: "book #{i}")
|
361
|
+
end
|
362
|
+
assert_difference "CompositeBook.count", +2 do
|
363
|
+
CompositeBook.import books
|
364
|
+
end
|
365
|
+
end
|
354
366
|
end
|
355
367
|
end
|
356
368
|
end
|
@@ -223,6 +223,34 @@ def should_support_basic_on_duplicate_key_update
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
end
|
226
|
+
|
227
|
+
context 'with locking disabled' do
|
228
|
+
it 'does not update the lock_version' do
|
229
|
+
users = [
|
230
|
+
User.new(name: 'Salomon'),
|
231
|
+
User.new(name: 'Nathan')
|
232
|
+
]
|
233
|
+
User.import(users)
|
234
|
+
assert User.count == users.length
|
235
|
+
User.all.each do |user|
|
236
|
+
assert_equal 0, user.lock_version
|
237
|
+
end
|
238
|
+
updated_users = User.all.map do |user|
|
239
|
+
user.name += ' Rothschild'
|
240
|
+
user
|
241
|
+
end
|
242
|
+
|
243
|
+
ActiveRecord::Base.lock_optimistically = false # Disable locking
|
244
|
+
User.import(updated_users, on_duplicate_key_update: [:name])
|
245
|
+
ActiveRecord::Base.lock_optimistically = true # Enable locking
|
246
|
+
|
247
|
+
assert User.count == updated_users.length
|
248
|
+
User.all.each_with_index do |user, i|
|
249
|
+
assert_equal user.name, "#{users[i].name} Rothschild"
|
250
|
+
assert_equal 0, user.lock_version
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
226
254
|
end
|
227
255
|
|
228
256
|
context "with :on_duplicate_key_update" do
|
@@ -165,6 +165,24 @@ def should_support_recursive_import
|
|
165
165
|
assert_equal 1, tags[0].tag_id
|
166
166
|
assert_equal 2, tags[1].tag_id
|
167
167
|
end
|
168
|
+
|
169
|
+
if ENV['AR_VERSION'].to_f >= 7.1
|
170
|
+
it "should import models with auto-incrementing ID successfully with recursive set to true" do
|
171
|
+
author = Author.create!(name: "Foo Barson")
|
172
|
+
books = []
|
173
|
+
2.times do |i|
|
174
|
+
books << CompositeBook.new(author_id: author.id, title: "Book #{i}", composite_chapters: [
|
175
|
+
CompositeChapter.new(title: "Book #{i} composite chapter 1"),
|
176
|
+
CompositeChapter.new(title: "Book #{i} composite chapter 2"),
|
177
|
+
])
|
178
|
+
end
|
179
|
+
assert_difference "CompositeBook.count", +2 do
|
180
|
+
assert_difference "CompositeChapter.count", +4 do
|
181
|
+
CompositeBook.import books, recursive: true
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
168
186
|
end
|
169
187
|
end
|
170
188
|
|
@@ -213,6 +231,54 @@ def should_support_recursive_import
|
|
213
231
|
end
|
214
232
|
end
|
215
233
|
end
|
234
|
+
|
235
|
+
describe "recursive_on_duplicate_key_update" do
|
236
|
+
let(:new_topics) { Build(1, :topic_with_book) }
|
237
|
+
|
238
|
+
setup do
|
239
|
+
Topic.import new_topics, recursive: true
|
240
|
+
end
|
241
|
+
|
242
|
+
it "updates associated objects" do
|
243
|
+
new_author_name = 'Richard Bachman'
|
244
|
+
topic = new_topics.first
|
245
|
+
topic.books.each do |book|
|
246
|
+
book.author_name = new_author_name
|
247
|
+
end
|
248
|
+
|
249
|
+
assert_nothing_raised do
|
250
|
+
Topic.import new_topics,
|
251
|
+
recursive: true,
|
252
|
+
on_duplicate_key_update: [:id],
|
253
|
+
recursive_on_duplicate_key_update: {
|
254
|
+
books: { conflict_target: [:id], columns: [:author_name] }
|
255
|
+
}
|
256
|
+
end
|
257
|
+
Topic.find(topic.id).books.each do |book|
|
258
|
+
assert_equal new_author_name, book.author_name
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
it "updates nested associated objects" do
|
263
|
+
new_chapter_title = 'The Final Chapter'
|
264
|
+
book = new_topics.first.books.first
|
265
|
+
book.author_name = 'Richard Bachman'
|
266
|
+
|
267
|
+
example_chapter = book.chapters.first
|
268
|
+
example_chapter.title = new_chapter_title
|
269
|
+
|
270
|
+
assert_nothing_raised do
|
271
|
+
Topic.import new_topics,
|
272
|
+
recursive: true,
|
273
|
+
on_duplicate_key_update: [:id],
|
274
|
+
recursive_on_duplicate_key_update: {
|
275
|
+
books: { conflict_target: [:id], columns: [:author_name] },
|
276
|
+
chapters: { conflict_target: [:id], columns: [:title] }
|
277
|
+
}
|
278
|
+
end
|
279
|
+
assert_equal new_chapter_title, Chapter.find(example_chapter.id).title
|
280
|
+
end
|
281
|
+
end
|
216
282
|
end
|
217
283
|
|
218
284
|
# If returning option is provided, it is only applied to top level models so that SQL with invalid
|
data/test/test_helper.rb
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../test_helper")
|
4
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/assertions")
|
5
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../support/mysql/import_examples")
|
6
|
+
|
7
|
+
should_support_mysql_import_functionality
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-import
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -87,12 +87,14 @@ files:
|
|
87
87
|
- lib/activerecord-import/active_record/adapters/postgresql_adapter.rb
|
88
88
|
- lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb
|
89
89
|
- lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb
|
90
|
+
- lib/activerecord-import/active_record/adapters/trilogy_adapter.rb
|
90
91
|
- lib/activerecord-import/adapters/abstract_adapter.rb
|
91
92
|
- lib/activerecord-import/adapters/em_mysql2_adapter.rb
|
92
93
|
- lib/activerecord-import/adapters/mysql2_adapter.rb
|
93
94
|
- lib/activerecord-import/adapters/mysql_adapter.rb
|
94
95
|
- lib/activerecord-import/adapters/postgresql_adapter.rb
|
95
96
|
- lib/activerecord-import/adapters/sqlite3_adapter.rb
|
97
|
+
- lib/activerecord-import/adapters/trilogy_adapter.rb
|
96
98
|
- lib/activerecord-import/base.rb
|
97
99
|
- lib/activerecord-import/import.rb
|
98
100
|
- lib/activerecord-import/mysql2.rb
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- test/adapters/seamless_database_pool.rb
|
115
117
|
- test/adapters/spatialite.rb
|
116
118
|
- test/adapters/sqlite3.rb
|
119
|
+
- test/adapters/trilogy.rb
|
117
120
|
- test/database.yml.sample
|
118
121
|
- test/github/database.yml
|
119
122
|
- test/import_test.rb
|
@@ -124,11 +127,14 @@ files:
|
|
124
127
|
- test/models/account.rb
|
125
128
|
- test/models/alarm.rb
|
126
129
|
- test/models/animal.rb
|
130
|
+
- test/models/author.rb
|
127
131
|
- test/models/bike_maker.rb
|
128
132
|
- test/models/book.rb
|
129
133
|
- test/models/car.rb
|
130
134
|
- test/models/card.rb
|
131
135
|
- test/models/chapter.rb
|
136
|
+
- test/models/composite_book.rb
|
137
|
+
- test/models/composite_chapter.rb
|
132
138
|
- test/models/customer.rb
|
133
139
|
- test/models/deck.rb
|
134
140
|
- test/models/dictionary.rb
|
@@ -172,13 +178,14 @@ files:
|
|
172
178
|
- test/support/sqlite3/import_examples.rb
|
173
179
|
- test/synchronize_test.rb
|
174
180
|
- test/test_helper.rb
|
181
|
+
- test/trilogy/import_test.rb
|
175
182
|
- test/value_sets_bytes_parser_test.rb
|
176
183
|
- test/value_sets_records_parser_test.rb
|
177
184
|
homepage: https://github.com/zdennis/activerecord-import
|
178
185
|
licenses:
|
179
186
|
- MIT
|
180
187
|
metadata: {}
|
181
|
-
post_install_message:
|
188
|
+
post_install_message:
|
182
189
|
rdoc_options: []
|
183
190
|
require_paths:
|
184
191
|
- lib
|
@@ -193,8 +200,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
200
|
- !ruby/object:Gem::Version
|
194
201
|
version: '0'
|
195
202
|
requirements: []
|
196
|
-
rubygems_version: 3.3.
|
197
|
-
signing_key:
|
203
|
+
rubygems_version: 3.0.3.1
|
204
|
+
signing_key:
|
198
205
|
specification_version: 4
|
199
206
|
summary: Bulk insert extension for ActiveRecord
|
200
207
|
test_files:
|
@@ -211,6 +218,7 @@ test_files:
|
|
211
218
|
- test/adapters/seamless_database_pool.rb
|
212
219
|
- test/adapters/spatialite.rb
|
213
220
|
- test/adapters/sqlite3.rb
|
221
|
+
- test/adapters/trilogy.rb
|
214
222
|
- test/database.yml.sample
|
215
223
|
- test/github/database.yml
|
216
224
|
- test/import_test.rb
|
@@ -221,11 +229,14 @@ test_files:
|
|
221
229
|
- test/models/account.rb
|
222
230
|
- test/models/alarm.rb
|
223
231
|
- test/models/animal.rb
|
232
|
+
- test/models/author.rb
|
224
233
|
- test/models/bike_maker.rb
|
225
234
|
- test/models/book.rb
|
226
235
|
- test/models/car.rb
|
227
236
|
- test/models/card.rb
|
228
237
|
- test/models/chapter.rb
|
238
|
+
- test/models/composite_book.rb
|
239
|
+
- test/models/composite_chapter.rb
|
229
240
|
- test/models/customer.rb
|
230
241
|
- test/models/deck.rb
|
231
242
|
- test/models/dictionary.rb
|
@@ -269,5 +280,6 @@ test_files:
|
|
269
280
|
- test/support/sqlite3/import_examples.rb
|
270
281
|
- test/synchronize_test.rb
|
271
282
|
- test/test_helper.rb
|
283
|
+
- test/trilogy/import_test.rb
|
272
284
|
- test/value_sets_bytes_parser_test.rb
|
273
285
|
- test/value_sets_records_parser_test.rb
|