activerecord-import 0.18.1 → 0.18.2

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: 49847bc4a5b9c30133bb9a3ea8223906bcb65927
4
- data.tar.gz: a4eeffe1ba579ccfe60f0e4e2e8ba7f641594aff
3
+ metadata.gz: 0ebf1fdae1a4d300498ed6ba18ea8d4039697734
4
+ data.tar.gz: 41e6dfff68a59c0fd9184691cbe6731cc420df8d
5
5
  SHA512:
6
- metadata.gz: 4b862bf40ca9dc76c6980bca024e9001db7e5ec89cdcfb98860cf9710280a44db4587e51ef45d5212c0121bc2bd407e45d6bbf6e047bf2ac5f8256c6b7d29ea6
7
- data.tar.gz: 4409b8fbf9ea44482496a22679029eaf80e5a411cda999624f4587436e68b26eb59be7e263d0f324c39c5156da97be6b49929efde6d6c7bcda7a9c153a17c49a
6
+ metadata.gz: ba3161f35e5521a4febe1a66e729f9a9c549620f54119992d87a834148dc3a42f9e28f3f4c4ca9fb722a0c73639ca678db6d9363325fe6e73a5fb61b58422c59
7
+ data.tar.gz: 4248f96c0adbd65300e1ba161ea91fcf707ac151523121ac9bc541d24cec8f32229bde28ac1783ea6ec69c1f3043f3ac5b5a06c094d2fe9f2c04f952128a0cf6
data/.travis.yml CHANGED
@@ -27,9 +27,6 @@ matrix:
27
27
  - bundle exec rake test:jdbcmysql
28
28
  - bundle exec rake test:jdbcpostgresql
29
29
 
30
- allow_failures:
31
- - env: AR_VERSION=5.1
32
-
33
30
  fast_finish: true
34
31
 
35
32
  before_script:
@@ -47,6 +44,7 @@ addons:
47
44
  - travis-ci/sqlite3
48
45
  packages:
49
46
  - sqlite3
47
+ - postgresql-9.5-postgis-2.3
50
48
 
51
49
  script:
52
50
  - bundle exec rake test:mysql2
@@ -60,4 +58,4 @@ script:
60
58
  - bundle exec rake test:sqlite3
61
59
  - bundle exec rubocop
62
60
 
63
- sudo: required
61
+ dist: trusty
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## Changes in 0.18.2
2
+
3
+ ### Fixes
4
+
5
+ * Enable custom validate callbacks when validating import. Thanks to @afn via \#410.
6
+ * Prevent wrong IDs being set on models when using :on_duplicate_key_ignore.
7
+ Thanks to @afn, @jkowens via \#412.
8
+
1
9
  ## Changes in 0.18.1
2
10
 
3
11
  ### Fixes
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ platforms :ruby do
12
12
  gem "mysql2", "~> 0.3.0"
13
13
  gem "pg", "~> 0.9"
14
14
  gem "sqlite3", "~> 1.3.10"
15
- gem "seamless_database_pool", "~> 1.0.18"
15
+ gem "seamless_database_pool", "~> 1.0.20"
16
16
  end
17
17
 
18
18
  platforms :jruby do
@@ -50,7 +50,7 @@ module ActiveRecord::Import::PostgreSQLAdapter
50
50
 
51
51
  sql += super(table_name, options)
52
52
 
53
- unless options[:no_returning] || options[:primary_key].blank?
53
+ unless options[:primary_key].blank? || options[:no_returning]
54
54
  primary_key = Array(options[:primary_key])
55
55
  sql << " RETURNING \"#{primary_key.join('", "')}\""
56
56
  end
@@ -24,9 +24,8 @@ module ActiveRecord::Import #:nodoc:
24
24
  end
25
25
 
26
26
  class Validator
27
- def initialize(validators, options = {})
28
- @validators = validators
29
- @options = options
27
+ def initialize(options = {})
28
+ @options = options
30
29
  end
31
30
 
32
31
  def valid_model?(model)
@@ -34,38 +33,47 @@ module ActiveRecord::Import #:nodoc:
34
33
  validation_context ||= (model.new_record? ? :create : :update)
35
34
 
36
35
  current_context = model.send(:validation_context)
37
- model.send(:validation_context=, validation_context)
38
- model.errors.clear
36
+ begin
37
+ model.send(:validation_context=, validation_context)
38
+ model.errors.clear
39
39
 
40
- validation_proc = lambda do
41
- @validators.each do |v|
42
- if validation_context == v.options.fetch(:on, validation_context)
43
- v.validate(model) if validate?(v, model)
44
- end
40
+ validate_callbacks = model._validate_callbacks.dup
41
+ validate_callbacks.each do |callback|
42
+ validate_callbacks.delete(callback) if callback.raw_filter.is_a? ActiveRecord::Validations::UniquenessValidator
45
43
  end
46
- end
47
-
48
- if model.respond_to?(:run_validation_callbacks)
49
- model.send(:_run_validation_callbacks, &validation_proc)
50
- else
51
- model.send(:run_callbacks, :validation, &validation_proc)
52
- end
53
-
54
- model.send(:validation_context=, current_context)
55
- model.errors.empty?
56
- end
57
44
 
58
- def validate?(validator, model)
59
- evaluate = lambda do |condition|
60
- case condition
61
- when String then model.instance_eval(condition)
62
- when Symbol then model.send(condition)
63
- when Proc then model.instance_eval(&condition)
45
+ model.run_callbacks(:validation) do
46
+ if defined?(ActiveSupport::Callbacks::Filters::Environment) # ActiveRecord >= 4.1
47
+ runner = validate_callbacks.compile
48
+ env = ActiveSupport::Callbacks::Filters::Environment.new(model, false, nil)
49
+ if runner.respond_to?(:call) # ActiveRecord < 5.1
50
+ runner.call(env)
51
+ else # ActiveRecord 5.1
52
+ # Note that this is a gross simplification of ActiveSupport::Callbacks#run_callbacks.
53
+ # It's technically possible for there to exist an "around" callback in the
54
+ # :validate chain, but this would be an aberration, since Rails doesn't define
55
+ # "around_validate". Still, rather than silently ignoring such callbacks, we
56
+ # explicitly raise a RuntimeError, since activerecord-import was asked to perform
57
+ # validations and it's unable to do so.
58
+ #
59
+ # The alternative here would be to copy-and-paste the bulk of the
60
+ # ActiveSupport::Callbacks#run_callbacks method, which is undesirable if there's
61
+ # no real-world use case for it.
62
+ raise "The :validate callback chain contains an 'around' callback, which is unsupported" unless runner.final?
63
+ runner.invoke_before(env)
64
+ runner.invoke_after(env)
65
+ end
66
+ elsif validate_callbacks.method(:compile).arity == 0 # ActiveRecord = 4.0
67
+ model.instance_eval validate_callbacks.compile
68
+ else # ActiveRecord 3.x
69
+ model.instance_eval validate_callbacks.compile(nil, model)
70
+ end
64
71
  end
65
- end
66
72
 
67
- Array(validator.options[:if]).map(&evaluate).compact.all? &&
68
- !Array(validator.options[:unless]).map(&evaluate).compact.any?
73
+ model.errors.empty?
74
+ ensure
75
+ model.send(:validation_context=, current_context)
76
+ end
69
77
  end
70
78
  end
71
79
  end
@@ -212,7 +220,9 @@ class ActiveRecord::Base
212
220
  # records that contain duplicate keys. For Postgres 9.5+ it adds
213
221
  # ON CONFLICT DO NOTHING, for MySQL it uses INSERT IGNORE, and for
214
222
  # SQLite it uses INSERT OR IGNORE. Cannot be enabled on a
215
- # recursive import.
223
+ # recursive import. For database adapters that normally support
224
+ # setting primary keys on imported objects, this option prevents
225
+ # that from occurring.
216
226
  # * +on_duplicate_key_update+ - an Array or Hash, tells import to
217
227
  # use MySQL's ON DUPLICATE KEY UPDATE or Postgres 9.5+ ON CONFLICT
218
228
  # DO UPDATE ability. See On Duplicate Key Update below.
@@ -511,7 +521,9 @@ class ActiveRecord::Base
511
521
 
512
522
  # if we have ids, then set the id on the models and mark the models as clean.
513
523
  if models && support_setting_primary_key_of_imported_objects?
514
- set_attributes_and_mark_clean(models, return_obj, timestamps)
524
+ if options[:recursive] || !(options[:ignore] || options[:on_duplicate_key_ignore])
525
+ set_attributes_and_mark_clean(models, return_obj, timestamps)
526
+ end
515
527
 
516
528
  # if there are auto-save associations on the models we imported that are new, import them as well
517
529
  import_associations(models, options.dup) if options[:recursive]
@@ -530,8 +542,7 @@ class ActiveRecord::Base
530
542
  def import_with_validations( column_names, array_of_attributes, options = {} )
531
543
  failed_instances = []
532
544
 
533
- validators = self.validators.reject { |v| v.is_a? ActiveRecord::Validations::UniquenessValidator }
534
- validator = ActiveRecord::Import::Validator.new(validators, options)
545
+ validator = ActiveRecord::Import::Validator.new(options)
535
546
 
536
547
  if block_given?
537
548
  yield validator, failed_instances
@@ -700,8 +711,8 @@ class ActiveRecord::Base
700
711
  # Returns SQL the VALUES for an INSERT statement given the passed in +columns+
701
712
  # and +array_of_attributes+.
702
713
  def values_sql_for_columns_and_attributes(columns, array_of_attributes) # :nodoc:
703
- # connection and type_caster get called a *lot* in this high intensity loop.
704
- # Reuse the same ones w/in the loop, otherwise they would keep being re-retreived (= lots of time for large imports)
714
+ # connection gets called a *lot* in this high intensity loop.
715
+ # Reuse the same one w/in the loop, otherwise it would keep being re-retreived (= lots of time for large imports)
705
716
  connection_memo = connection
706
717
 
707
718
  array_of_attributes.map do |arr|
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Import
3
- VERSION = "0.18.1".freeze
3
+ VERSION = "0.18.2".freeze
4
4
  end
5
5
  end
data/test/import_test.rb CHANGED
@@ -168,6 +168,15 @@ describe "#import" do
168
168
  end
169
169
  end
170
170
 
171
+ it "should not alter the callback chain of the model" do
172
+ attributes = columns.zip(valid_values.first).to_h
173
+ topic = Topic.new attributes
174
+ Topic.import [topic], validate: true
175
+ duplicate_topic = Topic.new attributes
176
+ Topic.import [duplicate_topic], validate: true
177
+ assert duplicate_topic.invalid?
178
+ end
179
+
171
180
  it "should not import invalid data" do
172
181
  assert_no_difference "Topic.count" do
173
182
  Topic.import columns, invalid_values, validate: true
@@ -230,6 +239,12 @@ describe "#import" do
230
239
  Topic.import columns, [["invalid", "Jerry Carter"]], validate: true
231
240
  end
232
241
  end
242
+
243
+ it "should call validation methods" do
244
+ assert_no_difference "Topic.count" do
245
+ Topic.import columns, [["validate_failed", "Jerry Carter"]], validate: true
246
+ end
247
+ end
233
248
  end
234
249
  end
235
250
 
data/test/models/topic.rb CHANGED
@@ -3,6 +3,7 @@ class Topic < ActiveRecord::Base
3
3
  validates :title, numericality: { only_integer: true }, on: :context_test
4
4
  validates :title, uniqueness: true
5
5
 
6
+ validate -> { errors.add(:title, :validate_failed) if title == 'validate_failed' }
6
7
  before_validation -> { errors.add(:title, :invalid) if title == 'invalid' }
7
8
 
8
9
  has_many :books, inverse_of: :topic
@@ -8,7 +8,9 @@ def should_support_on_duplicate_key_ignore
8
8
  it "should skip duplicates and continue import" do
9
9
  topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
10
10
  assert_difference "Topic.count", +1 do
11
- Topic.import topics, on_duplicate_key_ignore: true, validate: false
11
+ result = Topic.import topics, on_duplicate_key_ignore: true, validate: false
12
+ assert_not_equal topics.first.id, result.ids.first
13
+ assert_nil topics.last.id
12
14
  end
13
15
  end
14
16
 
@@ -31,7 +33,9 @@ def should_support_on_duplicate_key_ignore
31
33
  it "should skip duplicates and continue import" do
32
34
  topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
33
35
  assert_difference "Topic.count", +1 do
34
- Topic.import topics, ignore: true, validate: false
36
+ result = Topic.import topics, ignore: true, validate: false
37
+ assert_not_equal topics.first.id, result.ids.first
38
+ assert_nil topics.last.id
35
39
  end
36
40
  end
37
41
  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.18.1
4
+ version: 0.18.2
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-04-18 00:00:00.000000000 Z
11
+ date: 2017-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord