module Eco class CSV class Split include Eco::Language::AuxiliarLogger attr_reader :filename def initialize(filename, max_rows:, start_at: nil, **kargs) msg = "File '#{filename}' does not exist" raise ArgumentError, msg unless ::File.exist?(filename) @filename = filename @max_rows = max_rows @start_at = start_at @params = kargs init end # @yield [idx, file] a block to spot the filename # @yieldparam idx [Integer] the number of the file # @yieldparam file [String] the default name of the file # @yieldreturn [String] the filename of the file `idx`. # - If `nil` it will create its own filename convention # @return [Array] names of the generated files def call(&block) stream.for_each(start_at_idx: start_at) do |row, ridx| copy_row(row, ridx, &block) end out_files ensure puts "Close at row #{row_idx}" @csv&.close end private attr_reader :params attr_reader :idx, :max_rows, :start_at attr_reader :headers, :row_idx attr_accessor :exception def copy_row(row, ridx, &block) @headers ||= row.headers @row_idx = ridx current_csv(ridx) do |csv, fidx, file_out| included = true included &&= !block || yield(row, ridx, fidx, file_out) next unless included csv << row.fields end end def current_csv(ridx) if split?(ridx) || @csv.nil? puts "Split at row #{row_idx}" @csv&.close out_filename = generate_name(next_idx) @csv = ::CSV.open(out_filename, "w") @csv << headers out_files << out_filename end yield(@csv, idx, out_files.last) if block_given? @csv end def split?(ridx) ((ridx + 1) % max_rows).zero? end def next_idx idx.tap { @idx += 1 } end def init @idx ||= 0 # rubocop:disable Naming/MemoizedInstanceVariableName end def stream @stream ||= Eco::CSV::Stream.new(filename, **params) end def generate_name(fidx) File.join(input_dir, "#{input_name}_#{file_number(fidx)}#{input_ext}") end def file_number(num) "#{zeroed}#{num}"[-5..] end def zeroed "0" * 5 end def out_files @out_files ||= [] end def input_name @input_name ||= File.basename(input_basename, input_ext) end def input_ext @input_ext ||= input_basename.split('.')[1..].join('.').then do |name| ".#{name}" end end def input_basename @input_basename ||= File.basename(input_full_filename) end def input_dir @input_dir = File.dirname(input_full_filename) end def input_full_filename @input_full_filename ||= File.expand_path(filename) end end end end