activerecord-import 0.18.1 → 0.18.2

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