From 8fe2908a6388a7ae911867116aed25c8d8cf7795 Mon Sep 17 00:00:00 2001 From: Alexis Reigel <mail@koffeinfrei.org> Date: Thu, 31 May 2018 16:39:35 +0200 Subject: [PATCH] add model validation error reporting --- lib/excelsior/error.rb | 3 +++ lib/excelsior/import.rb | 14 ++++++++++++-- test/excelsior_test.rb | 7 +++++++ test/files/missing-first-name.xlsx | Bin 0 -> 5489 bytes 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 lib/excelsior/error.rb create mode 100644 test/files/missing-first-name.xlsx diff --git a/lib/excelsior/error.rb b/lib/excelsior/error.rb new file mode 100644 index 0000000..8493619 --- /dev/null +++ b/lib/excelsior/error.rb @@ -0,0 +1,3 @@ +module Excelsior + Error = Struct.new(:row, :errors) +end diff --git a/lib/excelsior/import.rb b/lib/excelsior/import.rb index 455ee54..fd17e42 100644 --- a/lib/excelsior/import.rb +++ b/lib/excelsior/import.rb @@ -3,6 +3,7 @@ require 'rails' require 'active_record' require 'excelsior/source' require 'excelsior/mapping' +require 'excelsior/error' module Excelsior class Import @@ -26,12 +27,13 @@ module Excelsior end def run # takes an optional block - @rows.map do |row| + @rows.map.with_index do |row, i| attributes = map_row_values(row, @columns) if block_given? yield attributes else - model_class.create!(attributes) + record = model_class.create(attributes) + add_model_errors(record, i) end end end @@ -52,5 +54,13 @@ module Excelsior def model_class self.class.name.gsub("Import", "").constantize end + + def add_model_errors(record, index) + return if record.errors.empty? + + @errors[:model] ||= [] + + @errors[:model] << Error.new(index + 1, record.errors.full_messages) + end end end diff --git a/test/excelsior_test.rb b/test/excelsior_test.rb index adafe5f..40b3c3f 100644 --- a/test/excelsior_test.rb +++ b/test/excelsior_test.rb @@ -15,6 +15,7 @@ end class ExcelsiorTest < Minitest::Test def setup + User.delete_all @import = UserImport.new end @@ -74,4 +75,10 @@ class ExcelsiorTest < Minitest::Test import = UserImport.new("test/files/missing-column.xlsx") assert import.errors[:missing_column].any? end + + def test_model_validations + import = UserImport.new("test/files/missing-first-name.xlsx").tap(&:run) + assert import.errors[:model].any? + assert_equal import.errors[:model], [Excelsior::Error.new(3, ["First name can't be blank"])] + end end diff --git a/test/files/missing-first-name.xlsx b/test/files/missing-first-name.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..56f6349dbf85db3ad7986cca64c9c5f9c02d4d22 GIT binary patch literal 5489 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 Y=IsD)RyL3#E(R`!2}}$Or-eW~0GhNb>i_@% literal 0 HcmV?d00001 -- GitLab