activerecord-import 1.8.0 → 2.0.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 +4 -4
- data/.github/workflows/test.yaml +17 -5
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +5 -11
- data/README.markdown +2 -2
- data/benchmarks/benchmark.rb +0 -2
- data/benchmarks/lib/base.rb +6 -3
- data/benchmarks/lib/cli_parser.rb +8 -6
- data/gemfiles/8.0.gemfile +3 -0
- data/lib/activerecord-import/import.rb +7 -9
- data/lib/activerecord-import/version.rb +1 -1
- data/test/github/database.yml +2 -2
- data/test/import_test.rb +15 -39
- data/test/models/author.rb +3 -1
- data/test/models/book.rb +6 -2
- data/test/models/composite_book.rb +1 -1
- data/test/models/composite_chapter.rb +4 -1
- data/test/models/customer.rb +1 -1
- data/test/models/order.rb +1 -1
- data/test/models/tag_alias.rb +1 -1
- data/test/models/topic.rb +1 -0
- data/test/support/active_support/test_case_extensions.rb +1 -5
- data/test/support/mysql/import_examples.rb +6 -8
- data/test/support/postgresql/import_examples.rb +37 -53
- data/test/support/shared_examples/recursive_import.rb +39 -0
- data/test/test_helper.rb +7 -20
- metadata +7 -12
- data/gemfiles/4.2.gemfile +0 -4
- data/gemfiles/5.0.gemfile +0 -4
- data/gemfiles/5.1.gemfile +0 -4
- data/lib/activerecord-import/mysql2.rb +0 -9
- data/lib/activerecord-import/postgresql.rb +0 -9
- data/lib/activerecord-import/sqlite3.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f55c9faf85e948fbeb480ebca5baeca11b2275a626bc6ba7517e9d3e8c37e9e7
|
4
|
+
data.tar.gz: 80b267b08ef3a10bb91b029401e8fb6b49fdfcadeeb29a2e5e18fd6c8f529192
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f838da07331afe7827ad7d4f323ebf3f2191decd59a82359393f20eee866345258abb979875dd1c9b853533099797cdf7599ded20ca4e07997f8bce491f4c03e
|
7
|
+
data.tar.gz: 5a257bacb43492f25a65ecc382fe8ad126c4dac474eae7ee2beb3ba92c2cb95e64070a40cd20c48e47f46b51e64b69c2addd76a15fed75863df2ecb4a0fbdb70
|
data/.github/workflows/test.yaml
CHANGED
@@ -21,6 +21,7 @@ jobs:
|
|
21
21
|
ports:
|
22
22
|
- 3306:3306
|
23
23
|
env:
|
24
|
+
MYSQL_HOST: 127.0.0.1
|
24
25
|
MYSQL_ROOT_PASSWORD: root
|
25
26
|
MYSQL_USER: github
|
26
27
|
MYSQL_PASSWORD: github
|
@@ -36,6 +37,8 @@ jobs:
|
|
36
37
|
ruby:
|
37
38
|
- 3.3
|
38
39
|
env:
|
40
|
+
- AR_VERSION: '8.0'
|
41
|
+
RUBYOPT: --enable-frozen-string-literal
|
39
42
|
- AR_VERSION: '7.2'
|
40
43
|
RUBYOPT: --enable-frozen-string-literal
|
41
44
|
- AR_VERSION: '7.1'
|
@@ -45,6 +48,9 @@ jobs:
|
|
45
48
|
- AR_VERSION: 6.1
|
46
49
|
RUBYOPT: --enable-frozen-string-literal
|
47
50
|
include:
|
51
|
+
- ruby: 3.2
|
52
|
+
env:
|
53
|
+
AR_VERSION: '8.0'
|
48
54
|
- ruby: 3.2
|
49
55
|
env:
|
50
56
|
AR_VERSION: '7.2'
|
@@ -72,7 +78,7 @@ jobs:
|
|
72
78
|
- ruby: '3.0'
|
73
79
|
env:
|
74
80
|
AR_VERSION: 6.1
|
75
|
-
- ruby: jruby-9.4.
|
81
|
+
- ruby: jruby-9.4.8.0
|
76
82
|
env:
|
77
83
|
AR_VERSION: '7.0'
|
78
84
|
- ruby: 2.7
|
@@ -84,7 +90,7 @@ jobs:
|
|
84
90
|
- ruby: 2.7
|
85
91
|
env:
|
86
92
|
AR_VERSION: '6.0'
|
87
|
-
- ruby: jruby-9.3.
|
93
|
+
- ruby: jruby-9.3.15.0
|
88
94
|
env:
|
89
95
|
AR_VERSION: '6.1'
|
90
96
|
- ruby: 2.6
|
@@ -96,6 +102,10 @@ jobs:
|
|
96
102
|
DB_DATABASE: activerecord_import_test
|
97
103
|
steps:
|
98
104
|
- uses: actions/checkout@v4
|
105
|
+
- name: Install SQLite3 Development Library
|
106
|
+
run: |
|
107
|
+
sudo apt-get update
|
108
|
+
sudo apt-get install libsqlite3-dev
|
99
109
|
- uses: ruby/setup-ruby@v1
|
100
110
|
with:
|
101
111
|
ruby-version: ${{ matrix.ruby }}
|
@@ -103,8 +113,6 @@ jobs:
|
|
103
113
|
rubygems: latest
|
104
114
|
- name: Set up databases
|
105
115
|
run: |
|
106
|
-
sudo /etc/init.d/mysql start
|
107
|
-
mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }} CHARACTER SET utf8 COLLATE utf8_general_ci;' -u root -proot
|
108
116
|
psql -h localhost -U postgres -c 'create database ${{ env.DB_DATABASE }};'
|
109
117
|
psql -h localhost -U postgres -d ${{ env.DB_DATABASE }} -c 'create extension if not exists hstore;'
|
110
118
|
psql -h localhost -U postgres -c 'create extension if not exists postgis;'
|
@@ -139,9 +147,13 @@ jobs:
|
|
139
147
|
AR_VERSION: '7.0'
|
140
148
|
steps:
|
141
149
|
- uses: actions/checkout@v4
|
150
|
+
- name: Install SQLite3 Development Library
|
151
|
+
run: |
|
152
|
+
sudo apt-get update
|
153
|
+
sudo apt-get install libsqlite3-dev
|
142
154
|
- uses: ruby/setup-ruby@v1
|
143
155
|
with:
|
144
|
-
ruby-version:
|
156
|
+
ruby-version: 3.0
|
145
157
|
bundler-cache: true
|
146
158
|
- name: Run Rubocop
|
147
159
|
run: bundle exec rubocop
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## Changes in 2.0.0
|
2
|
+
|
3
|
+
### Breaking Changes
|
4
|
+
|
5
|
+
* Fix `recursive_on_duplicate_key_update` doesn't work with non-standard
|
6
|
+
association name. Thanks to @jacob-carlborg-apoex via \#852. The documentation for the
|
7
|
+
`:recursive_on_duplicate_key_update` option specifies that the hash key is
|
8
|
+
the association name. But previously the name of associated table was used to
|
9
|
+
look up the options. Now the behavior matches the documentation and the name
|
10
|
+
of the association is used instead. This only affects associations that uses
|
11
|
+
a name that doesn't follow the ActiveRecord naming conventions of
|
12
|
+
associations and class names, i.e. when the `class_name:` option is used on
|
13
|
+
an association.
|
14
|
+
|
15
|
+
## Changes in 1.8.1
|
16
|
+
|
17
|
+
### Fixes
|
18
|
+
|
19
|
+
* Further update for ActiveRecord 7.2 compatibility when running validations. Thanks to @denisahearn via \##847.
|
20
|
+
|
1
21
|
## Changes in 1.8.0
|
2
22
|
|
3
23
|
### New Features
|
data/Gemfile
CHANGED
@@ -6,13 +6,15 @@ gemspec
|
|
6
6
|
|
7
7
|
version = ENV['AR_VERSION'].to_f
|
8
8
|
|
9
|
-
mysql2_version = '0.
|
10
|
-
mysql2_version = '0.4.0' if version >= 4.2
|
9
|
+
mysql2_version = '0.4.0'
|
11
10
|
mysql2_version = '0.5.0' if version >= 6.1
|
11
|
+
mysql2_version = '0.5.6' if version >= 8.0
|
12
12
|
sqlite3_version = '1.3.0'
|
13
13
|
sqlite3_version = '1.4.0' if version >= 6.0
|
14
|
+
sqlite3_version = '2.2.0' if version >= 8.0
|
14
15
|
pg_version = '0.9'
|
15
16
|
pg_version = '1.1' if version >= 6.1
|
17
|
+
pg_version = '1.5' if version >= 8.0
|
16
18
|
|
17
19
|
group :development, :test do
|
18
20
|
gem 'rubocop'
|
@@ -51,19 +53,11 @@ gem "chronic"
|
|
51
53
|
gem "mocha", "~> 2.1.0"
|
52
54
|
|
53
55
|
# Debugging
|
54
|
-
platforms :jruby do
|
55
|
-
gem "ruby-debug", "= 0.10.4"
|
56
|
-
end
|
57
|
-
|
58
56
|
platforms :ruby do
|
59
57
|
gem "pry-byebug"
|
60
58
|
gem "pry", "~> 0.14.0"
|
61
59
|
end
|
62
60
|
|
63
|
-
|
64
|
-
gem "minitest"
|
65
|
-
else
|
66
|
-
gem "test-unit"
|
67
|
-
end
|
61
|
+
gem "minitest"
|
68
62
|
|
69
63
|
eval_gemfile File.expand_path("../gemfiles/#{version}.gemfile", __FILE__)
|
data/README.markdown
CHANGED
@@ -569,11 +569,11 @@ require 'activerecord-import'
|
|
569
569
|
### Load Path Setup
|
570
570
|
To understand how rubygems loads code you can reference the following:
|
571
571
|
|
572
|
-
|
572
|
+
https://guides.rubygems.org/patterns/#loading-code
|
573
573
|
|
574
574
|
And an example of how active_record dynamically load adapters:
|
575
575
|
|
576
|
-
https://github.com/rails/rails/blob/
|
576
|
+
https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters.rb
|
577
577
|
|
578
578
|
In summary, when a gem is loaded rubygems adds the `lib` folder of the gem to the global load path `$LOAD_PATH` so that all `require` lookups will not propagate through all of the folders on the load path. When a `require` is issued each folder on the `$LOAD_PATH` is checked for the file and/or folder referenced. This allows a gem (like activerecord-import) to define push the activerecord-import folder (or namespace) on the `$LOAD_PATH` and any adapters provided by activerecord-import will be found by rubygems when the require is issued.
|
579
579
|
|
data/benchmarks/benchmark.rb
CHANGED
@@ -44,8 +44,6 @@ require adapter_schema if File.exist?(adapter_schema)
|
|
44
44
|
Dir["#{File.dirname(__FILE__)}/models/*.rb"].sort.each { |file| require file }
|
45
45
|
|
46
46
|
require File.join( benchmark_dir, 'lib', "#{options.adapter}_benchmark" )
|
47
|
-
|
48
|
-
table_types = nil
|
49
47
|
table_types = if options.benchmark_all_types
|
50
48
|
["all"]
|
51
49
|
else
|
data/benchmarks/lib/base.rb
CHANGED
@@ -16,7 +16,7 @@ class BenchmarkBase
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
# Returns
|
19
|
+
# Returns a struct which contains two attritues, +description+ and +tms+ after performing an
|
20
20
|
# actual benchmark.
|
21
21
|
#
|
22
22
|
# == PARAMETERS
|
@@ -24,9 +24,12 @@ class BenchmarkBase
|
|
24
24
|
# * blk - the block of code to benchmark
|
25
25
|
#
|
26
26
|
# == RETURNS
|
27
|
-
#
|
27
|
+
# A struct object with the following attributes:
|
28
28
|
# * description - the description of the benchmark ran
|
29
29
|
# * tms - a Benchmark::Tms containing the results of the benchmark
|
30
|
+
|
31
|
+
BmStruct = Struct.new( :description, :tms, :failed, keyword_init: true )
|
32
|
+
|
30
33
|
def bm( description, &block )
|
31
34
|
tms = nil
|
32
35
|
puts "Benchmarking #{description}"
|
@@ -35,7 +38,7 @@ class BenchmarkBase
|
|
35
38
|
delete_all
|
36
39
|
failed = false
|
37
40
|
|
38
|
-
|
41
|
+
BmStruct.new( description: description, tms: tms, failed: failed )
|
39
42
|
end
|
40
43
|
|
41
44
|
# Given a model class (ie: Topic), and an array of columns and value sets
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'optparse'
|
4
|
-
require 'ostruct'
|
5
4
|
|
6
5
|
#
|
7
6
|
# == PARAMETERS
|
@@ -10,7 +9,7 @@ require 'ostruct'
|
|
10
9
|
# * t - the table types to test. ie: myisam, innodb, memory, temporary, etc.
|
11
10
|
#
|
12
11
|
module BenchmarkOptionParser
|
13
|
-
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options."
|
12
|
+
BANNER = "Usage: ruby #{$0} [options]\nSee ruby #{$0} -h for more options.".freeze
|
14
13
|
|
15
14
|
def self.print_banner
|
16
15
|
puts BANNER
|
@@ -38,8 +37,11 @@ module BenchmarkOptionParser
|
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
40
|
+
OptionsStruct = Struct.new( :adapter, :table_types, :delete_on_finish, :number_of_objects, :outputs,
|
41
|
+
:benchmark_all_types, keyword_init: true )
|
42
|
+
OutputStruct = Struct.new( :format, :filename, keyword_init: true )
|
41
43
|
def self.parse( args )
|
42
|
-
options =
|
44
|
+
options = OptionsStruct.new(
|
43
45
|
adapter: 'mysql2',
|
44
46
|
table_types: {},
|
45
47
|
delete_on_finish: true,
|
@@ -81,12 +83,12 @@ module BenchmarkOptionParser
|
|
81
83
|
|
82
84
|
# print results in CSV format
|
83
85
|
opts.on( "--to-csv [String]", "Print results in a CSV file format" ) do |filename|
|
84
|
-
options.outputs <<
|
86
|
+
options.outputs << OutputStruct.new( format: 'csv', filename: filename)
|
85
87
|
end
|
86
88
|
|
87
89
|
# print results in HTML format
|
88
90
|
opts.on( "--to-html [String]", "Print results in HTML format" ) do |filename|
|
89
|
-
options.outputs <<
|
91
|
+
options.outputs << OutputStruct.new( format: 'html', filename: filename )
|
90
92
|
end
|
91
93
|
end # end opt.parse!
|
92
94
|
|
@@ -100,7 +102,7 @@ module BenchmarkOptionParser
|
|
100
102
|
end
|
101
103
|
|
102
104
|
options.number_of_objects = [1000] if options.number_of_objects.empty?
|
103
|
-
options.outputs = [
|
105
|
+
options.outputs = [OutputStruct.new( format: 'html', filename: 'benchmark.html')] if options.outputs.empty?
|
104
106
|
|
105
107
|
print_options( options )
|
106
108
|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "ostruct"
|
4
|
-
|
5
3
|
module ActiveRecord::Import::ConnectionAdapters; end
|
6
4
|
|
7
5
|
module ActiveRecord::Import # :nodoc:
|
@@ -94,7 +92,7 @@ module ActiveRecord::Import # :nodoc:
|
|
94
92
|
env = ActiveSupport::Callbacks::Filters::Environment.new(model, false, nil)
|
95
93
|
if runner.respond_to?(:call) # ActiveRecord < 5.1
|
96
94
|
runner.call(env)
|
97
|
-
else # ActiveRecord 5.1
|
95
|
+
else # ActiveRecord >= 5.1
|
98
96
|
# Note that this is a gross simplification of ActiveSupport::Callbacks#run_callbacks.
|
99
97
|
# It's technically possible for there to exist an "around" callback in the
|
100
98
|
# :validate chain, but this would be an aberration, since Rails doesn't define
|
@@ -107,7 +105,8 @@ module ActiveRecord::Import # :nodoc:
|
|
107
105
|
# no real-world use case for it.
|
108
106
|
raise "The :validate callback chain contains an 'around' callback, which is unsupported" unless runner.final?
|
109
107
|
runner.invoke_before(env)
|
110
|
-
|
108
|
+
# Ensure a truthy value is returned. ActiveRecord < 7.2 always returned an array.
|
109
|
+
runner.invoke_after(env) || []
|
111
110
|
end
|
112
111
|
elsif @validate_callbacks.method(:compile).arity == 0 # ActiveRecord = 4.0
|
113
112
|
model.instance_eval @validate_callbacks.compile
|
@@ -858,12 +857,11 @@ class ActiveRecord::Base
|
|
858
857
|
|
859
858
|
private
|
860
859
|
|
861
|
-
def associated_options(options,
|
860
|
+
def associated_options(options, association)
|
862
861
|
return options unless options.key?(:recursive_on_duplicate_key_update)
|
863
862
|
|
864
|
-
table_name = associated_class.arel_table.name.to_sym
|
865
863
|
options.merge(
|
866
|
-
on_duplicate_key_update: options[:recursive_on_duplicate_key_update][
|
864
|
+
on_duplicate_key_update: options[:recursive_on_duplicate_key_update][association]
|
867
865
|
)
|
868
866
|
end
|
869
867
|
|
@@ -972,12 +970,12 @@ class ActiveRecord::Base
|
|
972
970
|
options.delete(:returning)
|
973
971
|
|
974
972
|
associated_objects_by_class.each_value do |associations|
|
975
|
-
associations.
|
973
|
+
associations.each do |association, associated_records|
|
976
974
|
next if associated_records.empty?
|
977
975
|
|
978
976
|
associated_class = associated_records.first.class
|
979
977
|
associated_class.bulk_import(associated_records,
|
980
|
-
associated_options(options,
|
978
|
+
associated_options(options, association))
|
981
979
|
end
|
982
980
|
end
|
983
981
|
end
|
data/test/github/database.yml
CHANGED
@@ -3,7 +3,7 @@ common: &common
|
|
3
3
|
password: root
|
4
4
|
encoding: utf8
|
5
5
|
collation: utf8_general_ci
|
6
|
-
host:
|
6
|
+
host: 127.0.0.1
|
7
7
|
database: activerecord_import_test
|
8
8
|
|
9
9
|
jdbcpostgresql: &postgresql
|
@@ -54,7 +54,7 @@ seamless_database_pool:
|
|
54
54
|
pool_adapter: mysql2
|
55
55
|
prepared_statements: false
|
56
56
|
master:
|
57
|
-
host:
|
57
|
+
host: 127.0.0.1
|
58
58
|
|
59
59
|
sqlite:
|
60
60
|
adapter: sqlite
|
data/test/import_test.rb
CHANGED
@@ -741,14 +741,8 @@ describe "#import" do
|
|
741
741
|
]
|
742
742
|
Book.import books
|
743
743
|
assert_equal 2, Book.count
|
744
|
-
|
745
|
-
|
746
|
-
assert_equal 'draft', Book.first.read_attribute('status')
|
747
|
-
assert_equal 'published', Book.last.read_attribute('status')
|
748
|
-
else
|
749
|
-
assert_equal 0, Book.first.read_attribute('status')
|
750
|
-
assert_equal 1, Book.last.read_attribute('status')
|
751
|
-
end
|
744
|
+
assert_equal 'draft', Book.first.read_attribute('status')
|
745
|
+
assert_equal 'published', Book.last.read_attribute('status')
|
752
746
|
end
|
753
747
|
|
754
748
|
it 'should be able to import enum fields with default value' do
|
@@ -758,32 +752,19 @@ describe "#import" do
|
|
758
752
|
]
|
759
753
|
Book.import books
|
760
754
|
assert_equal 1, Book.count
|
761
|
-
|
762
|
-
if ENV['AR_VERSION'].to_i >= 5.0
|
763
|
-
assert_equal 'draft', Book.first.read_attribute('status')
|
764
|
-
else
|
765
|
-
assert_equal 0, Book.first.read_attribute('status')
|
766
|
-
end
|
755
|
+
assert_equal 'draft', Book.first.read_attribute('status')
|
767
756
|
end
|
768
757
|
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
if ENV['AR_VERSION'].to_i >= 5.0
|
780
|
-
assert_equal 'draft', Book.first.read_attribute('status')
|
781
|
-
assert_equal 'published', Book.last.read_attribute('status')
|
782
|
-
else
|
783
|
-
assert_equal 0, Book.first.read_attribute('status')
|
784
|
-
assert_equal 1, Book.last.read_attribute('status')
|
785
|
-
end
|
786
|
-
end
|
758
|
+
it 'should be able to import enum fields by name' do
|
759
|
+
Book.delete_all if Book.count > 0
|
760
|
+
books = [
|
761
|
+
Book.new(author_name: "Foo", title: "Baz", status: :draft),
|
762
|
+
Book.new(author_name: "Foo2", title: "Baz2", status: :published),
|
763
|
+
]
|
764
|
+
Book.import books
|
765
|
+
assert_equal 2, Book.count
|
766
|
+
assert_equal 'draft', Book.first.read_attribute('status')
|
767
|
+
assert_equal 'published', Book.last.read_attribute('status')
|
787
768
|
end
|
788
769
|
end
|
789
770
|
|
@@ -796,13 +777,8 @@ describe "#import" do
|
|
796
777
|
Book.import columns, values
|
797
778
|
assert_equal 2, Book.count
|
798
779
|
|
799
|
-
|
800
|
-
|
801
|
-
assert_equal 'published', Book.last.read_attribute('status')
|
802
|
-
else
|
803
|
-
assert_equal 0, Book.first.read_attribute('status')
|
804
|
-
assert_equal 1, Book.last.read_attribute('status')
|
805
|
-
end
|
780
|
+
assert_equal 'draft', Book.first.read_attribute('status')
|
781
|
+
assert_equal 'published', Book.last.read_attribute('status')
|
806
782
|
end
|
807
783
|
end
|
808
784
|
|
data/test/models/author.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Author < ActiveRecord::Base
|
4
|
-
if ENV['AR_VERSION'].to_f >=
|
4
|
+
if ENV['AR_VERSION'].to_f >= 8.0
|
5
|
+
has_many :composite_books, foreign_key: [:id, :author_id], inverse_of: :author
|
6
|
+
elsif ENV['AR_VERSION'].to_f >= 7.1
|
5
7
|
has_many :composite_books, query_constraints: [:id, :author_id], inverse_of: :author
|
6
8
|
end
|
7
9
|
end
|
data/test/models/book.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class Book < ActiveRecord::Base
|
4
4
|
belongs_to :topic, inverse_of: :books
|
5
|
-
if ENV['AR_VERSION'].to_f <= 7.0
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0 || ENV['AR_VERSION'].to_f >= 8.0
|
6
6
|
belongs_to :tag, foreign_key: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
7
7
|
else
|
8
8
|
belongs_to :tag, query_constraints: [:tag_id, :parent_id] unless ENV["SKIP_COMPOSITE_PK"]
|
@@ -10,5 +10,9 @@ class Book < ActiveRecord::Base
|
|
10
10
|
has_many :chapters, inverse_of: :book
|
11
11
|
has_many :discounts, as: :discountable
|
12
12
|
has_many :end_notes, inverse_of: :book
|
13
|
-
|
13
|
+
if ENV['AR_VERSION'].to_f >= 8.0
|
14
|
+
enum :status, [:draft, :published]
|
15
|
+
else
|
16
|
+
enum status: [:draft, :published]
|
17
|
+
end
|
14
18
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
class CompositeBook < ActiveRecord::Base
|
4
4
|
self.primary_key = %i[id author_id]
|
5
5
|
belongs_to :author
|
6
|
-
if ENV['AR_VERSION'].to_f <= 7.0
|
6
|
+
if ENV['AR_VERSION'].to_f <= 7.0 || ENV['AR_VERSION'].to_f >= 8.0
|
7
7
|
unless ENV["SKIP_COMPOSITE_PK"]
|
8
8
|
has_many :composite_chapters, inverse_of: :composite_book,
|
9
9
|
foreign_key: [:id, :author_id]
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class CompositeChapter < ActiveRecord::Base
|
4
|
-
if ENV['AR_VERSION'].to_f >=
|
4
|
+
if ENV['AR_VERSION'].to_f >= 8.0
|
5
|
+
belongs_to :composite_book, inverse_of: :composite_chapters,
|
6
|
+
foreign_key: [:composite_book_id, :author_id]
|
7
|
+
elsif ENV['AR_VERSION'].to_f >= 7.1
|
5
8
|
belongs_to :composite_book, inverse_of: :composite_chapters,
|
6
9
|
query_constraints: [:composite_book_id, :author_id]
|
7
10
|
end
|
data/test/models/customer.rb
CHANGED
data/test/models/order.rb
CHANGED
data/test/models/tag_alias.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class TagAlias < ActiveRecord::Base
|
4
4
|
unless ENV["SKIP_COMPOSITE_PK"]
|
5
|
-
if ENV['AR_VERSION'].to_f <= 7.0
|
5
|
+
if ENV['AR_VERSION'].to_f <= 7.0 || ENV['AR_VERSION'].to_f >= 8.0
|
6
6
|
belongs_to :tag, foreign_key: [:tag_id, :parent_id], required: true
|
7
7
|
else
|
8
8
|
belongs_to :tag, query_constraints: [:tag_id, :parent_id], required: true
|
data/test/models/topic.rb
CHANGED
@@ -16,6 +16,7 @@ class Topic < ActiveRecord::Base
|
|
16
16
|
before_validation -> { errors.add(:title, :invalid) if title == 'invalid' }
|
17
17
|
|
18
18
|
has_many :books, inverse_of: :topic
|
19
|
+
has_many :novels, inverse_of: :topic, class_name: "Book"
|
19
20
|
belongs_to :parent, class_name: "Topic"
|
20
21
|
|
21
22
|
composed_of :description, mapping: [%w(title title), %w(author_name author_name)], allow_nil: true, class_name: "TopicDescription"
|
@@ -3,11 +3,7 @@
|
|
3
3
|
class ActiveSupport::TestCase
|
4
4
|
include ActiveRecord::TestFixtures
|
5
5
|
|
6
|
-
|
7
|
-
self.use_transactional_tests = true
|
8
|
-
else
|
9
|
-
self.use_transactional_fixtures = true
|
10
|
-
end
|
6
|
+
self.use_transactional_tests = true
|
11
7
|
|
12
8
|
class << self
|
13
9
|
def requires_active_record_version(version_string, &blk)
|
@@ -84,14 +84,12 @@ def should_support_mysql_import_functionality
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
Book.import books
|
94
|
-
end
|
87
|
+
context "with virtual columns" do
|
88
|
+
let(:books) { [Book.new(author_name: "foo", title: "bar")] }
|
89
|
+
|
90
|
+
it "ignores virtual columns and creates record" do
|
91
|
+
assert_difference "Book.count", +1 do
|
92
|
+
Book.import books
|
95
93
|
end
|
96
94
|
end
|
97
95
|
end
|
@@ -38,10 +38,8 @@ def should_support_postgresql_import_functionality
|
|
38
38
|
assert !topic.changed?
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
assert topic.previous_changes.present?
|
44
|
-
end
|
41
|
+
it "moves the dirty changes to previous_changes" do
|
42
|
+
assert topic.previous_changes.present?
|
45
43
|
end
|
46
44
|
|
47
45
|
it "marks models as persisted" do
|
@@ -96,15 +94,9 @@ def should_support_postgresql_import_functionality
|
|
96
94
|
describe "returning" do
|
97
95
|
let(:books) { [Book.new(author_name: "King", title: "It")] }
|
98
96
|
let(:result) { Book.import(books, returning: %w(author_name title)) }
|
99
|
-
let(:book_id)
|
100
|
-
|
101
|
-
|
102
|
-
else
|
103
|
-
books.first.id.to_s
|
104
|
-
end
|
105
|
-
end
|
106
|
-
let(:true_returning_value) { ENV['AR_VERSION'].to_f >= 5.0 ? true : 't' }
|
107
|
-
let(:false_returning_value) { ENV['AR_VERSION'].to_f >= 5.0 ? false : 'f' }
|
97
|
+
let(:book_id) { books.first.id }
|
98
|
+
let(:true_returning_value) { true }
|
99
|
+
let(:false_returning_value) { false }
|
108
100
|
|
109
101
|
it "creates records" do
|
110
102
|
assert_difference("Book.count", +1) { result }
|
@@ -222,23 +214,21 @@ def should_support_postgresql_import_functionality
|
|
222
214
|
end
|
223
215
|
end
|
224
216
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
let(:vendors) { [vendor] }
|
217
|
+
describe "with a uuid primary key" do
|
218
|
+
let(:vendor) { Vendor.new(name: "foo") }
|
219
|
+
let(:vendors) { [vendor] }
|
229
220
|
|
230
|
-
|
231
|
-
|
232
|
-
Vendor.import vendors
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
it "assigns an id to the model objects" do
|
221
|
+
it "creates records" do
|
222
|
+
assert_difference "Vendor.count", +1 do
|
237
223
|
Vendor.import vendors
|
238
|
-
assert_not_nil vendor.id
|
239
224
|
end
|
240
225
|
end
|
241
226
|
|
227
|
+
it "assigns an id to the model objects" do
|
228
|
+
Vendor.import vendors
|
229
|
+
assert_not_nil vendor.id
|
230
|
+
end
|
231
|
+
|
242
232
|
describe "with an assigned uuid primary key" do
|
243
233
|
let(:id) { SecureRandom.uuid }
|
244
234
|
let(:vendor) { Vendor.new(id: id, name: "foo") }
|
@@ -254,44 +244,38 @@ def should_support_postgresql_import_functionality
|
|
254
244
|
end
|
255
245
|
|
256
246
|
describe "with store accessor fields" do
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
Vendor.import vendors
|
262
|
-
end
|
263
|
-
assert_equal(100, Vendor.first.size)
|
247
|
+
it "imports values for json fields" do
|
248
|
+
vendors = [Vendor.new(name: 'Vendor 1', size: 100)]
|
249
|
+
assert_difference "Vendor.count", +1 do
|
250
|
+
Vendor.import vendors
|
264
251
|
end
|
252
|
+
assert_equal(100, Vendor.first.size)
|
253
|
+
end
|
265
254
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
end
|
271
|
-
assert_equal('John Smith', Vendor.first.contact)
|
255
|
+
it "imports values for hstore fields" do
|
256
|
+
vendors = [Vendor.new(name: 'Vendor 1', contact: 'John Smith')]
|
257
|
+
assert_difference "Vendor.count", +1 do
|
258
|
+
Vendor.import vendors
|
272
259
|
end
|
260
|
+
assert_equal('John Smith', Vendor.first.contact)
|
273
261
|
end
|
274
262
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
Vendor.import vendors
|
280
|
-
end
|
281
|
-
assert_equal('12345', Vendor.first.charge_code)
|
263
|
+
it "imports values for jsonb fields" do
|
264
|
+
vendors = [Vendor.new(name: 'Vendor 1', charge_code: '12345')]
|
265
|
+
assert_difference "Vendor.count", +1 do
|
266
|
+
Vendor.import vendors
|
282
267
|
end
|
268
|
+
assert_equal('12345', Vendor.first.charge_code)
|
283
269
|
end
|
284
270
|
end
|
285
271
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
Vendor.import vendors
|
292
|
-
end
|
293
|
-
assert_equal({}, Vendor.first.json_data)
|
272
|
+
describe "with serializable fields" do
|
273
|
+
it "imports default values as correct data type" do
|
274
|
+
vendors = [Vendor.new(name: 'Vendor 1')]
|
275
|
+
assert_difference "Vendor.count", +1 do
|
276
|
+
Vendor.import vendors
|
294
277
|
end
|
278
|
+
assert_equal({}, Vendor.first.json_data)
|
295
279
|
end
|
296
280
|
|
297
281
|
%w(json jsonb).each do |json_type|
|
@@ -278,6 +278,45 @@ def should_support_recursive_import
|
|
278
278
|
end
|
279
279
|
assert_equal new_chapter_title, Chapter.find(example_chapter.id).title
|
280
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
|
281
320
|
end
|
282
321
|
end
|
283
322
|
|
data/test/test_helper.rb
CHANGED
@@ -13,19 +13,16 @@ ENV["RAILS_ENV"] = "test"
|
|
13
13
|
require "bundler"
|
14
14
|
Bundler.setup
|
15
15
|
|
16
|
-
|
16
|
+
unless RbConfig::CONFIG["RUBY_INSTALL_NAME"] =~ /jruby/
|
17
|
+
require 'pry'
|
18
|
+
require 'pry-byebug'
|
19
|
+
end
|
17
20
|
|
18
21
|
require "active_record"
|
19
22
|
require "active_record/fixtures"
|
20
23
|
require "active_support/test_case"
|
21
|
-
|
22
|
-
|
23
|
-
require 'test/unit'
|
24
|
-
require 'mocha/test_unit'
|
25
|
-
else
|
26
|
-
require 'active_support/testing/autorun'
|
27
|
-
require "mocha/minitest"
|
28
|
-
end
|
24
|
+
require 'active_support/testing/autorun'
|
25
|
+
require "mocha/minitest"
|
29
26
|
|
30
27
|
require 'timecop'
|
31
28
|
require 'chronic'
|
@@ -38,16 +35,6 @@ rescue LoadError
|
|
38
35
|
end
|
39
36
|
end
|
40
37
|
|
41
|
-
# Support MySQL 5.7
|
42
|
-
if ActiveSupport::VERSION::STRING < "4.1"
|
43
|
-
require "active_record/connection_adapters/mysql2_adapter"
|
44
|
-
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
45
|
-
NATIVE_DATABASE_TYPES[:primary_key] = "int(11) auto_increment PRIMARY KEY"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
require "ruby-debug" if RUBY_VERSION.to_f < 1.9
|
50
|
-
|
51
38
|
adapter = ENV["ARE_DB"] || "sqlite3"
|
52
39
|
|
53
40
|
FileUtils.mkdir_p 'log'
|
@@ -99,4 +86,4 @@ Dir["#{File.dirname(__FILE__)}/models/*.rb"].sort.each { |file| require file }
|
|
99
86
|
# Prevent this deprecation warning from breaking the tests.
|
100
87
|
Rake::FileList.send(:remove_method, :import)
|
101
88
|
|
102
|
-
ActiveSupport::TestCase.test_order = :random
|
89
|
+
ActiveSupport::TestCase.test_order = :random
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -70,15 +70,13 @@ files:
|
|
70
70
|
- benchmarks/models/test_myisam.rb
|
71
71
|
- benchmarks/schema/mysql2_schema.rb
|
72
72
|
- docker-compose.yml
|
73
|
-
- gemfiles/4.2.gemfile
|
74
|
-
- gemfiles/5.0.gemfile
|
75
|
-
- gemfiles/5.1.gemfile
|
76
73
|
- gemfiles/5.2.gemfile
|
77
74
|
- gemfiles/6.0.gemfile
|
78
75
|
- gemfiles/6.1.gemfile
|
79
76
|
- gemfiles/7.0.gemfile
|
80
77
|
- gemfiles/7.1.gemfile
|
81
78
|
- gemfiles/7.2.gemfile
|
79
|
+
- gemfiles/8.0.gemfile
|
82
80
|
- lib/activerecord-import.rb
|
83
81
|
- lib/activerecord-import/active_record/adapters/abstract_adapter.rb
|
84
82
|
- lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb
|
@@ -98,9 +96,6 @@ files:
|
|
98
96
|
- lib/activerecord-import/adapters/trilogy_adapter.rb
|
99
97
|
- lib/activerecord-import/base.rb
|
100
98
|
- lib/activerecord-import/import.rb
|
101
|
-
- lib/activerecord-import/mysql2.rb
|
102
|
-
- lib/activerecord-import/postgresql.rb
|
103
|
-
- lib/activerecord-import/sqlite3.rb
|
104
99
|
- lib/activerecord-import/synchronize.rb
|
105
100
|
- lib/activerecord-import/value_sets_parser.rb
|
106
101
|
- lib/activerecord-import/version.rb
|
@@ -187,7 +182,7 @@ licenses:
|
|
187
182
|
- MIT
|
188
183
|
metadata:
|
189
184
|
changelog_uri: https://github.com/zdennis/activerecord-import/blob/master/CHANGELOG.md
|
190
|
-
post_install_message:
|
185
|
+
post_install_message:
|
191
186
|
rdoc_options: []
|
192
187
|
require_paths:
|
193
188
|
- lib
|
@@ -202,8 +197,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
202
197
|
- !ruby/object:Gem::Version
|
203
198
|
version: '0'
|
204
199
|
requirements: []
|
205
|
-
rubygems_version: 3.
|
206
|
-
signing_key:
|
200
|
+
rubygems_version: 3.0.3.1
|
201
|
+
signing_key:
|
207
202
|
specification_version: 4
|
208
203
|
summary: Bulk insert extension for ActiveRecord
|
209
204
|
test_files:
|
data/gemfiles/4.2.gemfile
DELETED
data/gemfiles/5.0.gemfile
DELETED
data/gemfiles/5.1.gemfile
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
warn <<-MSG
|
4
|
-
[DEPRECATION] loading activerecord-import via 'require "activerecord-import/<adapter-name>"'
|
5
|
-
is deprecated. Update to autorequire using 'require "activerecord-import"'. See
|
6
|
-
http://github.com/zdennis/activerecord-import/wiki/Requiring for more information
|
7
|
-
MSG
|
8
|
-
|
9
|
-
require "activerecord-import"
|
@@ -1,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
warn <<-MSG
|
4
|
-
[DEPRECATION] loading activerecord-import via 'require "activerecord-import/<adapter-name>"'
|
5
|
-
is deprecated. Update to autorequire using 'require "activerecord-import"'. See
|
6
|
-
http://github.com/zdennis/activerecord-import/wiki/Requiring for more information
|
7
|
-
MSG
|
8
|
-
|
9
|
-
require "activerecord-import"
|
@@ -1,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
warn <<-MSG
|
4
|
-
[DEPRECATION] loading activerecord-import via 'require "activerecord-import/<adapter-name>"'
|
5
|
-
is deprecated. Update to autorequire using 'require "activerecord-import"'. See
|
6
|
-
http://github.com/zdennis/activerecord-import/wiki/Requiring for more information
|
7
|
-
MSG
|
8
|
-
|
9
|
-
require "activerecord-import"
|