Skip to content

Commit bc5ad3d

Browse files
authored
Merge pull request #223 from activeadmin-plugins/fix/batch-slice-columns-successive-calls
fix: compose successive batch_slice_columns calls (#186)
2 parents 72ed02b + 2fc2c03 commit bc5ad3d

3 files changed

Lines changed: 74 additions & 22 deletions

File tree

lib/active_admin_import/importer.rb

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,15 @@ def batch_replace(header_key, options)
8282
# end
8383
#
8484
def batch_slice_columns(slice_columns)
85-
# Only set @use_indexes for the first batch so that @use_indexes are in correct
86-
# position for subsequent batches
87-
unless defined?(@use_indexes)
88-
@use_indexes = []
89-
headers.values.each_with_index do |val, index|
90-
@use_indexes << index if val.in?(slice_columns)
91-
end
92-
return csv_lines if @use_indexes.empty?
93-
94-
# slice CSV headers
95-
@headers = headers.to_a.values_at(*@use_indexes).to_h
96-
end
85+
columns = headers.values
86+
indexes = columns.each_index.select { |i| columns[i].in?(slice_columns) }
87+
return csv_lines if indexes.empty?
9788

98-
# slice CSV values
99-
csv_lines.map! do |line|
100-
line.values_at(*@use_indexes)
101-
end
89+
# @headers is reset to the full set at the start of every batch (see
90+
# #batch_import), so each call narrows the previous call's result and every
91+
# batch slices the same way — calling this more than once now composes (#186).
92+
@headers = headers.to_a.values_at(*indexes).to_h
93+
csv_lines.map! { |line| line.values_at(*indexes) }
10294
end
10395

10496
def values_at(header_key)
@@ -129,17 +121,18 @@ def process_file
129121
end
130122

131123
def prepare_headers
132-
headers = self.headers.present? ? self.headers : yield
133-
blank_positions = headers.each_index.select { |i| headers[i].to_s.strip.empty? }
124+
names = self.headers.present? ? self.headers : yield
125+
blank_positions = names.each_index.select { |i| names[i].to_s.strip.empty? }
134126
unless blank_positions.empty?
135127
raise ActiveAdminImport::Exception,
136128
"blank column header at column #{blank_positions.map { |i| i + 1 }.join(', ')}"
137129
end
138130

139-
headers = headers.map(&:to_s)
140-
@headers = Hash[headers.zip(headers.map { |el| el.underscore.gsub(/\s+/, '_') })].with_indifferent_access
141-
@headers.merge!(options[:headers_rewrites].symbolize_keys.slice(*@headers.symbolize_keys.keys))
142-
@headers
131+
names = names.map(&:to_s)
132+
# @source_headers is the complete parsed header row; batch_import copies it
133+
# into the per-batch working @headers (see #batch_import).
134+
@source_headers = Hash[names.zip(names.map { |el| el.underscore.gsub(/\s+/, '_') })].with_indifferent_access
135+
@source_headers.merge!(options[:headers_rewrites].symbolize_keys.slice(*@source_headers.symbolize_keys.keys))
143136
end
144137

145138
def run_callback(name)
@@ -148,6 +141,9 @@ def run_callback(name)
148141

149142
def batch_import
150143
batch_result = nil
144+
# Every batch re-parses full-width rows, so restore the full header set
145+
# before slicing; batch_slice_columns then narrows this working copy.
146+
@headers = @source_headers.dup
151147
@resource.transaction do
152148
run_callback(:before_batch_import)
153149
batch_result = resource.import(headers.values, csv_lines, import_options)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Birthday,Name,Last name
2+
1986-05-01,John,Doe
3+
1988-11-16,Jane,Roe
4+
1990-01-01,Jack,Smith
5+
1991-02-02,Jill,Jones
6+
1992-03-03,Joe,Brown

spec/import_spec.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,56 @@ def upload_file!(name, ext = 'csv')
656656
end
657657
end
658658

659+
# Issue #186: each call must slice the result of the previous one, not re-apply
660+
# the first call's indices to an already-sliced row.
661+
context "with successive batch_slice_columns calls" do
662+
before do
663+
# validate: false isolates the slicing behaviour from the model's
664+
# uniqueness validation, which would otherwise reject a later batch's
665+
# author for sharing a NULL last_name with an earlier one.
666+
add_author_resource template_object: ActiveAdminImport::Model.new,
667+
validate: false,
668+
before_batch_import: lambda { |importer|
669+
importer.batch_slice_columns(%w(name last_name))
670+
importer.batch_slice_columns(%w(name))
671+
},
672+
batch_size: batch_size
673+
visit "/admin/authors/import"
674+
upload_file!(fixture)
675+
end
676+
677+
context "within a single batch" do
678+
let(:batch_size) { 2 } # authors.csv has 2 rows -> 1 batch
679+
let(:fixture) { :authors }
680+
681+
it "narrows the columns progressively" do
682+
expect(Author.pluck(:name, :last_name, :birthday)).to match_array(
683+
[
684+
["Jane", nil, nil],
685+
["John", nil, nil]
686+
]
687+
)
688+
end
689+
end
690+
691+
context "across several batches" do
692+
let(:batch_size) { 2 } # authors_many.csv has 5 rows -> 3 batches
693+
let(:fixture) { :authors_many }
694+
695+
it "narrows the columns progressively in every batch" do
696+
expect(Author.pluck(:name, :last_name, :birthday)).to match_array(
697+
[
698+
["John", nil, nil],
699+
["Jane", nil, nil],
700+
["Jack", nil, nil],
701+
["Jill", nil, nil],
702+
["Joe", nil, nil]
703+
]
704+
)
705+
end
706+
end
707+
end
708+
659709
context 'with invalid options' do
660710
let(:options) { { invalid_option: :invalid_value } }
661711

0 commit comments

Comments
 (0)