-1

I have file reading program. This program reads .txt files and run some function to it. I would like to change the function by files name. My .txt files have naming rules such as A001.txt,B001,C001..and so on. I'm using the if-else statements to change function now.

def Function1(input)
  if filename =~ /A.+\.txt/
     some process....
  elsif filename =~ /B.+\.txt/
     some process....
  .....
end

def Funcition2(input)
  if filename =~ /A.+\.txt/
     some process....
  elsif filename =~ /B.+\.txt/
     some process....
      .....
end

I have 4,5 more similar functions. I think this code isn't efficient and readable. What is the best way to change function when reading files?

user19701
  • 9
  • 2
  • One warning. Your first regex will match the filename `BA001.txt` which is probably not what you want. It would also match `A001.txt2`. You want to anchor the start and end of your regex like this `/^A.+\.txt$/`. It might also make sense to make it case insensitive. – Marc Rohloff Feb 28 '17 at 17:05

2 Answers2

0

You could use the switch statment - case - for that. Here's a previous answer: How to write a Ruby switch statement (case...when) with regex and backreferences?

Basically something like this:

filenames.each do |filename|
  case filename
  when /A.+\.txt/
     function 1
  when /B.+\.txt/
     function 2
  else
     function 3
  end
end
Community
  • 1
  • 1
Esa Mäkinen
  • 160
  • 8
0

I like to use dynamic method calls for this kind of thing:

def Function1(input)
  prefix  = filename[0].downcase
  handler = "import_#{preview}_file"
  handler = 'import_unknown_file' if !respond_to?(handler)
  send(handler, input)
end

def import_a_file(input); end
def import_b_file(input); end
def import_unknown_file(input); end

You can take this a step further to make it more readable:

HANDLERS_FOR_PREFIXES = {
  'a'       => :import_account_file,
  'b'       => :import_balance_file,
  'default' => :import_other_file
} 

def Function1(input)
  prefix = filename[0].downcase
  handler = HANDLERS_FOR_PREFIXES[prefix] || HANDLERS_FOR_PREFIXES['default']
  send(handler, input)
end

def import_account_file(input); end

However, I would suspect that the order you handle files is probably important (If you are importing bank information you need to import accounts before importing transactions into them). So it might make sense to do something like:

FILE_HANDLERS = {
  'A*.txt'  => :import_account_file,
  'T*.txt'  => :import_transaction_file
} 

def Function1(input)
  FILE_HANDLERS.each do |file_pattern, handler|
    Dir.glob(file_pattern).each do |filename|
      File.open(filename) do |f|
        f.each_line do |line| 
          parsed_line = parse(parse) 
          send(handler, parsed_line);
        end
      end
    end
  end
end 

Though I would refactor that to remove the deep nesting.

Lastly, As your code gets more complex you might want to break it up into separate classes to handle each type of file. This gives you a lot opportunities to reuse and break-up your code. For ex:

class AccountImporter < Importer
  def initialize(input); end
  def process
end

HANDLERS_FOR_PREFIXES = {
  'a'       => AccountImporter,
  ...
} 

def Function1(input)
  prefix = filename[0].downcase
  handler = HANDLERS_FOR_PREFIXES[prefix] || HANDLERS_FOR_PREFIXES['default']
  handler.new(input).process
end
Marc Rohloff
  • 1,332
  • 7
  • 8