activerecord-import 1.0.6 → 1.2.0

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