activerecord-import 1.0.6 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bedb8323d872ae8ca6daebc691f82fedc918a3f2c01e7e354a8534854c06d80e
4
- data.tar.gz: 2df3dfaef596b93d208854557700167a4a8e0e9bb151ff224e4a6255e374086f
3
+ metadata.gz: b3360334cbd71089351c8211214bb4aedf0bd3e65305513b17c9fb09e3f1cf19
4
+ data.tar.gz: 2b97e3a1c6c2b39970dd3bb2a00a608d6e105cddac19d3f9ebaf28f8f30ece69
5
5
  SHA512:
6
- metadata.gz: c97ba946d5453394831959f384868812ca59833d1c694feccc19d68967d38e3501305fca11fcd39dec32ddc8c88d26982ea2f087d590e34a19da88ff7def1c9c
7
- data.tar.gz: 0fcdc64e20ac0ee42584ab97f060e116f2542a353f0dc815a616adbe3810d6a343f8b021c0204adde9eb484aac5befc4e0b4699b1bbdfbddb2d486f8418d1839
6
+ metadata.gz: 4585ba6ff2300d94fbd646d5f5a07cc254311cb9a47eacc7b1266eab8df5d9a53ca3018232e6c2517f05e1aef0dafaff20942bd1150ef518d688940f795bf2a9
7
+ data.tar.gz: 532506d134f067323d0490a913739089e55bf17893f602c752e5e963af2d7d799f5200a72cc48e7c780992e9f82c68273dc7af308034664ba898ed5292e199c4
@@ -0,0 +1,73 @@
1
+ name: Test
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ services:
6
+ postgres:
7
+ image: postgis/postgis:10-2.5
8
+ env:
9
+ POSTGRES_USER: postgres
10
+ POSTGRES_PASSWORD: postgres
11
+ ports:
12
+ - 5432:5432
13
+ # Set health checks to wait until postgres has started
14
+ options: >-
15
+ --health-cmd pg_isready
16
+ --health-interval 10s
17
+ --health-timeout 5s
18
+ --health-retries 5
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ ruby:
23
+ - 2.6
24
+ env:
25
+ - AR_VERSION: 6.1
26
+ - AR_VERSION: 6.0
27
+ - AR_VERSION: 5.2
28
+ - AR_VERSION: 5.1
29
+ include:
30
+ - ruby: 2.4
31
+ env:
32
+ AR_VERSION: 5.0
33
+ - ruby: 2.4
34
+ env:
35
+ AR_VERSION: 4.2
36
+ runs-on: ubuntu-latest
37
+ env:
38
+ AR_VERSION: ${{ matrix.env.AR_VERSION }}
39
+ DB_DATABASE: activerecord_import_test
40
+ steps:
41
+ - uses: actions/checkout@v2
42
+ - uses: ruby/setup-ruby@v1
43
+ with:
44
+ ruby-version: ${{ matrix.ruby }}
45
+ - name: Setup Bundler 1.x for Ruby 2.3
46
+ if: ${{ matrix.ruby == '2.3' }}
47
+ run: echo "BUNDLER_VERSION=1.17.3" >> $GITHUB_ENV
48
+ - name: Set up databases
49
+ run: |
50
+ sudo /etc/init.d/mysql start
51
+ mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }} CHARACTER SET utf8 COLLATE utf8_general_ci;' -u root -proot
52
+ psql -h localhost -U postgres -c 'create database ${{ env.DB_DATABASE }};'
53
+ psql -h localhost -U postgres -d ${{ env.DB_DATABASE }} -c 'create extension if not exists hstore;'
54
+ psql -h localhost -U postgres -c 'create extension if not exists postgis;'
55
+ psql -h localhost -U postgres -c 'create extension if not exists "uuid-ossp";'
56
+ cp test/github/database.yml test/database.yml
57
+ env:
58
+ PGPASSWORD: postgres
59
+ - name: Install dependencies
60
+ run : AR_VERSION=${{ env.AR_VERSION }} bundle install
61
+ - name: Run tests
62
+ run: |
63
+ bundle exec rake test:mysql2
64
+ bundle exec rake test:mysql2_makara
65
+ bundle exec rake test:mysql2spatial
66
+ bundle exec rake test:postgis
67
+ bundle exec rake test:postgresql
68
+ bundle exec rake test:postgresql_makara
69
+ bundle exec rake test:seamless_database_pool
70
+ bundle exec rake test:spatialite
71
+ bundle exec rake test:sqlite3
72
+ - name: Run Rubocop
73
+ run: bundle exec rubocop
data/.gitignore CHANGED
@@ -24,6 +24,7 @@ pkg
24
24
  log/*.log
25
25
  test.db
26
26
  test/database.yml
27
+ benchmarks/log/
27
28
 
28
29
  .ruby-*
29
30
  .bundle/
data/CHANGELOG.md CHANGED
@@ -1,11 +1,44 @@
1
+ ## Changes in 1.2.0
2
+
3
+ ### Fixes
4
+
5
+ * Update JDBC MySQL adapter to use mysql2 connection adapter. Thanks to @terencechow via \##744.
6
+ * Fix importing STI models with ActiveRecord 6. Thanks to @clemens1483 via \##743.
7
+ * Use polymorphic_name instead of base_class.name for imports. Thanks to @kmhajjar via \##741.
8
+ * Fix compatibility issue with composite primary keys. Thanks to @dlanileonardo via \##737.
9
+ * Prevent double validation of associations on recursive import.
10
+
11
+ ## Changes in 1.1.0
12
+
13
+ ### New Features
14
+
15
+ * Add batch progress reporting. Thanks to @gee-forr via \##729.
16
+
17
+ ## Changes in 1.0.8
18
+
19
+ ### Fixes
20
+
21
+ * Use correct method for clearing query cache. Thanks to @EtienneDepaulis via \##719.
22
+
23
+ ## Changes in 1.0.7
24
+
25
+ ### New Features
26
+
27
+ * Use @@max_allowed_packet session variable instead of querying SHOW VARIABLES. Thanks to @diclophis via \#706.
28
+ * Add option :track_validation_failures. 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]. Thanks to @rorymckinley via \#684.
29
+
30
+ ### Fixes
31
+
32
+ * Prevent mass-assignment errors in Rails strict mode. Thanks to @diclophis via \##709.
33
+
1
34
  ## Changes in 1.0.6
2
35
 
3
36
  ### Fixes
4
37
 
5
38
  * Handle after_initialize callbacks. Thanks to @AhMohsen46 via \#691 and
6
39
  \#692.
7
- * Fix regression introduced in 1.0.4. Explicity allow adapters to
8
- support on duplicate key update. Thanks to @dsobiera, @jkowens via \#698.
40
+ * Fix regression introduced in 1.0.4. Explicitly allow adapters to
41
+ support on duplicate key update. Thanks to @dsobiera, @jkowens via \#696.
9
42
 
10
43
  ## Changes in 1.0.5
11
44
 
@@ -13,7 +46,7 @@
13
46
 
14
47
  * Allow serialized attributes to be returned from import. Thanks to @timanovsky, @jkowens via \#660.
15
48
  * Return ActiveRecord::Connection from
16
- ActiveREcord::Base#establish_connection. Thanks to @reverentF via
49
+ ActiveRecord::Base#establish_connection. Thanks to @reverentF via
17
50
  \#663.
18
51
  * Support PostgreSQL array. Thanks to @ujihisa via \#669.
19
52
  * Skip loading association ids when column changed. Thanks to @Aristat
@@ -68,7 +101,7 @@
68
101
  * Fix import issue for models with Postgresql json/jsonb fields. Thanks
69
102
  to @stokarenko via \#594.
70
103
  * Fix issue importing models with timestamps that contain timezone
71
- information. Thaks to @dekaikiwi, @jkowens via \#598.
104
+ information. Thanks to @dekaikiwi, @jkowens via \#598.
72
105
  * Ignore :no_returning when using :recursive option. Thanks to @dgollahon, @jkowens
73
106
  via \#599.
74
107
 
@@ -283,7 +316,7 @@
283
316
  Thanks to @jkowens via \#301.
284
317
  * Allow for custom timestamp columns. Thanks to @mojidabckuu, @jkowens
285
318
  via \#401.
286
-
319
+
287
320
  ### Fixes
288
321
 
289
322
  * Fix ActiveRecord 5 issue coercing boolean values when serializing
@@ -295,7 +328,7 @@
295
328
 
296
329
  * Fix issue where PostgreSQL cannot recognize columns if names
297
330
  include mixed case characters. Thanks to @hugobgranja via \#379.
298
- * Fix an issue for ActiveRecord 5 where serialized fields with
331
+ * Fix an issue for ActiveRecord 5 where serialized fields with
299
332
  default values were not being typecast. Thanks to @whistlerbrk,
300
333
  @jkowens via \#386.
301
334
  * Add option :force_single_insert for MySQL to make sure a single
data/Gemfile CHANGED
@@ -6,8 +6,11 @@ version = ENV['AR_VERSION'].to_f
6
6
 
7
7
  mysql2_version = '0.3.0'
8
8
  mysql2_version = '0.4.0' if version >= 4.2
9
+ mysql2_version = '0.5.0' if version >= 6.1
9
10
  sqlite3_version = '1.3.0'
10
11
  sqlite3_version = '1.4.0' if version >= 6.0
12
+ pg_version = '0.9'
13
+ pg_version = '1.1' if version >= 6.1
11
14
 
12
15
  group :development, :test do
13
16
  gem 'rubocop', '~> 0.40.0'
@@ -17,13 +20,13 @@ end
17
20
  # Database Adapters
18
21
  platforms :ruby do
19
22
  gem "mysql2", "~> #{mysql2_version}"
20
- gem "pg", "~> 0.9"
23
+ gem "pg", "~> #{pg_version}"
21
24
  gem "sqlite3", "~> #{sqlite3_version}"
22
25
  gem "seamless_database_pool", "~> 1.0.20"
23
26
  end
24
27
 
25
28
  platforms :jruby do
26
- gem "jdbc-mysql"
29
+ gem 'jdbc-mysql', '< 8', require: false
27
30
  gem "jdbc-postgres"
28
31
  gem "activerecord-jdbcsqlite3-adapter", "~> 1.3"
29
32
  gem "activerecord-jdbcmysql-adapter", "~> 1.3"
data/README.markdown CHANGED
@@ -1,4 +1,4 @@
1
- # Activerecord-Import [![Build Status](https://travis-ci.org/zdennis/activerecord-import.svg?branch=master)](https://travis-ci.org/zdennis/activerecord-import)
1
+ # Activerecord-Import ![Build Status](https://github.com/zdennis/activerecord-import/actions/workflows/test.yaml/badge.svg)
2
2
 
3
3
  Activerecord-Import is a library for bulk inserting data using ActiveRecord.
4
4
 
@@ -231,6 +231,18 @@ columns = [ :title ]
231
231
  Book.import columns, books, batch_size: 2
232
232
  ```
233
233
 
234
+ If your import is particularly large or slow (possibly due to [callbacks](#callbacks)) whilst batch importing, you might want a way to report back on progress. This is supported by passing a callable as the `batch_progress` option. e.g:
235
+
236
+ ```ruby
237
+ my_proc = ->(rows_size, num_batches, current_batch_number, batch_duration_in_secs) {
238
+ # Using the arguments provided to the callable, you can
239
+ # send an email, post to a websocket,
240
+ # update slack, alert if import is taking too long, etc.
241
+ }
242
+
243
+ Book.import columns, books, batch_size: 2, batch_progress: my_proc
244
+ ```
245
+
234
246
  #### Recursive
235
247
 
236
248
  NOTE: This only works with PostgreSQL and ActiveRecord objects. This won't work with
@@ -250,20 +262,21 @@ Book.import books, recursive: true
250
262
 
251
263
  ### Options
252
264
 
253
- Key | Options | Default | Description
254
- ----------------------- | --------------------- | ------------------ | -----------
255
- :validate | `true`/`false` | `true` | Whether or not to run `ActiveRecord` validations (uniqueness skipped). This option will always be true when using `import!`.
256
- :validate_uniqueness | `true`/`false` | `false` | Whether or not to run uniqueness validations, has potential pitfalls, use with caution (requires `>= v0.27.0`).
257
- :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.
258
- :on_duplicate_key_ignore| `true`/`false` | `false` | Allows skipping records with duplicate keys. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-ignore) for more details.
259
- :ignore | `true`/`false` | `false` | Alias for :on_duplicate_key_ignore.
260
- :on_duplicate_key_update| :all, `Array`, `Hash` | N/A | Allows upsert logic to be used. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-update) for more details.
261
- :synchronize | `Array` | N/A | An array of ActiveRecord instances. This synchronizes existing instances in memory with updates from the import.
262
- :timestamps | `true`/`false` | `true` | Enables/disables timestamps on imported records.
263
- :recursive | `true`/`false` | `false` | Imports has_many/has_one associations (PostgreSQL only).
264
- :batch_size | `Integer` | total # of records | Max number of records to insert per import
265
- :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.
266
- :all_or_none | `true`/`false` | `false` | Will not import any records if there is a record with validation errors.
265
+ Key | Options | Default | Description
266
+ ------------------------- | --------------------- | ------------------ | -----------
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, has potential pitfalls, use with caution (requires `>= v0.27.0`).
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
+ :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
+ :on_duplicate_key_ignore | `true`/`false` | `false` | Allows skipping records with duplicate keys. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-ignore) for more details.
272
+ :ignore | `true`/`false` | `false` | Alias for :on_duplicate_key_ignore.
273
+ :on_duplicate_key_update | :all, `Array`, `Hash` | N/A | Allows upsert logic to be used. See [here](https://github.com/zdennis/activerecord-import/#duplicate-key-update) for more details.
274
+ :synchronize | `Array` | N/A | An array of ActiveRecord instances. This synchronizes existing instances in memory with updates from the import.
275
+ :timestamps | `true`/`false` | `true` | Enables/disables timestamps on imported records.
276
+ :recursive | `true`/`false` | `false` | Imports has_many/has_one associations (PostgreSQL only).
277
+ :batch_size | `Integer` | total # of records | Max number of records to insert per import
278
+ :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
+ :all_or_none | `true`/`false` | `false` | Will not import any records if there is a record with validation errors.
267
280
 
268
281
  #### Duplicate Key Ignore
269
282
 
@@ -379,7 +392,7 @@ Book.import books, validate_uniqueness: true
379
392
 
380
393
  ### Return Info
381
394
 
382
- The `import` method returns a `Result` object that responds to `failed_instances` and `num_inserts`. Additionally, for users of Postgres, there will be two arrays `ids` and `results` that can be accessed`.
395
+ The `import` method returns a `Result` object that responds to `failed_instances` and `num_inserts`. Additionally, for users of Postgres, there will be two arrays `ids` and `results` that can be accessed.
383
396
 
384
397
  ```ruby
385
398
  articles = [
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
6
6
  gem.email = ["zach.dennis@gmail.com"]
7
7
  gem.summary = "Bulk insert extension for ActiveRecord"
8
8
  gem.description = "A library for bulk inserting data using ActiveRecord."
9
- gem.homepage = "http://github.com/zdennis/activerecord-import"
9
+ gem.homepage = "https://github.com/zdennis/activerecord-import"
10
10
  gem.license = "MIT"
11
11
 
12
12
  gem.files = `git ls-files`.split($\)
data/gemfiles/6.0.gemfile CHANGED
@@ -1 +1,2 @@
1
1
  gem 'activerecord', '~> 6.0.0'
2
+ gem 'composite_primary_keys', '~> 12.0'
data/gemfiles/6.1.gemfile CHANGED
@@ -1 +1 @@
1
- gem 'activerecord', '~> 6.1.0.alpha', github: "rails/rails"
1
+ gem 'activerecord', '~> 6.1.0'
@@ -1,6 +1,6 @@
1
- require "active_record/connection_adapters/mysql_adapter"
2
- require "activerecord-import/adapters/mysql_adapter"
1
+ require "active_record/connection_adapters/mysql2_adapter"
2
+ require "activerecord-import/adapters/mysql2_adapter"
3
3
 
4
- class ActiveRecord::ConnectionAdapters::MysqlAdapter
5
- include ActiveRecord::Import::MysqlAdapter
4
+ class ActiveRecord::ConnectionAdapters::Mysql2Adapter
5
+ include ActiveRecord::Import::Mysql2Adapter
6
6
  end
@@ -56,9 +56,9 @@ module ActiveRecord::Import::MysqlAdapter
56
56
  # in a single packet
57
57
  def max_allowed_packet # :nodoc:
58
58
  @max_allowed_packet ||= begin
59
- result = execute( "SHOW VARIABLES like 'max_allowed_packet'" )
59
+ result = execute( "SELECT @@max_allowed_packet" )
60
60
  # original Mysql gem responds to #fetch_row while Mysql2 responds to #first
61
- val = result.respond_to?(:fetch_row) ? result.fetch_row[1] : result.first[1]
61
+ val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
62
62
  val.to_i
63
63
  end
64
64
  end
@@ -28,7 +28,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
28
28
  else
29
29
  select_values( sql2insert, *args )
30
30
  end
31
- query_cache.clear if query_cache_enabled
31
+ clear_query_cache if query_cache_enabled
32
32
  end
33
33
 
34
34
  if options[:returning].blank?
@@ -49,7 +49,7 @@ module ActiveRecord::Import #:nodoc:
49
49
  associations = klass.reflect_on_all_associations(:belongs_to)
50
50
  associations.each do |assoc|
51
51
  if (index = attrs.index(assoc.name))
52
- key = assoc.foreign_key.to_sym
52
+ key = assoc.foreign_key.is_a?(Array) ? assoc.foreign_key.map(&:to_sym) : assoc.foreign_key.to_sym
53
53
  attrs[index] = key unless attrs.include?(key)
54
54
  end
55
55
  end
@@ -547,7 +547,7 @@ class ActiveRecord::Base
547
547
  alias import! bulk_import! unless ActiveRecord::Base.respond_to? :import!
548
548
 
549
549
  def import_helper( *args )
550
- options = { validate: true, timestamps: true }
550
+ options = { validate: true, timestamps: true, track_validation_failures: false }
551
551
  options.merge!( args.pop ) if args.last.is_a? Hash
552
552
  # making sure that current model's primary key is used
553
553
  options[:primary_key] = primary_key
@@ -703,13 +703,18 @@ class ActiveRecord::Base
703
703
  # keep track of the instance and the position it is currently at. if this fails
704
704
  # validation we'll use the index to remove it from the array_of_attributes
705
705
  arr.each_with_index do |hsh, i|
706
- model = new(hsh)
706
+ # utilize block initializer syntax to prevent failure when 'mass_assignment_sanitizer = :strict'
707
+ model = new do |m|
708
+ hsh.each_pair { |k, v| m[k] = v }
709
+ end
710
+
707
711
  next if validator.valid_model?(model)
708
712
  raise(ActiveRecord::RecordInvalid, model) if options[:raise_error]
713
+
709
714
  array_of_attributes[i] = nil
710
715
  failure = model.dup
711
716
  failure.errors.send(:initialize_dup, model.errors)
712
- failed_instances << failure
717
+ failed_instances << (options[:track_validation_failures] ? [i, failure] : failure )
713
718
  end
714
719
  array_of_attributes.compact!
715
720
  end
@@ -729,7 +734,7 @@ class ActiveRecord::Base
729
734
  set_attributes_and_mark_clean(models, return_obj, timestamps, options)
730
735
 
731
736
  # if there are auto-save associations on the models we imported that are new, import them as well
732
- import_associations(models, options.dup) if options[:recursive]
737
+ import_associations(models, options.dup.merge(validate: false)) if options[:recursive]
733
738
  end
734
739
 
735
740
  return_obj
@@ -770,21 +775,22 @@ class ActiveRecord::Base
770
775
  unless scope_columns.blank?
771
776
  scope_columns.zip(scope_values).each do |name, value|
772
777
  name_as_sym = name.to_sym
773
- next if column_names.include?(name_as_sym)
774
-
775
- is_sti = (name_as_sym == inheritance_column.to_sym && self < base_class)
776
- value = Array(value).first if is_sti
777
-
778
+ next if column_names.include?(name_as_sym) || name_as_sym == inheritance_column.to_sym
778
779
  column_names << name_as_sym
779
780
  array_of_attributes.each { |attrs| attrs << value }
780
781
  end
781
782
  end
782
783
 
784
+ if finder_needs_type_condition?
785
+ unless column_names.include?(inheritance_column.to_sym)
786
+ column_names << inheritance_column.to_sym
787
+ array_of_attributes.each { |attrs| attrs << sti_name }
788
+ end
789
+ end
790
+
783
791
  columns = column_names.each_with_index.map do |name, i|
784
792
  column = columns_hash[name.to_s]
785
-
786
793
  raise ActiveRecord::Import::MissingColumnError.new(name.to_s, i) if column.nil?
787
-
788
794
  column
789
795
  end
790
796
 
@@ -800,17 +806,29 @@ class ActiveRecord::Base
800
806
  if supports_import?
801
807
  # generate the sql
802
808
  post_sql_statements = connection.post_sql_statements( quoted_table_name, options )
809
+ import_size = values_sql.size
810
+
811
+ batch_size = options[:batch_size] || import_size
812
+ run_proc = options[:batch_size].to_i.positive? && options[:batch_progress].respond_to?( :call )
813
+ progress_proc = options[:batch_progress]
814
+ current_batch = 0
815
+ batches = (import_size / batch_size.to_f).ceil
803
816
 
804
- batch_size = options[:batch_size] || values_sql.size
805
817
  values_sql.each_slice(batch_size) do |batch_values|
818
+ batch_started_at = Time.now.to_i
819
+
806
820
  # perform the inserts
807
821
  result = connection.insert_many( [insert_sql, post_sql_statements].flatten,
808
822
  batch_values,
809
823
  options,
810
- "#{model_name} Create Many Without Validations Or Callbacks" )
824
+ "#{model_name} Create Many" )
825
+
811
826
  number_inserted += result.num_inserts
812
827
  ids += result.ids
813
828
  results += result.results
829
+ current_batch += 1
830
+
831
+ progress_proc.call(import_size, batches, current_batch, Time.now.to_i - batch_started_at) if run_proc
814
832
  end
815
833
  else
816
834
  transaction(requires_new: true) do
@@ -944,8 +962,13 @@ class ActiveRecord::Base
944
962
  changed_objects.each do |child|
945
963
  child.public_send("#{association_reflection.foreign_key}=", model.id)
946
964
  # For polymorphic associations
965
+ association_name = if model.class.respond_to?(:polymorphic_name)
966
+ model.class.polymorphic_name
967
+ else
968
+ model.class.base_class
969
+ end
947
970
  association_reflection.type.try do |type|
948
- child.public_send("#{type}=", model.class.base_class.name)
971
+ child.public_send("#{type}=", association_name)
949
972
  end
950
973
  end
951
974
  associated_objects_by_class[model.class.name][association_reflection.name].concat changed_objects
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/array'
2
+
1
3
  module ActiveRecord::Import
2
4
  class ValueSetTooLargeError < StandardError
3
5
  attr_reader :size
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "1.0.6".freeze
3
+ VERSION = "1.2.0".freeze
4
4
  end
5
5
  end
@@ -1,7 +1,8 @@
1
1
  common: &common
2
2
  username: root
3
- password:
3
+ password: root
4
4
  encoding: utf8
5
+ collation: utf8_general_ci
5
6
  host: localhost
6
7
  database: activerecord_import_test
7
8
 
@@ -37,6 +38,7 @@ oracle:
37
38
  postgresql: &postgresql
38
39
  <<: *common
39
40
  username: postgres
41
+ password: postgres
40
42
  adapter: postgresql
41
43
  min_messages: warning
42
44
 
data/test/import_test.rb CHANGED
@@ -169,7 +169,17 @@ describe "#import" do
169
169
  assert_difference "Dictionary.count", +1 do
170
170
  Dictionary.import dictionaries
171
171
  end
172
- assert_equal "Dictionary", Dictionary.first.type
172
+ assert_equal "Dictionary", Dictionary.last.type
173
+ end
174
+
175
+ it "should import arrays successfully" do
176
+ columns = [:author_name, :title]
177
+ values = [["Noah Webster", "Webster's Dictionary"]]
178
+
179
+ assert_difference "Dictionary.count", +1 do
180
+ Dictionary.import columns, values
181
+ end
182
+ assert_equal "Dictionary", Dictionary.last.type
173
183
  end
174
184
  end
175
185
 
@@ -252,6 +262,16 @@ describe "#import" do
252
262
  end
253
263
  end
254
264
 
265
+ it "should index the failed instances by their poistion in the set if `track_failures` is true" do
266
+ index_offset = valid_values.length
267
+ results = Topic.import columns, valid_values + invalid_values, validate: true, track_validation_failures: true
268
+ assert_equal invalid_values.size, results.failed_instances.size
269
+ invalid_values.each_with_index do |value_set, index|
270
+ assert_equal index + index_offset, results.failed_instances[index].first
271
+ assert_equal value_set.first, results.failed_instances[index].last.title
272
+ end
273
+ end
274
+
255
275
  it "should set ids in valid models if adapter supports setting primary key of imported objects" do
256
276
  if ActiveRecord::Base.supports_setting_primary_key_of_imported_objects?
257
277
  Topic.import (invalid_models + valid_models), validate: true
@@ -395,6 +415,15 @@ describe "#import" do
395
415
  assert_equal 3, result.num_inserts if Topic.supports_import?
396
416
  end
397
417
  end
418
+
419
+ it "should accept and call an optional callable to run after each batch" do
420
+ lambda_called = 0
421
+
422
+ my_proc = ->(_row_count, _batches, _batch, _duration) { lambda_called += 1 }
423
+ Topic.import Build(10, :topics), batch_size: 4, batch_progress: my_proc
424
+
425
+ assert_equal 3, lambda_called
426
+ end
398
427
  end
399
428
 
400
429
  context "with :synchronize option" do
@@ -642,6 +671,14 @@ describe "#import" do
642
671
  assert_equal [val1, val2], scope.map(&column).sort
643
672
  end
644
673
 
674
+ context "for cards and decks" do
675
+ it "works when the polymorphic name is different than base class name" do
676
+ deck = Deck.create(id: 1, name: 'test')
677
+ deck.cards.import [:id, :deck_type], [[1, 'PlayingCard']]
678
+ assert_equal deck.cards.first.deck_type, "PlayingCard"
679
+ end
680
+ end
681
+
645
682
  it "works importing array of hashes" do
646
683
  scope.import [{ column => val1 }, { column => val2 }]
647
684
 
@@ -0,0 +1,3 @@
1
+ class Card < ActiveRecord::Base
2
+ belongs_to :deck, polymorphic: true
3
+ end
@@ -0,0 +1,6 @@
1
+ class Deck < ActiveRecord::Base
2
+ has_many :cards
3
+ def self.polymorphic_name
4
+ "PlayingCard"
5
+ end
6
+ end
@@ -0,0 +1,2 @@
1
+ class PlayingCard < ActiveRecord::Base
2
+ end
@@ -52,6 +52,20 @@ ActiveRecord::Schema.define do
52
52
  t.string :name
53
53
  end
54
54
 
55
+ create_table :cards, force: :cascade do |t|
56
+ t.string :name
57
+ t.string :deck_type
58
+ t.integer :deck_id
59
+ end
60
+
61
+ create_table :decks, force: :cascade do |t|
62
+ t.string :name
63
+ end
64
+
65
+ create_table :playing_cards, force: :cascade do |t|
66
+ t.string :name
67
+ end
68
+
55
69
  create_table :books, force: :cascade do |t|
56
70
  t.string :title, null: false
57
71
  t.string :publisher, null: false, default: 'Default Publisher'
data/test/test_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'rake'
2
3
  test_dir = Pathname.new File.dirname(__FILE__)
3
4
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
5
  $LOAD_PATH.unshift(File.dirname(__FILE__))
@@ -48,7 +49,15 @@ adapter = ENV["ARE_DB"] || "sqlite3"
48
49
  FileUtils.mkdir_p 'log'
49
50
  ActiveRecord::Base.logger = Logger.new("log/test.log")
50
51
  ActiveRecord::Base.logger.level = Logger::DEBUG
51
- ActiveRecord::Base.configurations["test"] = YAML.load_file(test_dir.join("database.yml"))[adapter]
52
+
53
+ if ENV['AR_VERSION'].to_f >= 6.0
54
+ yaml_config = YAML.load_file(test_dir.join("database.yml"))[adapter]
55
+ config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", adapter, yaml_config)
56
+ ActiveRecord::Base.configurations.configurations << config
57
+ else
58
+ ActiveRecord::Base.configurations["test"] = YAML.load_file(test_dir.join("database.yml"))[adapter]
59
+ end
60
+
52
61
  ActiveRecord::Base.default_timezone = :utc
53
62
 
54
63
  require "activerecord-import"
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.6
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Dennis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-01 00:00:00.000000000 Z
11
+ date: 2021-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -45,10 +45,10 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".github/workflows/test.yaml"
48
49
  - ".gitignore"
49
50
  - ".rubocop.yml"
50
51
  - ".rubocop_todo.yml"
51
- - ".travis.yml"
52
52
  - Brewfile
53
53
  - CHANGELOG.md
54
54
  - Gemfile
@@ -67,7 +67,7 @@ files:
67
67
  - benchmarks/models/test_innodb.rb
68
68
  - benchmarks/models/test_memory.rb
69
69
  - benchmarks/models/test_myisam.rb
70
- - benchmarks/schema/mysql_schema.rb
70
+ - benchmarks/schema/mysql2_schema.rb
71
71
  - gemfiles/3.2.gemfile
72
72
  - gemfiles/4.0.gemfile
73
73
  - gemfiles/4.1.gemfile
@@ -114,6 +114,7 @@ files:
114
114
  - test/adapters/spatialite.rb
115
115
  - test/adapters/sqlite3.rb
116
116
  - test/database.yml.sample
117
+ - test/github/database.yml
117
118
  - test/import_test.rb
118
119
  - test/jdbcmysql/import_test.rb
119
120
  - test/jdbcpostgresql/import_test.rb
@@ -125,11 +126,14 @@ files:
125
126
  - test/models/bike_maker.rb
126
127
  - test/models/book.rb
127
128
  - test/models/car.rb
129
+ - test/models/card.rb
128
130
  - test/models/chapter.rb
131
+ - test/models/deck.rb
129
132
  - test/models/dictionary.rb
130
133
  - test/models/discount.rb
131
134
  - test/models/end_note.rb
132
135
  - test/models/group.rb
136
+ - test/models/playing_card.rb
133
137
  - test/models/promotion.rb
134
138
  - test/models/question.rb
135
139
  - test/models/rule.rb
@@ -164,10 +168,9 @@ files:
164
168
  - test/support/sqlite3/import_examples.rb
165
169
  - test/synchronize_test.rb
166
170
  - test/test_helper.rb
167
- - test/travis/database.yml
168
171
  - test/value_sets_bytes_parser_test.rb
169
172
  - test/value_sets_records_parser_test.rb
170
- homepage: http://github.com/zdennis/activerecord-import
173
+ homepage: https://github.com/zdennis/activerecord-import
171
174
  licenses:
172
175
  - MIT
173
176
  metadata: {}
@@ -186,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
189
  - !ruby/object:Gem::Version
187
190
  version: '0'
188
191
  requirements: []
189
- rubygems_version: 3.0.6
192
+ rubygems_version: 3.0.9
190
193
  signing_key:
191
194
  specification_version: 4
192
195
  summary: Bulk insert extension for ActiveRecord
@@ -205,6 +208,7 @@ test_files:
205
208
  - test/adapters/spatialite.rb
206
209
  - test/adapters/sqlite3.rb
207
210
  - test/database.yml.sample
211
+ - test/github/database.yml
208
212
  - test/import_test.rb
209
213
  - test/jdbcmysql/import_test.rb
210
214
  - test/jdbcpostgresql/import_test.rb
@@ -216,11 +220,14 @@ test_files:
216
220
  - test/models/bike_maker.rb
217
221
  - test/models/book.rb
218
222
  - test/models/car.rb
223
+ - test/models/card.rb
219
224
  - test/models/chapter.rb
225
+ - test/models/deck.rb
220
226
  - test/models/dictionary.rb
221
227
  - test/models/discount.rb
222
228
  - test/models/end_note.rb
223
229
  - test/models/group.rb
230
+ - test/models/playing_card.rb
224
231
  - test/models/promotion.rb
225
232
  - test/models/question.rb
226
233
  - test/models/rule.rb
@@ -255,6 +262,5 @@ test_files:
255
262
  - test/support/sqlite3/import_examples.rb
256
263
  - test/synchronize_test.rb
257
264
  - test/test_helper.rb
258
- - test/travis/database.yml
259
265
  - test/value_sets_bytes_parser_test.rb
260
266
  - test/value_sets_records_parser_test.rb
data/.travis.yml DELETED
@@ -1,74 +0,0 @@
1
- language: ruby
2
- cache: bundler
3
- rvm:
4
- - 2.5.5
5
-
6
- env:
7
- global:
8
- # https://github.com/discourse/discourse/blob/master/.travis.yml
9
- - RUBY_GC_MALLOC_LIMIT=50000000
10
- matrix:
11
- - AR_VERSION=5.1
12
- - AR_VERSION=5.2
13
- - AR_VERSION=6.0
14
-
15
- matrix:
16
- include:
17
- - rvm: 2.3.8
18
- env: AR_VERSION=3.2
19
- - rvm: 2.3.8
20
- env: AR_VERSION=4.0
21
- - rvm: 2.3.8
22
- env: AR_VERSION=4.1
23
- - rvm: 2.3.8
24
- env: AR_VERSION=4.2
25
- - rvm: 2.3.8
26
- env: AR_VERSION=5.0
27
-
28
- fast_finish: true
29
-
30
- addons:
31
- postgresql: "9.5"
32
- apt:
33
- sources:
34
- - travis-ci/sqlite3
35
- - mysql-5.7-trusty
36
- packages:
37
- - sqlite3
38
- - mysql-server
39
- - mysql-client
40
- - postgresql-9.5-postgis-2.4
41
-
42
- before_install:
43
- - gem update --system
44
- - sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;"
45
- - sudo mysql_upgrade
46
- - sudo service mysql restart
47
-
48
- before_script:
49
- - mysql -e 'create database activerecord_import_test;'
50
- - psql -c 'create database activerecord_import_test;' -U postgres
51
- - psql activerecord_import_test -c 'create extension if not exists hstore;' -U postgres
52
- - psql -c 'create extension if not exists postgis;' -U postgres
53
- - psql -c 'create extension if not exists "uuid-ossp";' -U postgres
54
- - cp test/travis/database.yml test/database.yml
55
-
56
- script:
57
- - bundle exec rake test:mysql2
58
- - bundle exec rake test:mysql2_makara
59
- - bundle exec rake test:mysql2spatial
60
- - bundle exec rake test:postgis
61
- - bundle exec rake test:postgresql
62
- - bundle exec rake test:postgresql_makara
63
- - bundle exec rake test:seamless_database_pool
64
- - bundle exec rake test:spatialite
65
- - bundle exec rake test:sqlite3
66
- - bundle exec rubocop
67
-
68
- dist: xenial
69
-
70
- services:
71
- - mysql
72
- - postgresql
73
-
74
- sudo: required