activerecord-import 1.0.0 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|