activerecord-import 0.20.2 → 0.21.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
  SHA1:
3
- metadata.gz: aedb604a103a355bb35e3373239fb12e1e9877cd
4
- data.tar.gz: 235a13a5819d9181aaa478686ec42596c54806fe
3
+ metadata.gz: d4441271287712d8e44b9cac793f2b8b8d315988
4
+ data.tar.gz: ec27555ffefb8a071bcf3c54b354a8b56b6e68c2
5
5
  SHA512:
6
- metadata.gz: 8e9349db11dd48246fb292215d4dd43efd2c8fe0c2e544e6fa585d4ad0aa7eb2ba612d8b7a84bbbaf991958b8c7897135314c6e73b4520d05a31861ec68ba5e7
7
- data.tar.gz: 58de95004307eb2cfe48d0a6e534313612ed90c69df08e1bdb0372cb065d857189d23fa12552d652d0fbfb4a7ba91cedcba8c7c14c44dfd72c5bff26442bf0bd
6
+ metadata.gz: fbafbd6476a5f7c3681b26e85e3f4960fb69b6065697a173426392f91f1a89c188dd83b1b16c1639645ba33b8a1fbba97a91dea7e20186692c8ca3e33b9eaf41
7
+ data.tar.gz: 54f3499be94b3c1b44a99128c39bb9f964e5bcb8da8549dbd66fe5d568a14f195d8b60f2c17a8dab9b5745ba7efb9db58950b433377aee510f4586e1a3cf2074
@@ -17,7 +17,7 @@ env:
17
17
 
18
18
  matrix:
19
19
  include:
20
- - rvm: jruby-9.1.9.0
20
+ - rvm: jruby-9.1.14.0
21
21
  env: AR_VERSION=4.2
22
22
  before_install:
23
23
  - gem update --system
@@ -1,3 +1,21 @@
1
+ ## Changes in 0.21.0
2
+
3
+ ### New Features
4
+
5
+ * Allow SQL subqueries (objects that respond to .to_sql) to be passed as values. Thanks
6
+ to @jalada, @jkowens via \#471
7
+ * Raise an ArgumentError when importing an array of hashes if any of the
8
+ hash objects have different keys. Thanks to @mbell697 via \#465.
9
+
10
+ ### Fixes
11
+
12
+ * Fix issue loading incorrect foreign key value when syncing belongs_to
13
+ associations with custom foreign key columns. Thanks to @marcgreenstock, @jkowens via \#470.
14
+ * Fix issue importing models with polymorphic belongs_to associations.
15
+ Thanks to @zorab47, @jkowens via \#476.
16
+ * Fix issue importing STI models with ActiveRecord 4.0. Thanks to
17
+ @kazuki-st, @jkowens via \#478.
18
+
1
19
  ## Changes in 0.20.2
2
20
 
3
21
  ### Fixes
data/Gemfile CHANGED
@@ -18,9 +18,9 @@ end
18
18
  platforms :jruby do
19
19
  gem "jdbc-mysql"
20
20
  gem "jdbc-postgres"
21
- gem "activerecord-jdbcsqlite3-adapter"
22
- gem "activerecord-jdbcmysql-adapter"
23
- gem "activerecord-jdbcpostgresql-adapter"
21
+ gem "activerecord-jdbcsqlite3-adapter", "~> 1.3"
22
+ gem "activerecord-jdbcmysql-adapter", "~> 1.3"
23
+ gem "activerecord-jdbcpostgresql-adapter", "~> 1.3"
24
24
  end
25
25
 
26
26
  # Support libs
@@ -1 +1,2 @@
1
1
  gem 'activerecord', '~> 5.1.0'
2
+ gem 'composite_primary_keys', '~> 10.0'
@@ -247,6 +247,9 @@ class ActiveRecord::Base
247
247
  # BlogPost.import posts
248
248
  #
249
249
  # # Example using array_of_hash_objects
250
+ # # NOTE: column_names will be determined by using the keys of the first hash in the array. If later hashes in the
251
+ # # array have different keys an exception will be raised. If you have hashes to import with different sets of keys
252
+ # # we recommend grouping these into batches before importing.
250
253
  # values = [ {author_name: 'zdennis', title: 'test post'} ], [ {author_name: 'jdoe', title: 'another test post'} ] ]
251
254
  # BlogPost.import values
252
255
  #
@@ -470,12 +473,18 @@ class ActiveRecord::Base
470
473
  if args.length == 2
471
474
  array_of_hashes = args.last
472
475
  column_names = args.first.dup
476
+ allow_extra_hash_keys = true
473
477
  else
474
478
  array_of_hashes = args.first
475
479
  column_names = array_of_hashes.first.keys
480
+ allow_extra_hash_keys = false
476
481
  end
477
482
 
478
483
  array_of_attributes = array_of_hashes.map do |h|
484
+ error_message = validate_hash_import(h, column_names, allow_extra_hash_keys)
485
+
486
+ raise ArgumentError, error_message if error_message
487
+
479
488
  column_names.map do |key|
480
489
  h[key]
481
490
  end
@@ -613,7 +622,7 @@ class ActiveRecord::Base
613
622
  next if column_names.include?(name_as_sym)
614
623
 
615
624
  is_sti = (name_as_sym == inheritance_column.to_sym && self < base_class)
616
- value = value.first if is_sti
625
+ value = Array(value).first if is_sti
617
626
 
618
627
  column_names << name_as_sym
619
628
  array_of_attributes.each { |attrs| attrs << value }
@@ -711,10 +720,12 @@ class ActiveRecord::Base
711
720
  def load_association_ids(model)
712
721
  association_reflections = model.class.reflect_on_all_associations(:belongs_to)
713
722
  association_reflections.each do |association_reflection|
723
+ next if association_reflection.options[:polymorphic]
714
724
  association = model.association(association_reflection.name)
715
725
  association = association.target
716
- if association && association.id
717
- model.public_send("#{association_reflection.foreign_key}=", association.id)
726
+ if association
727
+ association_primary_key = association_reflection.association_primary_key
728
+ model.public_send("#{association_reflection.foreign_key}=", association.send(association_primary_key))
718
729
  end
719
730
  end
720
731
  end
@@ -782,6 +793,8 @@ class ActiveRecord::Base
782
793
  # be sure to query sequence_name *last*, only if cheaper tests fail, because it's costly
783
794
  if val.nil? && column.name == primary_key && !sequence_name.blank?
784
795
  connection_memo.next_value_for_sequence(sequence_name)
796
+ elsif val.respond_to?(:to_sql)
797
+ "(#{val.to_sql})"
785
798
  elsif column
786
799
  if respond_to?(:type_caster) # Rails 5.0 and higher
787
800
  type = type_for_attribute(column.name)
@@ -846,5 +859,42 @@ class ActiveRecord::Base
846
859
  def validations_array_for_column_names_and_attributes( column_names, array_of_attributes ) # :nodoc:
847
860
  array_of_attributes.map { |values| Hash[column_names.zip(values)] }
848
861
  end
862
+
863
+ # Checks that the imported hash has the required_keys, optionally also checks that the hash has
864
+ # no keys beyond those required when `allow_extra_keys` is false.
865
+ # returns `nil` if validation passes, or an error message if it fails
866
+ def validate_hash_import(hash, required_keys, allow_extra_keys) # :nodoc:
867
+ extra_keys = allow_extra_keys ? [] : hash.keys - required_keys
868
+ missing_keys = required_keys - hash.keys
869
+
870
+ return nil if extra_keys.empty? && missing_keys.empty?
871
+
872
+ if allow_extra_keys
873
+ <<-EOS
874
+ Hash key mis-match.
875
+
876
+ When importing an array of hashes with provided columns_names, each hash must contain keys for all column_names.
877
+
878
+ Required keys: #{column_names}
879
+ Missing keys: #{missing_keys}
880
+
881
+ Hash: #{hash}
882
+ EOS
883
+ else
884
+ <<-EOS
885
+ Hash key mis-match.
886
+
887
+ When importing an array of hashes, all hashes must have the same keys.
888
+ If you have records that are missing some values, we recommend you either set default values
889
+ for the missing keys or group these records into batches by key set before importing.
890
+
891
+ Required keys: #{column_names}
892
+ Extra keys: #{extra_keys}
893
+ Missing keys: #{missing_keys}
894
+
895
+ Hash: #{hash}
896
+ EOS
897
+ end
898
+ end
849
899
  end
850
900
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.20.2".freeze
3
+ VERSION = "0.21.0".freeze
4
4
  end
5
5
  end
@@ -86,6 +86,54 @@ describe "#import" do
86
86
  assert_nil t.author_email_address
87
87
  end
88
88
  end
89
+
90
+ context "with extra keys" do
91
+ let(:values) do
92
+ [
93
+ { title: "LDAP", author_name: "Jerry Carter" },
94
+ { title: "Rails Recipes", author_name: "Chad Fowler", author_email_address: "cfowler@test.com" } # author_email_address is unknown
95
+ ]
96
+ end
97
+
98
+ it "should fail when column names are not specified" do
99
+ err = assert_raises ArgumentError do
100
+ Topic.import values, validate: false
101
+ end
102
+
103
+ assert err.message.include? 'Extra keys: [:author_email_address]'
104
+ end
105
+
106
+ it "should succeed when column names are specified" do
107
+ assert_difference "Topic.count", +2 do
108
+ Topic.import columns, values, validate: false
109
+ end
110
+ end
111
+ end
112
+
113
+ context "with missing keys" do
114
+ let(:values) do
115
+ [
116
+ { title: "LDAP", author_name: "Jerry Carter" },
117
+ { title: "Rails Recipes" } # author_name is missing
118
+ ]
119
+ end
120
+
121
+ it "should fail when column names are not specified" do
122
+ err = assert_raises ArgumentError do
123
+ Topic.import values, validate: false
124
+ end
125
+
126
+ assert err.message.include? 'Missing keys: [:author_name]'
127
+ end
128
+
129
+ it "should fail on missing hash key from specified column names" do
130
+ err = assert_raises ArgumentError do
131
+ Topic.import %i(author_name), values, validate: false
132
+ end
133
+
134
+ assert err.message.include? 'Missing keys: [:author_name]'
135
+ end
136
+ end
89
137
  end
90
138
 
91
139
  unless ENV["SKIP_COMPOSITE_PK"]
@@ -545,6 +593,17 @@ describe "#import" do
545
593
  end
546
594
  end
547
595
 
596
+ context "importing model with polymorphic belongs_to" do
597
+ it "works without error" do
598
+ book = FactoryGirl.create :book
599
+ discount = Discount.new(discountable: book)
600
+
601
+ Discount.import([discount])
602
+
603
+ assert_equal 1, Discount.count
604
+ end
605
+ end
606
+
548
607
  context 'When importing models with Enum fields' do
549
608
  it 'should be able to import enum fields' do
550
609
  Book.delete_all if Book.count > 0
@@ -760,5 +819,19 @@ describe "#import" do
760
819
  end
761
820
  end
762
821
  end
822
+
823
+ context "with objects that respond to .to_sql as values" do
824
+ let(:columns) { %w(title author_name) }
825
+ let(:valid_values) { [["LDAP", Book.select("'Jerry Carter'").limit(1)], ["Rails Recipes", Book.select("'Chad Fowler'").limit(1)]] }
826
+
827
+ it "should import data" do
828
+ assert_difference "Topic.count", +2 do
829
+ Topic.import! columns, valid_values
830
+ topics = Topic.all
831
+ assert_equal "Jerry Carter", topics.first.author_name
832
+ assert_equal "Chad Fowler", topics.last.author_name
833
+ end
834
+ end
835
+ end
763
836
  end
764
837
  end
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: 0.20.2
4
+ version: 0.21.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: 2017-10-02 00:00:00.000000000 Z
11
+ date: 2017-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -176,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  version: '0'
177
177
  requirements: []
178
178
  rubyforge_project:
179
- rubygems_version: 2.6.11
179
+ rubygems_version: 2.6.13
180
180
  signing_key:
181
181
  specification_version: 4
182
182
  summary: Bulk insert extension for ActiveRecord