abstract_importer 1.5.6 → 1.6.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 +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/CHANGELOG.md +2 -0
- data/README.md +17 -0
- data/abstract_importer.gemspec +2 -3
- data/lib/abstract_importer/strategies/insert_strategy.rb +19 -4
- data/lib/abstract_importer/strategies/upsert_strategy.rb +2 -2
- data/lib/abstract_importer/version.rb +1 -1
- data/test/insert_strategy_test.rb +17 -1
- data/test/upsert_strategy_test.rb +1 -1
- metadata +8 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f1a26d52ad8e17f1b2539d5abbcf996a4c80ac157139a988fefd4a417c4fe03e
|
4
|
+
data.tar.gz: 388a9123f67dd9b52c35ae50a8added3c6995d2b8d71d703269dd317e13e8918
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19531db43b5cd36a2831384fcef16092930b23cc9fa622c1aaa1b1bdc193d73df0d824deda4bdc5ce427d80272bd1d48cd972efea15011187fffa1c5720cb4cf
|
7
|
+
data.tar.gz: 14d9f5e0d630fc2e15be68bd83ef61ca610bf4cec4442af820cfa429915020320d88416af4c8f2fc5d7779daa8b83900c7b0be7e4a87bc21aa73985dd0a58c17
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3
|
1
|
+
2.6.3
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -187,6 +187,23 @@ The following alternate strategies are built in:
|
|
187
187
|
|
188
188
|
Replaces records that have already been imported rather than skipping them.
|
189
189
|
|
190
|
+
##### insert
|
191
|
+
|
192
|
+
Bulk inserts records rather than creating them one-by-one as ActiveRecord objects, skipping those that have already been imported.
|
193
|
+
|
194
|
+
##### upsert
|
195
|
+
|
196
|
+
Bulk inserts records, but updates records that have already been imported rather than skipping them.
|
197
|
+
|
198
|
+
|
199
|
+
|
200
|
+
### Important Note on Upgrading from 1.5.x to 1.6.0
|
201
|
+
|
202
|
+
With the jump to 1.6.0, `abstract_importer` has dropped reliance upon the `activerecord-insert_many` gem in favor of taking advantage of Rails 6's built-in `insert_all` and `upsert_all` for the insert and upsert strategies. With the move from the gem to relying on Rails, the syntax to specify what constitutes a conflict/duplicate record has been updated to align more closely with that of Rails. In particular:
|
203
|
+
|
204
|
+
* Instead of specifying an `on_conflict: { do: :update }` clause, you should specify `on_duplicate: :update` to opt-in to upsert behavior.
|
205
|
+
* Rather than listing columns in `on_conflict: { columns: %i{legacy_id id} }`, you should use `unique_by: %i{legacy_id id}` to specify the index or columns by which a duplicate or conflict is defined. The option follows the behavior of Rails' `upsert_all`, so refer to the Rails 6 documentation for more information.
|
206
|
+
|
190
207
|
|
191
208
|
|
192
209
|
## Contributing
|
data/abstract_importer.gemspec
CHANGED
@@ -17,9 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "activerecord", ">=
|
21
|
-
spec.add_dependency "activesupport", ">=
|
22
|
-
spec.add_dependency "activerecord-insert_many", ">= 0.4.4"
|
20
|
+
spec.add_dependency "activerecord", ">= 6.0"
|
21
|
+
spec.add_dependency "activesupport", ">= 6.0"
|
23
22
|
spec.add_dependency "progressbar"
|
24
23
|
|
25
24
|
spec.add_development_dependency "bundler"
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "abstract_importer/strategies/base"
|
2
|
-
require "activerecord/insert_many"
|
3
2
|
|
4
3
|
module AbstractImporter
|
5
4
|
module Strategies
|
@@ -9,7 +8,8 @@ module AbstractImporter
|
|
9
8
|
super
|
10
9
|
@batch = []
|
11
10
|
@batch_size = options.fetch(:batch_size, 250)
|
12
|
-
@
|
11
|
+
@bulk_operation = options[:on_duplicate] == :update ? :upsert_all : :insert_all
|
12
|
+
@insert_options = options.slice(:unique_by)
|
13
13
|
@insert_options.merge!(returning: [:legacy_id, :id]) if remap_ids?
|
14
14
|
end
|
15
15
|
|
@@ -52,7 +52,9 @@ module AbstractImporter
|
|
52
52
|
|
53
53
|
|
54
54
|
def insert_batch(batch)
|
55
|
-
|
55
|
+
return if batch.empty?
|
56
|
+
|
57
|
+
result = collection.scope.public_send(@bulk_operation, batch, @insert_options)
|
56
58
|
add_batch_to_id_map(result) if remap_ids?
|
57
59
|
end
|
58
60
|
|
@@ -66,13 +68,26 @@ module AbstractImporter
|
|
66
68
|
|
67
69
|
|
68
70
|
def add_batch_to_id_map(result)
|
69
|
-
map = result.each_with_object({}) do |attrs, map|
|
71
|
+
map = cast_result(result, collection.table_name).each_with_object({}) do |attrs, map|
|
70
72
|
map[attrs.fetch("legacy_id")] = attrs.fetch("id")
|
71
73
|
end
|
72
74
|
id_map.merge! collection.table_name, map
|
73
75
|
end
|
74
76
|
|
75
77
|
|
78
|
+
def cast_result(result, table_name)
|
79
|
+
types_by_column = result.columns.each_with_object({}) do |column_name, types|
|
80
|
+
types[column_name] = collection.scope.connection.lookup_cast_type_from_column(collection.scope.columns.find { |column| column.name == column_name })
|
81
|
+
end
|
82
|
+
|
83
|
+
result.to_a.map { |row|
|
84
|
+
Hash[row.map { |column_name, value|
|
85
|
+
[ column_name, types_by_column[column_name].deserialize(value) ]
|
86
|
+
}]
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
|
76
91
|
end
|
77
92
|
end
|
78
93
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require "abstract_importer/strategies/insert_strategy"
|
2
|
-
require "activerecord/insert_many"
|
3
2
|
|
4
3
|
module AbstractImporter
|
5
4
|
module Strategies
|
@@ -7,7 +6,8 @@ module AbstractImporter
|
|
7
6
|
|
8
7
|
def initialize(collection, options={})
|
9
8
|
super
|
10
|
-
@
|
9
|
+
@bulk_operation = :upsert_all
|
10
|
+
@insert_options.reverse_merge!(unique_by: remap_ids? ? (association_attrs.keys + %i{legacy_id}) : :id)
|
11
11
|
end
|
12
12
|
|
13
13
|
# We won't skip any records for already being imported
|
@@ -17,7 +17,7 @@ class InsertStrategyTest < ActiveSupport::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
should "import the records in batches" do
|
20
|
-
mock.proxy(Student).
|
20
|
+
mock.proxy(Student).insert_all(satisfy { |arg| arg.length == 3 }, anything)
|
21
21
|
import!
|
22
22
|
assert_equal [456, 457, 458], account.students.pluck(:legacy_id)
|
23
23
|
end
|
@@ -54,6 +54,22 @@ class InsertStrategyTest < ActiveSupport::TestCase
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
context "With an empty data source" do
|
58
|
+
setup do
|
59
|
+
plan do |import|
|
60
|
+
import.students
|
61
|
+
end
|
62
|
+
@data_source = OpenStruct.new
|
63
|
+
@data_source.students = []
|
64
|
+
end
|
65
|
+
|
66
|
+
should "still be able to import" do
|
67
|
+
assert_nothing_raised do
|
68
|
+
import!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
57
73
|
context "When records already exist" do
|
58
74
|
setup do
|
59
75
|
plan do |import|
|
@@ -17,7 +17,7 @@ class UpsertStrategyTest < ActiveSupport::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
should "import the records in batches" do
|
20
|
-
mock.proxy(Student).
|
20
|
+
mock.proxy(Student).upsert_all(satisfy { |arg| arg.length == 3 }, anything)
|
21
21
|
import!
|
22
22
|
assert_equal [456, 457, 458], account.students.pluck(:legacy_id)
|
23
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstract_importer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Lail
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-09-
|
11
|
+
date: 2019-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,42 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '6.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: activerecord-insert_many
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 0.4.4
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 0.4.4
|
40
|
+
version: '6.0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: progressbar
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -230,6 +216,7 @@ files:
|
|
230
216
|
- ".gitignore"
|
231
217
|
- ".ruby-version"
|
232
218
|
- ".travis.yml"
|
219
|
+
- CHANGELOG.md
|
233
220
|
- Gemfile
|
234
221
|
- LICENSE.txt
|
235
222
|
- README.md
|
@@ -288,8 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
288
275
|
- !ruby/object:Gem::Version
|
289
276
|
version: '0'
|
290
277
|
requirements: []
|
291
|
-
|
292
|
-
rubygems_version: 2.5.1
|
278
|
+
rubygems_version: 3.0.3
|
293
279
|
signing_key:
|
294
280
|
specification_version: 4
|
295
281
|
summary: Provides services for the mass-import of complex relational data
|