Reduce columns to produce product keycodes I have written the following code that achieves my desired goal of converting a "raw" CSV file into a keycoded list. The idea is that a person will get the first product they ask for, and English products are coded as blank because they are the default.

This gist contains a sample of the input and output data. As it stands, rubocop thinks my code is too complex, and I am inclined to agree with it here, as it feels like I am doing far more than I need to in order to get such a simple result. 

Not sure if this is helpful but the rubocop stats I'm getting are as follows:


ABC size too high (19.44/15)
Cyclomatic complexity too high (9/6)
Method has too many lines (17/10) (is this one really even a problem?)
Perceived complexity too high (10/7)


Are any of these "true" problems? They seem to reflect my opinion that the code just turned out "ugly"

On paper it I narrowed it down to about 2 cases with some sub cases. (1. Normal header -> write 2.Header has "#" in it -> subcases) and in the second case it's either blank, "1", or a product name. If the line contains "English Map" I write nothing, otherwise I am to write the first product requested sequentially on the line.

require 'CSV'
in_file = 'test.csv'

def code_line_by_product(line)
  new_line = []
  tmp = nil
  e_line = false
  line.each do |item|
    if item[0].include?('#')
      if item[1] != ' ' &amp;&amp; item[1] &amp;&amp; item[1] != '1' &amp;&amp; item[1] != ''
        e_line = true if item[1].include?('English')
        unless tmp || e_line
          tmp = item[1][0]
          new_line &lt;&lt; tmp
        end
      end
    else
      new_line &lt;&lt; item[1]
    end
  end
  new_line
end

CSV.open("#{File.basename(in_file, '.csv')}_coded.csv", 'a+') do |out|
    out &lt;&lt; %w(First Last Company Address address2 City State Zip Country phone_number email_address keycode)
  CSV.foreach(in_file, headers: true) do |line|
    out &lt;&lt; code_line_by_product(line)
  end
end


Also, I've been noticing that I use that construction of writing from one file to another a lot and was wondering if there was a more convenient way of doing it (such as writing a function that takes the in file, out file, and the desired transformation). While it's only 4 lines of code, I essentially use those nested block all the time and it would be nice to only have to write the transformation function and then just call a single function to execute it.
I would do something like this. It is untested and may have typos but it should give you some idea of how to break the logic up.  I would:


Express variable to what they mean
Use a class


Hope this helps

require 'CSV'

class CSVTranslator
  def self.write file_path, options={}
    new(file_path, options).translate
  end

  def initialize source_file, options={}
    @source_file = source_file
    @destination_file = options.fetch(:to) { "#{File.basename(source_file, '.csv')}_coded.csv" }
  end

  def translate
    CSV.open(destination_file, 'a+') do |out|
      out &lt;&lt; headers
      each_row { |row| out &lt;&lt; row }
    end
  end

  private

  attr_reader :source_file, :destination_file

  def headers
    %w(First Last Company Address address2 City State Zip Country phone_number email_address keycode)
  end

  def each_row 
    CSV.foreach(source_file, headers: true) do |line|
      yield code_line_by_product(line)
    end
  end

  def code_line_by_product(line)
    new_line = {}
    english_language = false
    keycode = ""

    line.each do |header, value|
      if header.include?('#')
        english_language ||= value.include?('English') 
        keycode = value.first if value.first != '1'
      else
        new_line[header] = value
      end
    end

    new_line['keycode'] = keycode unless english_language || keycode.blank?

    new_line
  end
end

CSVTranslator.write 'test.csv', to: 'test_coded.csv'