From f3a705336f1765039021844d66718a38683c8d33 Mon Sep 17 00:00:00 2001 From: Michael Keene Date: Fri, 1 Sep 2023 12:30:35 +0100 Subject: [PATCH 1/5] index by the headers of the csv to respect the order of the input rather than the order of definition --- lib/csvbuilder/importer/concerns/import/attributes.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/csvbuilder/importer/concerns/import/attributes.rb b/lib/csvbuilder/importer/concerns/import/attributes.rb index 4b2d5e1..2f82e2d 100644 --- a/lib/csvbuilder/importer/concerns/import/attributes.rb +++ b/lib/csvbuilder/importer/concerns/import/attributes.rb @@ -30,7 +30,10 @@ def attribute_objects end def read_attribute_for_validation(attr) - source_row[self.class.column_names.index(attr)] + attr_index = source_headers.index(column_header(attr)) + return source_row[attr_index] unless attr_index.nil? + + nil end protected From f73b21d16f68297b27b3d55cae35fb8e1e06e1fa Mon Sep 17 00:00:00 2001 From: Michael Keene Date: Fri, 1 Sep 2023 12:31:12 +0100 Subject: [PATCH 2/5] use read_attribute_for_validation --- lib/csvbuilder/importer/concerns/import/attributes.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/csvbuilder/importer/concerns/import/attributes.rb b/lib/csvbuilder/importer/concerns/import/attributes.rb index 2f82e2d..23fb334 100644 --- a/lib/csvbuilder/importer/concerns/import/attributes.rb +++ b/lib/csvbuilder/importer/concerns/import/attributes.rb @@ -39,10 +39,8 @@ def read_attribute_for_validation(attr) protected def _attribute_objects - index = -1 - - array_to_block_hash(self.class.column_names) do |column_name| - Attribute.new(column_name, source_row[index += 1], errors.to_hash[column_name], self) + self.class.column_names.to_h do |column_name| + [column_name, Attribute.new(column_name, read_attribute_for_validation(column_name), errors.to_hash[column_name], self)] end end From e91d129d2e0fd49e2ee3605af6b28ea2ef0e0ad6 Mon Sep 17 00:00:00 2001 From: Michael Keene Date: Fri, 1 Sep 2023 12:32:19 +0100 Subject: [PATCH 3/5] ensure source headers is an array --- lib/csvbuilder/importer/concerns/import/base.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/csvbuilder/importer/concerns/import/base.rb b/lib/csvbuilder/importer/concerns/import/base.rb index de91118..1afb565 100644 --- a/lib/csvbuilder/importer/concerns/import/base.rb +++ b/lib/csvbuilder/importer/concerns/import/base.rb @@ -25,7 +25,13 @@ def initialize(source_row_or_exception = [], options = {}) @line_number = options[:line_number] @index = options[:index] - @source_headers = options[:source_headers] + # options[:source_headers] can be an Exception or nil + @source_headers = case options[:source_headers] + in Array + options[:source_headers] + else + [] + end @previous = options[:previous].try(:dup) From 17854fa6ae019d23912788332088b315bcfb327b Mon Sep 17 00:00:00 2001 From: Michael Keene Date: Fri, 1 Sep 2023 12:32:51 +0100 Subject: [PATCH 4/5] add in source_headers to test contexts --- .../importer/concerns/import/attributes_spec.rb | 7 ++++++- .../csvbuilder/importer/concerns/import/base_spec.rb | 12 ++++++++---- .../concerns/import/csv_string_model_spec.rb | 3 +-- spec/csvbuilder/importer/public/import_spec.rb | 3 ++- .../support/shared_examples/methods/column_method.rb | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/spec/csvbuilder/importer/concerns/import/attributes_spec.rb b/spec/csvbuilder/importer/concerns/import/attributes_spec.rb index 5f59397..2ef245e 100644 --- a/spec/csvbuilder/importer/concerns/import/attributes_spec.rb +++ b/spec/csvbuilder/importer/concerns/import/attributes_spec.rb @@ -7,7 +7,7 @@ module Import RSpec.describe Attributes do let(:row_model_class) { Class.new BasicImportModel } let(:source_row) { %w[alpha beta] } - let(:options) { { foo: :bar } } + let(:options) { { foo: :bar, source_headers: ["Alpha", "Beta Two"] } } let(:instance) { row_model_class.new(source_row, options) } describe "instance" do @@ -21,6 +21,11 @@ module Import it "returns a hash of cells mapped to their column_name" do expect(attribute_objects.keys).to eql row_model_class.column_names expect(attribute_objects.values.map(&:class)).to eql [Csvbuilder::Import::Attribute] * 2 + + # ensure that attributes are correctly set + {alpha: "alpha", beta: "beta"}.each do |attr, value| + expect(attribute_objects[attr].value).to eql value + end end context "when invalid" do diff --git a/spec/csvbuilder/importer/concerns/import/base_spec.rb b/spec/csvbuilder/importer/concerns/import/base_spec.rb index a658802..3309726 100644 --- a/spec/csvbuilder/importer/concerns/import/base_spec.rb +++ b/spec/csvbuilder/importer/concerns/import/base_spec.rb @@ -7,7 +7,8 @@ module Import RSpec.describe Base do describe "instance" do let(:source_row) { %w[alpha beta] } - let(:options) { {} } + let(:source_headers) { { source_headers: ["Alpha", "Beta Two"] } } + let(:options) { source_headers } let(:row_model_class) { BasicImportModel } let(:instance) { row_model_class.new(source_row, options) } @@ -40,7 +41,7 @@ module Import describe "#free_previous" do subject(:free_previous) { instance.free_previous } - let(:options) { { previous: row_model_class.new([]) } } + let(:options) { { previous: row_model_class.new([], source_headers), **source_headers } } it "makes previous nil" do expect { free_previous }.to change(instance, :previous).to(nil) @@ -56,9 +57,12 @@ def alpha end let(:source_row) { [] } let(:options) do - { previous: row_model_class.new([], + { + previous: row_model_class.new([], previous: row_model_class.new(["alpha from previous > previous", - "beta"])) } + "beta"], source_headers), **source_headers), + **source_headers + } end it "grabs alpha from previous.previous" do diff --git a/spec/csvbuilder/importer/concerns/import/csv_string_model_spec.rb b/spec/csvbuilder/importer/concerns/import/csv_string_model_spec.rb index a133e50..672523c 100644 --- a/spec/csvbuilder/importer/concerns/import/csv_string_model_spec.rb +++ b/spec/csvbuilder/importer/concerns/import/csv_string_model_spec.rb @@ -5,8 +5,7 @@ RSpec.describe "Csvbuilder::Import::ParsedModel" do describe "instance" do let(:source_row) { %w[alpha beta] } - let(:options) { { foo: :bar } } - let(:klass) { BasicImportModel } + let(:options) { { foo: :bar, source_headers: ["Id"] } } let(:instance) { klass.new(source_row, options) } describe "#valid?" do diff --git a/spec/csvbuilder/importer/public/import_spec.rb b/spec/csvbuilder/importer/public/import_spec.rb index 3dd710e..1094509 100644 --- a/spec/csvbuilder/importer/public/import_spec.rb +++ b/spec/csvbuilder/importer/public/import_spec.rb @@ -15,7 +15,8 @@ module Csvbuilder end it do - expect(klass.new(%w[alpha beta]).attributes).to eql(alpha: "alpha", beta: "beta") + instance = klass.new(%w[alpha beta], source_headers: ["Alpha", "Beta"]) + expect(instance.attributes).to eql(alpha: "alpha", beta: "beta") end it "has Csvbuilder::Model included" do diff --git a/spec/support/shared_examples/methods/column_method.rb b/spec/support/shared_examples/methods/column_method.rb index 7c07d9d..2f45879 100644 --- a/spec/support/shared_examples/methods/column_method.rb +++ b/spec/support/shared_examples/methods/column_method.rb @@ -8,7 +8,7 @@ row_model_class.send(:include, Csvbuilder::Model) row_model_class.send(:column, :alpha) row_model_class.send(:include, mod) - row_model_class.send(:column, :beta) + row_model_class.send(:column, :beta, header: "Beta Two") end it do From 2f7f904995c0dbf685245e2d6474ffb422e84b04 Mon Sep 17 00:00:00 2001 From: Michael Keene Date: Fri, 1 Sep 2023 12:43:47 +0100 Subject: [PATCH 5/5] test that csv columns are order independant --- .../importer/concerns/import/attributes_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/csvbuilder/importer/concerns/import/attributes_spec.rb b/spec/csvbuilder/importer/concerns/import/attributes_spec.rb index 2ef245e..cd9f9dc 100644 --- a/spec/csvbuilder/importer/concerns/import/attributes_spec.rb +++ b/spec/csvbuilder/importer/concerns/import/attributes_spec.rb @@ -28,6 +28,21 @@ module Import end end + context "when provided in a different order to column definition" do + let(:source_row) { %w[beta alpha] } + let(:options) { { foo: :bar, source_headers: ["Beta Two", "Alpha"] } } + + it "returns a hash of cells mapped to their column_name" do + expect(attribute_objects.keys).to eql row_model_class.column_names + expect(attribute_objects.values.map(&:class)).to eql [Csvbuilder::Import::Attribute] * 2 + + # ensure that attributes are correctly set + {alpha: "alpha", beta: "beta"}.each do |attr, value| + expect(attribute_objects[attr].value).to eql value + end + end + end + context "when invalid" do let(:row_model_class) do Class.new(BasicImportModel) do