activerecord-import 2.2.0 → 2.3.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.markdown +9 -9
  4. data/lib/activerecord-import/active_record/adapters/mysql2_proxy_adapter.rb +8 -0
  5. data/lib/activerecord-import/active_record/adapters/postgresql_proxy_adapter.rb +8 -0
  6. data/lib/activerecord-import/active_record/adapters/sqlite3_proxy_adapter.rb +8 -0
  7. data/lib/activerecord-import/active_record/adapters/trilogy_proxy_adapter.rb +8 -0
  8. data/lib/activerecord-import/adapters/active_record_proxy_adapter.rb +19 -0
  9. data/lib/activerecord-import/adapters/mysql2_proxy_adapter.rb +9 -0
  10. data/lib/activerecord-import/adapters/mysql_adapter.rb +10 -3
  11. data/lib/activerecord-import/adapters/postgresql_adapter.rb +1 -1
  12. data/lib/activerecord-import/adapters/postgresql_proxy_adapter.rb +9 -0
  13. data/lib/activerecord-import/adapters/sqlite3_proxy_adapter.rb +9 -0
  14. data/lib/activerecord-import/adapters/trilogy_proxy_adapter.rb +9 -0
  15. data/lib/activerecord-import/base.rb +0 -2
  16. data/lib/activerecord-import/import.rb +243 -207
  17. data/lib/activerecord-import/version.rb +1 -1
  18. metadata +13 -208
  19. data/.github/workflows/test.yaml +0 -165
  20. data/.gitignore +0 -37
  21. data/.rubocop.yml +0 -118
  22. data/.rubocop_todo.yml +0 -30
  23. data/Brewfile +0 -5
  24. data/Dockerfile +0 -23
  25. data/Gemfile +0 -63
  26. data/Rakefile +0 -75
  27. data/activerecord-import.gemspec +0 -28
  28. data/benchmarks/README +0 -32
  29. data/benchmarks/benchmark.rb +0 -72
  30. data/benchmarks/lib/base.rb +0 -143
  31. data/benchmarks/lib/cli_parser.rb +0 -111
  32. data/benchmarks/lib/float.rb +0 -17
  33. data/benchmarks/lib/mysql2_benchmark.rb +0 -21
  34. data/benchmarks/lib/output_to_csv.rb +0 -21
  35. data/benchmarks/lib/output_to_html.rb +0 -66
  36. data/benchmarks/models/test_innodb.rb +0 -5
  37. data/benchmarks/models/test_memory.rb +0 -5
  38. data/benchmarks/models/test_myisam.rb +0 -5
  39. data/benchmarks/schema/mysql2_schema.rb +0 -18
  40. data/docker-compose.yml +0 -34
  41. data/gemfiles/5.2.gemfile +0 -4
  42. data/gemfiles/6.0.gemfile +0 -4
  43. data/gemfiles/6.1.gemfile +0 -4
  44. data/gemfiles/7.0.gemfile +0 -4
  45. data/gemfiles/7.1.gemfile +0 -3
  46. data/gemfiles/7.2.gemfile +0 -3
  47. data/gemfiles/8.0.gemfile +0 -3
  48. data/test/adapters/janus_mysql2.rb +0 -3
  49. data/test/adapters/janus_trilogy.rb +0 -11
  50. data/test/adapters/jdbcmysql.rb +0 -3
  51. data/test/adapters/jdbcpostgresql.rb +0 -3
  52. data/test/adapters/jdbcsqlite3.rb +0 -3
  53. data/test/adapters/makara_postgis.rb +0 -3
  54. data/test/adapters/mysql2.rb +0 -3
  55. data/test/adapters/mysql2_makara.rb +0 -3
  56. data/test/adapters/mysql2_proxy.rb +0 -3
  57. data/test/adapters/mysql2spatial.rb +0 -3
  58. data/test/adapters/postgis.rb +0 -3
  59. data/test/adapters/postgresql.rb +0 -3
  60. data/test/adapters/postgresql_makara.rb +0 -3
  61. data/test/adapters/postgresql_proxy.rb +0 -3
  62. data/test/adapters/seamless_database_pool.rb +0 -3
  63. data/test/adapters/spatialite.rb +0 -3
  64. data/test/adapters/sqlite3.rb +0 -3
  65. data/test/adapters/trilogy.rb +0 -11
  66. data/test/database.yml.sample +0 -73
  67. data/test/github/database.yml +0 -85
  68. data/test/import_test.rb +0 -970
  69. data/test/janus_mysql2/import_test.rb +0 -8
  70. data/test/janus_trilogy/import_test.rb +0 -7
  71. data/test/jdbcmysql/import_test.rb +0 -7
  72. data/test/jdbcpostgresql/import_test.rb +0 -6
  73. data/test/jdbcsqlite3/import_test.rb +0 -6
  74. data/test/makara_postgis/import_test.rb +0 -10
  75. data/test/models/account.rb +0 -5
  76. data/test/models/alarm.rb +0 -4
  77. data/test/models/animal.rb +0 -8
  78. data/test/models/author.rb +0 -9
  79. data/test/models/bike_maker.rb +0 -10
  80. data/test/models/book.rb +0 -18
  81. data/test/models/car.rb +0 -5
  82. data/test/models/card.rb +0 -5
  83. data/test/models/chapter.rb +0 -6
  84. data/test/models/composite_book.rb +0 -19
  85. data/test/models/composite_chapter.rb +0 -12
  86. data/test/models/customer.rb +0 -18
  87. data/test/models/deck.rb +0 -8
  88. data/test/models/dictionary.rb +0 -6
  89. data/test/models/discount.rb +0 -5
  90. data/test/models/end_note.rb +0 -6
  91. data/test/models/group.rb +0 -5
  92. data/test/models/order.rb +0 -17
  93. data/test/models/playing_card.rb +0 -4
  94. data/test/models/promotion.rb +0 -5
  95. data/test/models/question.rb +0 -5
  96. data/test/models/rule.rb +0 -5
  97. data/test/models/tag.rb +0 -12
  98. data/test/models/tag_alias.rb +0 -11
  99. data/test/models/topic.rb +0 -31
  100. data/test/models/user.rb +0 -5
  101. data/test/models/user_token.rb +0 -6
  102. data/test/models/vendor.rb +0 -9
  103. data/test/models/widget.rb +0 -33
  104. data/test/mysql2/import_test.rb +0 -7
  105. data/test/mysql2_makara/import_test.rb +0 -8
  106. data/test/mysql2_proxy/import_test.rb +0 -6
  107. data/test/mysqlspatial2/import_test.rb +0 -8
  108. data/test/postgis/import_test.rb +0 -10
  109. data/test/postgresql/import_test.rb +0 -6
  110. data/test/postgresql_proxy/import_test.rb +0 -6
  111. data/test/schema/generic_schema.rb +0 -230
  112. data/test/schema/jdbcpostgresql_schema.rb +0 -3
  113. data/test/schema/mysql2_schema.rb +0 -21
  114. data/test/schema/postgis_schema.rb +0 -3
  115. data/test/schema/postgresql_schema.rb +0 -94
  116. data/test/schema/sqlite3_schema.rb +0 -15
  117. data/test/schema/version.rb +0 -12
  118. data/test/sqlite3/import_test.rb +0 -6
  119. data/test/support/active_support/test_case_extensions.rb +0 -73
  120. data/test/support/assertions.rb +0 -75
  121. data/test/support/factories.rb +0 -66
  122. data/test/support/generate.rb +0 -31
  123. data/test/support/mysql/import_examples.rb +0 -97
  124. data/test/support/postgresql/import_examples.rb +0 -642
  125. data/test/support/shared_examples/on_duplicate_key_ignore.rb +0 -45
  126. data/test/support/shared_examples/on_duplicate_key_update.rb +0 -437
  127. data/test/support/shared_examples/recursive_import.rb +0 -352
  128. data/test/support/sqlite3/import_examples.rb +0 -232
  129. data/test/synchronize_test.rb +0 -43
  130. data/test/test_helper.rb +0 -91
  131. data/test/trilogy/import_test.rb +0 -7
  132. data/test/value_sets_bytes_parser_test.rb +0 -106
  133. data/test/value_sets_records_parser_test.rb +0 -34
@@ -1,352 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def should_support_recursive_import
4
- describe "importing objects with associations" do
5
- let(:new_topics) { Build(num_topics, :topic_with_book) }
6
- let(:new_topics_with_invalid_chapter) do
7
- chapter = new_topics.first.books.first.chapters.first
8
- chapter.title = nil
9
- new_topics
10
- end
11
- let(:num_topics) { 3 }
12
- let(:num_books) { 6 }
13
- let(:num_chapters) { 18 }
14
- let(:num_endnotes) { 24 }
15
-
16
- let(:new_question_with_rule) { FactoryBot.build :question, :with_rule }
17
-
18
- it 'imports top level' do
19
- assert_difference "Topic.count", +num_topics do
20
- Topic.import new_topics, recursive: true
21
- new_topics.each do |topic|
22
- assert_not_nil topic.id
23
- end
24
- end
25
- end
26
-
27
- it 'imports first level associations' do
28
- assert_difference "Book.count", +num_books do
29
- Topic.import new_topics, recursive: true
30
- new_topics.each do |topic|
31
- topic.books.each do |book|
32
- assert_equal topic.id, book.topic_id
33
- end
34
- end
35
- end
36
- end
37
-
38
- it 'imports polymorphic associations' do
39
- discounts = Array.new(1) { |i| Discount.new(amount: i) }
40
- books = Array.new(1) { |i| Book.new(author_name: "Author ##{i}", title: "Book ##{i}") }
41
- books.each do |book|
42
- book.discounts << discounts
43
- end
44
- Book.import books, recursive: true
45
- books.each do |book|
46
- book.discounts.each do |discount|
47
- assert_not_nil discount.discountable_id
48
- assert_equal 'Book', discount.discountable_type
49
- end
50
- end
51
- end
52
-
53
- it 'imports polymorphic associations from subclass' do
54
- discounts = Array.new(1) { |i| Discount.new(amount: i) }
55
- dictionaries = Array.new(1) { |i| Dictionary.new(author_name: "Author ##{i}", title: "Book ##{i}") }
56
- dictionaries.each do |dictionary|
57
- dictionary.discounts << discounts
58
- end
59
- Dictionary.import dictionaries, recursive: true
60
- assert_equal 1, Dictionary.last.discounts.count
61
- dictionaries.each do |dictionary|
62
- dictionary.discounts.each do |discount|
63
- assert_not_nil discount.discountable_id
64
- assert_equal 'Book', discount.discountable_type
65
- end
66
- end
67
- end
68
-
69
- [{ recursive: false }, {}].each do |import_options|
70
- it "skips recursion for #{import_options}" do
71
- assert_difference "Book.count", 0 do
72
- Topic.import new_topics, import_options
73
- end
74
- end
75
- end
76
-
77
- it 'imports deeper nested associations' do
78
- assert_difference "Chapter.count", +num_chapters do
79
- assert_difference "EndNote.count", +num_endnotes do
80
- Topic.import new_topics, recursive: true
81
- new_topics.each do |topic|
82
- topic.books.each do |book|
83
- book.chapters.each do |chapter|
84
- assert_equal book.id, chapter.book_id
85
- end
86
- book.end_notes.each do |endnote|
87
- assert_equal book.id, endnote.book_id
88
- end
89
- end
90
- end
91
- end
92
- end
93
- end
94
-
95
- # Models are only valid if all associations are valid
96
- it "only imports models with valid associations" do
97
- assert_difference "Topic.count", 2 do
98
- assert_difference "Book.count", 4 do
99
- assert_difference "Chapter.count", 12 do
100
- assert_difference "EndNote.count", 16 do
101
- Topic.import new_topics_with_invalid_chapter, recursive: true
102
- end
103
- end
104
- end
105
- end
106
- end
107
-
108
- it "skips validation of the associations if requested" do
109
- assert_difference "Chapter.count", +num_chapters do
110
- Topic.import new_topics_with_invalid_chapter, validate: false, recursive: true
111
- end
112
- end
113
-
114
- it 'imports has_one associations' do
115
- assert_difference 'Rule.count' do
116
- Question.import [new_question_with_rule], recursive: true
117
- end
118
- end
119
-
120
- it "imports an imported belongs_to association id" do
121
- first_new_topic = new_topics[0]
122
- second_new_topic = new_topics[1]
123
-
124
- books = first_new_topic.books.to_a
125
- Topic.import new_topics, validate: false
126
-
127
- assert_difference "Book.count", books.size do
128
- Book.import books, validate: false
129
- end
130
-
131
- books.each do |book|
132
- assert_equal book.topic_id, first_new_topic.id
133
- end
134
-
135
- books.each { |book| book.topic_id = second_new_topic.id }
136
- assert_no_difference "Book.count", books.size do
137
- Book.import books, validate: false, on_duplicate_key_update: [:topic_id]
138
- end
139
-
140
- books.each do |book|
141
- assert_equal book.topic_id, second_new_topic.id
142
- end
143
-
144
- books.each { |book| book.topic_id = nil }
145
- assert_no_difference "Book.count", books.size do
146
- Book.import books, validate: false, on_duplicate_key_update: [:topic_id]
147
- end
148
-
149
- books.each do |book|
150
- assert_nil book.topic_id, nil
151
- end
152
- end
153
-
154
- unless ENV["SKIP_COMPOSITE_PK"]
155
- describe "with composite primary keys" do
156
- it "should import models and set id" do
157
- tags = []
158
- tags << Tag.new(tag_id: 1, publisher_id: 1, tag: 'Mystery')
159
- tags << Tag.new(tag_id: 2, publisher_id: 1, tag: 'Science')
160
-
161
- assert_difference "Tag.count", +2 do
162
- Tag.import tags
163
- end
164
-
165
- assert_equal 1, tags[0].tag_id
166
- assert_equal 2, tags[1].tag_id
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
186
- end
187
- end
188
-
189
- describe "all_or_none" do
190
- [Book, Chapter, Topic, EndNote].each do |type|
191
- it "creates #{type}" do
192
- assert_difference "#{type}.count", 0 do
193
- Topic.import new_topics_with_invalid_chapter, all_or_none: true, recursive: true
194
- end
195
- end
196
- end
197
- end
198
-
199
- # If adapter supports on_duplicate_key_update and specific columns are specified, it is only applied to top level models so that SQL with invalid
200
- # columns, keys, etc isn't generated for child associations when doing recursive import
201
- if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
202
- describe "on_duplicate_key_update" do
203
- let(:new_topics) { Build(1, :topic_with_book) }
204
-
205
- it "imports objects with associations" do
206
- assert_difference "Topic.count", +1 do
207
- Topic.import new_topics, recursive: true, on_duplicate_key_update: [:updated_at], validate: false
208
- new_topics.each do |topic|
209
- assert_not_nil topic.id
210
- end
211
- end
212
- end
213
-
214
- context "when :all fields are updated" do
215
- setup do
216
- Topic.import new_topics, recursive: true
217
- end
218
-
219
- it "updates associated objects" do
220
- new_author_name = 'Richard Bachman'
221
- topic = new_topics.first
222
- topic.books.each do |book|
223
- book.author_name = new_author_name
224
- end
225
- assert_nothing_raised do
226
- Topic.import new_topics, recursive: true, on_duplicate_key_update: :all
227
- end
228
- Topic.find(topic.id).books.each do |book|
229
- assert_equal new_author_name, book.author_name
230
- end
231
- end
232
- end
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
-
282
- context "when a non-standard association name is used" do
283
- let(:new_topics) do
284
- topic = Build(:topic)
285
-
286
- 2.times do
287
- novel = topic.novels.build(title: FactoryBot.generate(:book_title), author_name: 'Stephen King')
288
- 3.times do
289
- novel.chapters.build(title: FactoryBot.generate(:chapter_title))
290
- end
291
-
292
- 4.times do
293
- novel.end_notes.build(note: FactoryBot.generate(:end_note))
294
- end
295
- end
296
-
297
- [topic]
298
- end
299
-
300
- it "updates nested associated objects" do
301
- new_chapter_title = 'The Final Chapter'
302
- novel = new_topics.first.novels.first
303
- novel.author_name = 'Richard Bachman'
304
-
305
- example_chapter = novel.chapters.first
306
- example_chapter.title = new_chapter_title
307
-
308
- assert_nothing_raised do
309
- Topic.import new_topics,
310
- recursive: true,
311
- on_duplicate_key_update: [:id],
312
- recursive_on_duplicate_key_update: {
313
- novels: { conflict_target: [:id], columns: [:author_name] },
314
- chapters: { conflict_target: [:id], columns: [:title] }
315
- }
316
- end
317
- assert_equal new_chapter_title, Chapter.find(example_chapter.id).title
318
- end
319
- end
320
- end
321
- end
322
-
323
- # If returning option is provided, it is only applied to top level models so that SQL with invalid
324
- # columns, keys, etc isn't generated for child associations when doing recursive import
325
- describe "returning" do
326
- let(:new_topics) { Build(1, :topic_with_book) }
327
-
328
- it "imports objects with associations" do
329
- assert_difference "Topic.count", +1 do
330
- Topic.import new_topics, recursive: true, returning: [:content], validate: false
331
- new_topics.each do |topic|
332
- assert_not_nil topic.id
333
- end
334
- end
335
- end
336
- end
337
-
338
- # If no returning option is provided, it is ignored
339
- describe "no returning" do
340
- let(:new_topics) { Build(1, :topic_with_book) }
341
-
342
- it "is ignored and imports objects with associations" do
343
- assert_difference "Topic.count", +1 do
344
- Topic.import new_topics, recursive: true, no_returning: true, validate: false
345
- new_topics.each do |topic|
346
- assert_not_nil topic.id
347
- end
348
- end
349
- end
350
- end
351
- end
352
- end
@@ -1,232 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def should_support_sqlite3_import_functionality
4
- if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
5
- should_support_sqlite_upsert_functionality
6
- end
7
-
8
- describe "#supports_imports?" do
9
- it "should support import" do
10
- assert ActiveRecord::Base.supports_import?
11
- end
12
- end
13
-
14
- describe "#import" do
15
- it "imports with a single insert on SQLite 3.7.11 or higher" do
16
- assert_difference "Topic.count", +507 do
17
- result = Topic.import Build(7, :topics)
18
- assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
19
- assert_equal 7, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
20
-
21
- result = Topic.import Build(500, :topics)
22
- assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
23
- assert_equal 507, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
24
- end
25
- end
26
-
27
- it "imports with a two inserts on SQLite 3.7.11 or higher" do
28
- assert_difference "Topic.count", +501 do
29
- result = Topic.import Build(501, :topics)
30
- assert_equal 2, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
31
- assert_equal 501, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
32
- end
33
- end
34
-
35
- it "imports with a five inserts on SQLite 3.7.11 or higher" do
36
- assert_difference "Topic.count", +2500 do
37
- result = Topic.import Build(2500, :topics)
38
- assert_equal 5, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
39
- assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
40
- end
41
- end
42
- end
43
- end
44
-
45
- def should_support_sqlite_upsert_functionality
46
- should_support_basic_on_duplicate_key_update
47
- should_support_on_duplicate_key_ignore
48
-
49
- describe "#import" do
50
- extend ActiveSupport::TestCase::ImportAssertions
51
-
52
- macro(:perform_import) { raise "supply your own #perform_import in a context below" }
53
- macro(:updated_topic) { Topic.find(@topic.id) }
54
-
55
- context "with :on_duplicate_key_ignore and validation checks turned off" do
56
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
57
- let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17]] }
58
- let(:updated_values) { [[99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
59
-
60
- setup do
61
- Topic.import columns, values, validate: false
62
- end
63
-
64
- it "should not update any records" do
65
- result = Topic.import columns, updated_values, on_duplicate_key_ignore: true, validate: false
66
- assert_equal [], result.ids
67
- end
68
- end
69
-
70
- context "with :on_duplicate_key_update and validation checks turned off" do
71
- asssertion_group(:should_support_on_duplicate_key_update) do
72
- should_not_update_fields_not_mentioned
73
- should_update_foreign_keys
74
- should_not_update_created_at_on_timestamp_columns
75
- should_update_updated_at_on_timestamp_columns
76
- end
77
-
78
- context "using a hash" do
79
- context "with :columns a hash" do
80
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
81
- let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17]] }
82
- let(:updated_values) { [[99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
83
-
84
- macro(:perform_import) do |*opts|
85
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id, columns: update_columns }, validate: false)
86
- end
87
-
88
- setup do
89
- Topic.import columns, values, validate: false
90
- @topic = Topic.find 99
91
- end
92
-
93
- it "should not modify the passed in :on_duplicate_key_update columns array" do
94
- assert_nothing_raised do
95
- columns = %w(title author_name).freeze
96
- Topic.import columns, [%w(foo, bar)], on_duplicate_key_update: { columns: columns }
97
- end
98
- end
99
-
100
- context "using string hash map" do
101
- let(:update_columns) { { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
102
- should_support_on_duplicate_key_update
103
- should_update_fields_mentioned
104
- end
105
-
106
- context "using string hash map, but specifying column mismatches" do
107
- let(:update_columns) { { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
108
- should_support_on_duplicate_key_update
109
- should_update_fields_mentioned_with_hash_mappings
110
- end
111
-
112
- context "using symbol hash map" do
113
- let(:update_columns) { { title: :title, author_email_address: :author_email_address, parent_id: :parent_id } }
114
- should_support_on_duplicate_key_update
115
- should_update_fields_mentioned
116
- end
117
-
118
- context "using symbol hash map, but specifying column mismatches" do
119
- let(:update_columns) { { title: :author_email_address, author_email_address: :title, parent_id: :parent_id } }
120
- should_support_on_duplicate_key_update
121
- should_update_fields_mentioned_with_hash_mappings
122
- end
123
- end
124
-
125
- context 'with :index_predicate' do
126
- let(:columns) { %w( id device_id alarm_type status metadata ) }
127
- let(:values) { [[99, 17, 1, 1, 'foo']] }
128
- let(:updated_values) { [[99, 17, 1, 2, 'bar']] }
129
-
130
- macro(:perform_import) do |*opts|
131
- Alarm.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: [:device_id, :alarm_type], index_predicate: 'status <> 0', columns: [:status] }, validate: false)
132
- end
133
-
134
- macro(:updated_alarm) { Alarm.find(@alarm.id) }
135
-
136
- setup do
137
- Alarm.import columns, values, validate: false
138
- @alarm = Alarm.find 99
139
- end
140
-
141
- context 'supports on duplicate key update for partial indexes' do
142
- it 'should not update created_at timestamp columns' do
143
- Timecop.freeze Chronic.parse("5 minutes from now") do
144
- perform_import
145
- assert_in_delta @alarm.created_at.to_i, updated_alarm.created_at.to_i, 1
146
- end
147
- end
148
-
149
- it 'should update updated_at timestamp columns' do
150
- time = Chronic.parse("5 minutes from now")
151
- Timecop.freeze time do
152
- perform_import
153
- assert_in_delta time.to_i, updated_alarm.updated_at.to_i, 1
154
- end
155
- end
156
-
157
- it 'should not update fields not mentioned' do
158
- perform_import
159
- assert_equal 'foo', updated_alarm.metadata
160
- end
161
-
162
- it 'should update fields mentioned with hash mappings' do
163
- perform_import
164
- assert_equal 2, updated_alarm.status
165
- end
166
- end
167
- end
168
-
169
- context 'with :condition' do
170
- let(:columns) { %w( id device_id alarm_type status metadata) }
171
- let(:values) { [[99, 17, 1, 1, 'foo']] }
172
- let(:updated_values) { [[99, 17, 1, 1, 'bar']] }
173
-
174
- macro(:perform_import) do |*opts|
175
- Alarm.import(
176
- columns,
177
- updated_values,
178
- opts.extract_options!.merge(
179
- on_duplicate_key_update: {
180
- conflict_target: [:id],
181
- condition: "alarms.metadata NOT LIKE '%foo%'",
182
- columns: [:metadata]
183
- },
184
- validate: false
185
- )
186
- )
187
- end
188
-
189
- macro(:updated_alarm) { Alarm.find(@alarm.id) }
190
-
191
- setup do
192
- Alarm.import columns, values, validate: false
193
- @alarm = Alarm.find 99
194
- end
195
-
196
- it 'should not update fields not matched' do
197
- perform_import
198
- assert_equal 'foo', updated_alarm.metadata
199
- end
200
- end
201
-
202
- context "with no :conflict_target" do
203
- context "with no primary key" do
204
- it "raises ArgumentError" do
205
- error = assert_raises ArgumentError do
206
- Rule.import Build(3, :rules), on_duplicate_key_update: [:condition_text], validate: false
207
- end
208
- assert_match(/Expected :conflict_target to be specified/, error.message)
209
- end
210
- end
211
- end
212
-
213
- context "with no :columns" do
214
- let(:columns) { %w( id title author_name author_email_address ) }
215
- let(:values) { [[100, "Book", "John Doe", "john@doe.com"]] }
216
- let(:updated_values) { [[100, "Title Should Not Change", "Author Should Not Change", "john@nogo.com"]] }
217
-
218
- macro(:perform_import) do |*opts|
219
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id }, validate: false)
220
- end
221
-
222
- setup do
223
- Topic.import columns, values, validate: false
224
- @topic = Topic.find 100
225
- end
226
-
227
- should_update_updated_at_on_timestamp_columns
228
- end
229
- end
230
- end
231
- end
232
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require File.expand_path('../test_helper', __FILE__)
4
-
5
- describe ".synchronize" do
6
- let(:topics) { Generate(3, :topics) }
7
- let(:titles) { %w(one two three) }
8
-
9
- setup do
10
- # update records outside of ActiveRecord knowing about it
11
- Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[0]}_haha' WHERE id=#{topics[0].id}", "Updating record 1 without ActiveRecord" )
12
- Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[1]}_haha' WHERE id=#{topics[1].id}", "Updating record 2 without ActiveRecord" )
13
- Topic.connection.execute( "UPDATE #{Topic.table_name} SET title='#{titles[2]}_haha' WHERE id=#{topics[2].id}", "Updating record 3 without ActiveRecord" )
14
- end
15
-
16
- it "reloads data for the specified records" do
17
- Topic.synchronize topics
18
-
19
- actual_titles = topics.map(&:title)
20
- assert_equal "#{titles[0]}_haha", actual_titles[0], "the first record was not correctly updated"
21
- assert_equal "#{titles[1]}_haha", actual_titles[1], "the second record was not correctly updated"
22
- assert_equal "#{titles[2]}_haha", actual_titles[2], "the third record was not correctly updated"
23
- end
24
-
25
- it "the synchronized records aren't dirty" do
26
- # Update the in memory records so they're dirty
27
- topics.each { |topic| topic.title = 'dirty title' }
28
-
29
- Topic.synchronize topics
30
-
31
- assert_equal false, topics[0].changed?, "the first record was dirty"
32
- assert_equal false, topics[1].changed?, "the second record was dirty"
33
- assert_equal false, topics[2].changed?, "the third record was dirty"
34
- end
35
-
36
- it "ignores default scope" do
37
- # update records outside of ActiveRecord knowing about it
38
- Topic.connection.execute( "UPDATE #{Topic.table_name} SET approved='0' WHERE id=#{topics[0].id}", "Updating record 1 without ActiveRecord" )
39
-
40
- Topic.synchronize topics
41
- assert_equal false, topics[0].approved
42
- end
43
- end