Handling inputs with different arity in Ruby

In a recent Ruby project at work, I undertook a feature request which resulted in a fundamental change in one of the utility classes in the project.

The class in question takes as input, an array of file objects for processing. Due to the requirement change, this class would now have to deal with both single and multiple file objects.

My initial first attempt was clumsy to say the least:

 1 class MyFiles
 2   def initialize(input:)
 3     @data = input
 4   end
 6   def process
 7     if @data.respond_to?(:each)
 8       @data.each{|f| process_file(f)}
 9     else
10       process_file(@data)
11     end
12   end
13 end

My refactoring has resulted in MyFiles knowing too much about the the type of the input passed in by checking if the input responds to :each.

Also, this is not very DRY as subsequent code that has to process @data will duplicate the process_file method call and calling it either in an each block if it is an array or on its own for single inputs.

After further thinking, I realised that the only variant is the @data input which could either be a collection of files or a single file input. If we transform @data into an Array, then we will have a collection to work with and not have to check for the type of input. my second attempt at refactoring goes like this:

 1 class MyFiles
 2   def initialize(input:)
 3     @data = Array(input)
 4   end
 6   def process
 7     @data.each do |f|
 8       process_file(f)
 9     end
10   end
11 end

By wrapping input with Kernel#Array, we ensure that @data is always an array and I don’t have to worry about the input type anymore.

Specifically, Array does the following:

1 Array(nil) => []
3 Array("one") => ["one"]
5 Array([1,2,3]) => [1,2,3]

By transforming a variable input type into an array we make the class more flexible in terms of the inputs it can accept and a flexible interface to work with.

Happy Hacking!!!