activerecord-import 0.17.0 → 0.17.1

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: 8a9d274345964888ca0c5b9242a689d3953271c7
4
- data.tar.gz: 59dcdf278fec2582d2e44a7fb416d355026856a5
3
+ metadata.gz: d3e8c2443ad40d5d2a2b22c304ebc88780834b0b
4
+ data.tar.gz: f55f0a5b40c6ba2dee599c13a2c2de83eeeefc6b
5
5
  SHA512:
6
- metadata.gz: 96f341704f5a40797d626039985e52813d01087e9d41427ef375b5c383c1ce7f22cfac0120939e52ea037b86d84c8ae491ea098f82c2bcc6a94fea34a823dd02
7
- data.tar.gz: 4d0f4890f4b97ea9c3d36bf8971d6334ab880d235eda6c14a2af7a8395900562fdb09fd2127c55d3283f48a8734e543671eb2ce0734b1f91689faa95a7bbff56
6
+ metadata.gz: 967eb0e53dd4bb1c8267263788d9dea872f8c4f431dfc5516d819fad1cbc5c6bae20000bf0bb29c6c045012c8f61cdfd93232e5f46a34ef78216247a179ae811
7
+ data.tar.gz: 73f3dd915a1ad43f22d0d0cc03dc7ccc717191180e05cbce8d35ddbe91ea3a747730f655443cfac34a8aae036a2117bf54bd347c6fbb3998193169fc9175dcdf
@@ -18,6 +18,10 @@ matrix:
18
18
  include:
19
19
  - rvm: jruby-9.0.5.0
20
20
  env: AR_VERSION=4.2
21
+ before_install:
22
+ - gem uninstall -i /home/travis/.rvm/gems/jruby-9.0.5.0@global bundler
23
+ - gem install bundler -v 1.13.7 --no-rdoc --no-ri --no-document
24
+
21
25
  script:
22
26
  - bundle exec rake test:jdbcsqlite3
23
27
  - bundle exec rake test:jdbcmysql
@@ -1,3 +1,13 @@
1
+ ## Changes in 0.17.1
2
+
3
+ ### Fixes
4
+
5
+ * Along with setting id on models for adapters that support it,
6
+ add created_at and updated_at timestamps. Thanks to @jacob-carlborg
7
+ via \#364.
8
+ * Properly set returned ids when using composite_primary_keys.
9
+ Thanks to @guigs, @jkowens via \#371.
10
+
1
11
  ## Changes in 0.17.0
2
12
 
3
13
  ### New Features
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gemspec
4
4
 
5
5
  group :development, :test do
6
6
  gem 'rubocop', '~> 0.38.0'
7
+ gem 'rake'
7
8
  end
8
9
 
9
10
  # Database Adapters
@@ -30,10 +31,6 @@ gem "mocha"
30
31
 
31
32
  # Debugging
32
33
  platforms :jruby do
33
- gem "ruby-debug-base", "= 0.10.4"
34
- end
35
-
36
- platforms :jruby, :mri_18 do
37
34
  gem "ruby-debug", "= 0.10.4"
38
35
  end
39
36
 
@@ -41,7 +41,7 @@ Use the latest in the activerecord-import 0.3.x series.
41
41
 
42
42
  Use activerecord-import 0.2.11. As of activerecord-import 0.3.0 we are relying on functionality that was introduced in Rails 3.1. Since Rails 3.0.x is no longer a supported version of Rails we have decided to drop support as well.
43
43
 
44
- ### For More Information
44
+ ### More Information : Usage and Examples in Wiki
45
45
 
46
46
  For more information on activerecord-import please see its wiki: https://github.com/zdennis/activerecord-import/wiki
47
47
 
@@ -4,8 +4,8 @@ require File.expand_path('../lib/activerecord-import/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Zach Dennis"]
6
6
  gem.email = ["zach.dennis@gmail.com"]
7
- gem.summary = "Bulk-loading extension for ActiveRecord"
8
- gem.description = "Extraction of the ActiveRecord::Base#import functionality from ar-extensions for Rails 3 and beyond"
7
+ gem.summary = "Bulk insert extension for ActiveRecord"
8
+ gem.description = "A library for bulk inserting data using ActiveRecord."
9
9
  gem.homepage = "http://github.com/zdennis/activerecord-import"
10
10
  gem.license = "Ruby"
11
11
 
@@ -4,7 +4,7 @@ module ActiveRecord::Import::AbstractAdapter
4
4
  %(#{sequence_name}.nextval)
5
5
  end
6
6
 
7
- def insert_many( sql, values, *args ) # :nodoc:
7
+ def insert_many( sql, values, _options = {}, *args ) # :nodoc:
8
8
  number_of_inserts = 1
9
9
 
10
10
  base_sql, post_sql = if sql.is_a?( String )
@@ -45,7 +45,7 @@ module ActiveRecord::Import::AbstractAdapter
45
45
  post_sql_statements = []
46
46
 
47
47
  if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
48
- post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] )
48
+ post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update], options[:primary_key] )
49
49
  elsif options[:on_duplicate_key_update]
50
50
  logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
51
51
  end
@@ -7,7 +7,7 @@ module ActiveRecord::Import::MysqlAdapter
7
7
 
8
8
  # +sql+ can be a single string or an array. If it is an array all
9
9
  # elements that are in position >= 1 will be appended to the final SQL.
10
- def insert_many( sql, values, *args ) # :nodoc:
10
+ def insert_many( sql, values, _options = {}, *args ) # :nodoc:
11
11
  # the number of inserts default
12
12
  number_of_inserts = 0
13
13
 
@@ -4,7 +4,8 @@ module ActiveRecord::Import::PostgreSQLAdapter
4
4
 
5
5
  MIN_VERSION_FOR_UPSERT = 90_500
6
6
 
7
- def insert_many( sql, values, *args ) # :nodoc:
7
+ def insert_many( sql, values, options = {}, *args ) # :nodoc:
8
+ primary_key = options[:primary_key]
8
9
  number_of_inserts = 1
9
10
  ids = []
10
11
 
@@ -15,11 +16,17 @@ module ActiveRecord::Import::PostgreSQLAdapter
15
16
  end
16
17
 
17
18
  sql2insert = base_sql + values.join( ',' ) + post_sql
18
- if post_sql =~ /RETURNING\s/
19
- ids = select_values( sql2insert, *args )
20
- query_cache.clear if query_cache_enabled
21
- else
19
+
20
+ if primary_key.blank? || options[:no_returning]
22
21
  insert( sql2insert, *args )
22
+ else
23
+ ids = if primary_key.is_a?( Array )
24
+ # Select composite primary keys
25
+ select_rows( sql2insert, *args )
26
+ else
27
+ select_values( sql2insert, *args )
28
+ end
29
+ query_cache.clear if query_cache_enabled
23
30
  end
24
31
 
25
32
  [number_of_inserts, ids]
@@ -45,7 +52,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
45
52
 
46
53
  unless options[:no_returning] || options[:primary_key].blank?
47
54
  primary_key = Array(options[:primary_key])
48
- sql << " RETURNING (#{primary_key.join(', ')})"
55
+ sql << " RETURNING #{primary_key.join(', ')}"
49
56
  end
50
57
 
51
58
  sql
@@ -76,7 +83,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
76
83
  # Returns a generated ON CONFLICT DO UPDATE statement given the passed
77
84
  # in +args+.
78
85
  def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
79
- arg = args.first
86
+ arg, primary_key = args
80
87
  arg = { columns: arg } if arg.is_a?( Array ) || arg.is_a?( String )
81
88
  return unless arg.is_a?( Hash )
82
89
 
@@ -88,7 +95,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
88
95
  return sql << "#{conflict_target}DO NOTHING"
89
96
  end
90
97
 
91
- conflict_target ||= sql_for_default_conflict_target( table_name )
98
+ conflict_target ||= sql_for_default_conflict_target( table_name, primary_key )
92
99
  unless conflict_target
93
100
  raise ArgumentError, 'Expected :conflict_target or :constraint_name to be specified'
94
101
  end
@@ -136,20 +143,8 @@ module ActiveRecord::Import::PostgreSQLAdapter
136
143
  end
137
144
  end
138
145
 
139
- def sql_for_default_conflict_target( table_name )
140
- pks = select_values(<<-SQL.strip_heredoc, "SCHEMA")
141
- WITH pk_constraint AS (
142
- SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
143
- WHERE contype = 'p'
144
- AND conrelid = #{quote(quote_table_name(table_name))}::regclass
145
- ), cons AS (
146
- SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
147
- )
148
- SELECT attr.attname FROM pg_attribute attr
149
- INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
150
- ORDER BY cons.rownum
151
- SQL
152
- conflict_target = pks.join(', ')
146
+ def sql_for_default_conflict_target( table_name, primary_key )
147
+ conflict_target = Array(primary_key).join(', ')
153
148
  "(#{conflict_target}) " if conflict_target.present?
154
149
  end
155
150
 
@@ -17,7 +17,7 @@ module ActiveRecord::Import::SQLite3Adapter
17
17
 
18
18
  # +sql+ can be a single string or an array. If it is an array all
19
19
  # elements that are in position >= 1 will be appended to the final SQL.
20
- def insert_many(sql, values, *args) # :nodoc:
20
+ def insert_many( sql, values, _options = {}, *args ) # :nodoc:
21
21
  number_of_inserts = 0
22
22
 
23
23
  base_sql, post_sql = if sql.is_a?( String )
@@ -442,9 +442,11 @@ class ActiveRecord::Base
442
442
  array_of_attributes.each { |a| a.concat(new_fields) }
443
443
  end
444
444
 
445
+ timestamps = {}
446
+
445
447
  # record timestamps unless disabled in ActiveRecord::Base
446
448
  if record_timestamps && options.delete( :timestamps )
447
- add_special_rails_stamps column_names, array_of_attributes, options
449
+ timestamps = add_special_rails_stamps column_names, array_of_attributes, options
448
450
  end
449
451
 
450
452
  return_obj = if is_validating
@@ -474,7 +476,7 @@ class ActiveRecord::Base
474
476
 
475
477
  # if we have ids, then set the id on the models and mark the models as clean.
476
478
  if models && support_setting_primary_key_of_imported_objects?
477
- set_ids_and_mark_clean(models, return_obj)
479
+ set_attributes_and_mark_clean(models, return_obj, timestamps)
478
480
 
479
481
  # if there are auto-save associations on the models we imported that are new, import them as well
480
482
  import_associations(models, options.dup) if options[:recursive]
@@ -575,6 +577,7 @@ class ActiveRecord::Base
575
577
  # perform the inserts
576
578
  result = connection.insert_many( [insert_sql, post_sql_statements].flatten,
577
579
  batch_values,
580
+ options,
578
581
  "#{self.class.name} Create Many Without Validations Or Callbacks" )
579
582
  number_inserted += result[0]
580
583
  ids += result[1]
@@ -592,7 +595,7 @@ class ActiveRecord::Base
592
595
 
593
596
  private
594
597
 
595
- def set_ids_and_mark_clean(models, import_result)
598
+ def set_attributes_and_mark_clean(models, import_result, timestamps)
596
599
  return if models.nil?
597
600
  models -= import_result.failed_instances
598
601
  import_result.ids.each_with_index do |id, index|
@@ -604,6 +607,10 @@ class ActiveRecord::Base
604
607
  model.instance_variable_get(:@changed_attributes).clear
605
608
  end
606
609
  model.instance_variable_set(:@new_record, false)
610
+
611
+ timestamps.each do |attr, value|
612
+ model.send(attr + "=", value)
613
+ end
607
614
  end
608
615
  end
609
616
 
@@ -689,9 +696,13 @@ class ActiveRecord::Base
689
696
  end
690
697
 
691
698
  def add_special_rails_stamps( column_names, array_of_attributes, options )
699
+ timestamps = {}
700
+
692
701
  AREXT_RAILS_COLUMNS[:create].each_pair do |key, blk|
693
702
  next unless self.column_names.include?(key)
694
703
  value = blk.call
704
+ timestamps[key] = value
705
+
695
706
  index = column_names.index(key) || column_names.index(key.to_sym)
696
707
  if index
697
708
  # replace every instance of the array of attributes with our value
@@ -705,6 +716,8 @@ class ActiveRecord::Base
705
716
  AREXT_RAILS_COLUMNS[:update].each_pair do |key, blk|
706
717
  next unless self.column_names.include?(key)
707
718
  value = blk.call
719
+ timestamps[key] = value
720
+
708
721
  index = column_names.index(key) || column_names.index(key.to_sym)
709
722
  if index
710
723
  # replace every instance of the array of attributes with our value
@@ -718,6 +731,8 @@ class ActiveRecord::Base
718
731
  connection.add_column_for_on_duplicate_key_update(key, options)
719
732
  end
720
733
  end
734
+
735
+ timestamps
721
736
  end
722
737
 
723
738
  # Returns an Array of Hashes for the passed in +column_names+ and +array_of_attributes+.
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.17.0".freeze
3
+ VERSION = "0.17.1".freeze
4
4
  end
5
5
  end
@@ -183,6 +183,24 @@ describe "#import" do
183
183
  end
184
184
  end
185
185
 
186
+ it "should set ActiveRecord timestamps in valid models if adapter supports setting primary key of imported objects" do
187
+ if ActiveRecord::Base.support_setting_primary_key_of_imported_objects?
188
+ Timecop.freeze(Time.at(0)) do
189
+ Topic.import (invalid_models + valid_models), validate: true
190
+ end
191
+
192
+ assert_nil invalid_models[0].created_at
193
+ assert_nil invalid_models[0].updated_at
194
+ assert_nil invalid_models[1].created_at
195
+ assert_nil invalid_models[1].updated_at
196
+
197
+ assert_equal valid_models[0].created_at, Topic.all[0].created_at
198
+ assert_equal valid_models[0].updated_at, Topic.all[0].updated_at
199
+ assert_equal valid_models[1].created_at, Topic.all[1].created_at
200
+ assert_equal valid_models[1].updated_at, Topic.all[1].updated_at
201
+ end
202
+ end
203
+
186
204
  it "should import valid data when mixed with invalid data" do
187
205
  assert_difference "Topic.count", +2 do
188
206
  Topic.import columns, valid_values + invalid_values, validate: true
@@ -463,12 +481,12 @@ describe "#import" do
463
481
 
464
482
  context "importing through an association scope" do
465
483
  { has_many: :chapters, polymorphic: :discounts }.each do |association_type, association|
466
- let(:book) { FactoryGirl.create :book }
467
- let(:scope) { book.public_send association }
468
- let(:klass) { { chapters: Chapter, discounts: Discount }[association] }
469
- let(:column) { { chapters: :title, discounts: :amount }[association] }
470
- let(:val1) { { chapters: 'A', discounts: 5 }[association] }
471
- let(:val2) { { chapters: 'B', discounts: 6 }[association] }
484
+ book = FactoryGirl.create :book
485
+ scope = book.public_send association
486
+ klass = { chapters: Chapter, discounts: Discount }[association]
487
+ column = { chapters: :title, discounts: :amount }[association]
488
+ val1 = { chapters: 'A', discounts: 5 }[association]
489
+ val2 = { chapters: 'B', discounts: 6 }[association]
472
490
 
473
491
  context "for #{association_type}" do
474
492
  it "works importing models" do
@@ -114,7 +114,7 @@ ActiveRecord::Schema.define do
114
114
  add_index :animals, [:name], unique: true, name: 'uk_animals'
115
115
 
116
116
  create_table :widgets, id: false, force: :cascade do |t|
117
- t.integer :w_id
117
+ t.integer :w_id, primary_key: true
118
118
  t.boolean :active, default: false
119
119
  t.text :data
120
120
  t.text :json_data
@@ -1,6 +1,11 @@
1
1
  class ActiveSupport::TestCase
2
2
  include ActiveRecord::TestFixtures
3
- self.use_transactional_fixtures = true
3
+
4
+ if ENV['AR_VERSION'].to_f >= 5.0
5
+ self.use_transactional_tests = true
6
+ else
7
+ self.use_transactional_fixtures = true
8
+ end
4
9
 
5
10
  class << self
6
11
  def requires_active_record_version(version_string, &blk)
@@ -323,7 +323,7 @@ def should_support_postgresql_upsert_functionality
323
323
  context "with no primary key" do
324
324
  it "raises ArgumentError" do
325
325
  error = assert_raises ArgumentError do
326
- Widget.import Build(1, :widgets), on_duplicate_key_update: [:data], validate: false
326
+ Rule.import Build(3, :rules), on_duplicate_key_update: [:condition_text], validate: false
327
327
  end
328
328
  assert_match(/Expected :conflict_target or :constraint_name to be specified/, error.message)
329
329
  end
@@ -102,6 +102,21 @@ def should_support_recursive_import
102
102
  end
103
103
  end
104
104
 
105
+ describe "with composite primary keys" do
106
+ it "should import models and set id" do
107
+ tags = []
108
+ tags << Tag.new(tag_id: 1, publisher_id: 1, tag: 'Mystery')
109
+ tags << Tag.new(tag_id: 2, publisher_id: 1, tag: 'Science')
110
+
111
+ assert_difference "Tag.count", +2 do
112
+ Tag.import tags
113
+ end
114
+
115
+ assert_equal 1, tags[0].tag_id
116
+ assert_equal 2, tags[1].tag_id
117
+ end
118
+ end
119
+
105
120
  # These models dont validate associated. So we expect that books and topics get inserted, but not chapters
106
121
  # Putting a transaction around everything wouldn't work, so if you want your chapters to prevent topics from
107
122
  # being created, you would need to have validates_associated in your models and insert with validation
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.17.0
4
+ version: 0.17.1
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-01-17 00:00:00.000000000 Z
11
+ date: 2017-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,8 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
- description: Extraction of the ActiveRecord::Base#import functionality from ar-extensions
42
- for Rails 3 and beyond
41
+ description: A library for bulk inserting data using ActiveRecord.
43
42
  email:
44
43
  - zach.dennis@gmail.com
45
44
  executables: []
@@ -178,7 +177,7 @@ rubyforge_project:
178
177
  rubygems_version: 2.6.2
179
178
  signing_key:
180
179
  specification_version: 4
181
- summary: Bulk-loading extension for ActiveRecord
180
+ summary: Bulk insert extension for ActiveRecord
182
181
  test_files:
183
182
  - test/adapters/jdbcmysql.rb
184
183
  - test/adapters/jdbcpostgresql.rb