activerecord-import 1.0.0 → 1.0.5
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/.travis.yml +17 -14
- data/CHANGELOG.md +65 -0
- data/Gemfile +4 -1
- data/LICENSE +21 -56
- data/README.markdown +54 -59
- data/activerecord-import.gemspec +2 -2
- data/gemfiles/6.0.gemfile +1 -0
- data/gemfiles/6.1.gemfile +1 -0
- data/lib/activerecord-import/adapters/abstract_adapter.rb +8 -2
- data/lib/activerecord-import/adapters/mysql_adapter.rb +4 -5
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +11 -12
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +15 -20
- data/lib/activerecord-import/base.rb +8 -1
- data/lib/activerecord-import/import.rb +41 -36
- data/lib/activerecord-import/synchronize.rb +1 -1
- data/lib/activerecord-import/version.rb +1 -1
- data/test/import_test.rb +5 -0
- data/test/schema/postgresql_schema.rb +16 -0
- data/test/support/postgresql/import_examples.rb +44 -0
- data/test/support/shared_examples/on_duplicate_key_update.rb +10 -0
- data/test/support/shared_examples/recursive_import.rb +38 -0
- data/test/support/sqlite3/import_examples.rb +2 -15
- metadata +7 -6
@@ -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)
|
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 []
|
@@ -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
|
@@ -116,6 +116,26 @@ def should_support_postgresql_import_functionality
|
|
116
116
|
assert_equal [%w(King It)], result.results
|
117
117
|
end
|
118
118
|
|
119
|
+
context "when given an empty array" do
|
120
|
+
let(:result) { Book.import([], returning: %w(title)) }
|
121
|
+
|
122
|
+
setup { result }
|
123
|
+
|
124
|
+
it "returns empty arrays for ids and results" do
|
125
|
+
assert_equal [], result.ids
|
126
|
+
assert_equal [], result.results
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when a returning column is a serialized attribute" do
|
131
|
+
let(:vendor) { Vendor.new(hours: { monday: '8-5' }) }
|
132
|
+
let(:result) { Vendor.import([vendor], returning: %w(hours)) }
|
133
|
+
|
134
|
+
it "creates records" do
|
135
|
+
assert_difference("Vendor.count", +1) { result }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
119
139
|
context "when primary key and returning overlap" do
|
120
140
|
let(:result) { Book.import(books, returning: %w(id title)) }
|
121
141
|
|
@@ -234,6 +254,30 @@ def should_support_postgresql_import_functionality
|
|
234
254
|
assert_equal({}, Vendor.first.json_data)
|
235
255
|
end
|
236
256
|
end
|
257
|
+
|
258
|
+
%w(json jsonb).each do |json_type|
|
259
|
+
describe "with pure #{json_type} fields" do
|
260
|
+
let(:data) { { a: :b } }
|
261
|
+
let(:json_field_name) { "pure_#{json_type}_data" }
|
262
|
+
it "imports the values from saved records" do
|
263
|
+
vendor = Vendor.create!(name: 'Vendor 1', json_field_name => data)
|
264
|
+
|
265
|
+
Vendor.import [vendor], on_duplicate_key_update: [json_field_name]
|
266
|
+
assert_equal(data.as_json, vendor.reload[json_field_name])
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "with enum field" do
|
273
|
+
let(:vendor_type) { "retailer" }
|
274
|
+
it "imports the correct values for enum fields" do
|
275
|
+
vendor = Vendor.new(name: 'Vendor 1', vendor_type: vendor_type)
|
276
|
+
assert_difference "Vendor.count", +1 do
|
277
|
+
Vendor.import [vendor]
|
278
|
+
end
|
279
|
+
assert_equal(vendor_type, Vendor.first.vendor_type)
|
280
|
+
end
|
237
281
|
end
|
238
282
|
|
239
283
|
describe "with binary field" do
|
@@ -73,6 +73,16 @@ def should_support_basic_on_duplicate_key_update
|
|
73
73
|
assert_equal user.name, users[i].name + ' Rothschild'
|
74
74
|
assert_equal 1, user.lock_version
|
75
75
|
end
|
76
|
+
updated_values2 = User.all.map do |user|
|
77
|
+
user.name += ' jr.'
|
78
|
+
{ id: user.id, name: user.name }
|
79
|
+
end
|
80
|
+
User.import(updated_values2, on_duplicate_key_update: [:name])
|
81
|
+
assert User.count == updated_values2.length
|
82
|
+
User.all.each_with_index do |user, i|
|
83
|
+
assert_equal user.name, users[i].name + ' Rothschild jr.'
|
84
|
+
assert_equal 2, user.lock_version
|
85
|
+
end
|
76
86
|
end
|
77
87
|
|
78
88
|
it 'upsert optimistic lock columns other than lock_version by model' do
|
@@ -138,6 +138,15 @@ def should_support_recursive_import
|
|
138
138
|
books.each do |book|
|
139
139
|
assert_equal book.topic_id, second_new_topic.id
|
140
140
|
end
|
141
|
+
|
142
|
+
books.each { |book| book.topic_id = nil }
|
143
|
+
assert_no_difference "Book.count", books.size do
|
144
|
+
Book.import books, validate: false, on_duplicate_key_update: [:topic_id]
|
145
|
+
end
|
146
|
+
|
147
|
+
books.each do |book|
|
148
|
+
assert_equal book.topic_id, nil
|
149
|
+
end
|
141
150
|
end
|
142
151
|
|
143
152
|
unless ENV["SKIP_COMPOSITE_PK"]
|
@@ -183,5 +192,34 @@ def should_support_recursive_import
|
|
183
192
|
end
|
184
193
|
end
|
185
194
|
end
|
195
|
+
|
196
|
+
# If returning option is provided, it is only applied to top level models so that SQL with invalid
|
197
|
+
# columns, keys, etc isn't generated for child associations when doing recursive import
|
198
|
+
describe "returning" do
|
199
|
+
let(:new_topics) { Build(1, :topic_with_book) }
|
200
|
+
|
201
|
+
it "imports objects with associations" do
|
202
|
+
assert_difference "Topic.count", +1 do
|
203
|
+
Topic.import new_topics, recursive: true, returning: [:content], validate: false
|
204
|
+
new_topics.each do |topic|
|
205
|
+
assert_not_nil topic.id
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# If no returning option is provided, it is ignored
|
212
|
+
describe "no returning" do
|
213
|
+
let(:new_topics) { Build(1, :topic_with_book) }
|
214
|
+
|
215
|
+
it "is ignored and imports objects with associations" do
|
216
|
+
assert_difference "Topic.count", +1 do
|
217
|
+
Topic.import new_topics, recursive: true, no_returning: true, validate: false
|
218
|
+
new_topics.each do |topic|
|
219
|
+
assert_not_nil topic.id
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
186
224
|
end
|
187
225
|
end
|
@@ -5,21 +5,8 @@ def should_support_sqlite3_import_functionality
|
|
5
5
|
end
|
6
6
|
|
7
7
|
describe "#supports_imports?" do
|
8
|
-
|
9
|
-
|
10
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11")
|
11
|
-
assert ActiveRecord::Base.supports_import?(version)
|
12
|
-
|
13
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12")
|
14
|
-
assert ActiveRecord::Base.supports_import?(version)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "and SQLite less than 3.7.11" do
|
19
|
-
it "doesn't support import" do
|
20
|
-
version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10")
|
21
|
-
assert !ActiveRecord::Base.supports_import?(version)
|
22
|
-
end
|
8
|
+
it "should support import" do
|
9
|
+
assert ActiveRecord::Base.supports_import?
|
23
10
|
end
|
24
11
|
end
|
25
12
|
|
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.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -75,6 +75,8 @@ files:
|
|
75
75
|
- gemfiles/5.0.gemfile
|
76
76
|
- gemfiles/5.1.gemfile
|
77
77
|
- gemfiles/5.2.gemfile
|
78
|
+
- gemfiles/6.0.gemfile
|
79
|
+
- gemfiles/6.1.gemfile
|
78
80
|
- lib/activerecord-import.rb
|
79
81
|
- lib/activerecord-import/active_record/adapters/abstract_adapter.rb
|
80
82
|
- lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb
|
@@ -166,7 +168,7 @@ files:
|
|
166
168
|
- test/value_sets_records_parser_test.rb
|
167
169
|
homepage: http://github.com/zdennis/activerecord-import
|
168
170
|
licenses:
|
169
|
-
-
|
171
|
+
- MIT
|
170
172
|
metadata: {}
|
171
173
|
post_install_message:
|
172
174
|
rdoc_options: []
|
@@ -176,15 +178,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
178
|
requirements:
|
177
179
|
- - ">="
|
178
180
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
181
|
+
version: 2.0.0
|
180
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
183
|
requirements:
|
182
184
|
- - ">="
|
183
185
|
- !ruby/object:Gem::Version
|
184
186
|
version: '0'
|
185
187
|
requirements: []
|
186
|
-
|
187
|
-
rubygems_version: 2.7.7
|
188
|
+
rubygems_version: 3.0.6
|
188
189
|
signing_key:
|
189
190
|
specification_version: 4
|
190
191
|
summary: Bulk insert extension for ActiveRecord
|