activerecord-import 0.17.0 → 0.17.1

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
  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