Accepting a parameter either as individual object or as array of objects
The simplest solution is to use Kernel method Array:
Array(5) #=> [5]
Array([1, 2, 3]) #=> [1,2,3]
so
def foo(bar_or_bars)
Array(bar_or_bars).each { |bar| ... }
This will even work on nested arrays that have arrays as elements (they wont be flattened out).
One case this won't work for is Hashes:
Array(a: 1, b: 2) #=> [[:a, 1], [:b, 2]]
If you want to iterate through unchanged Hash objects, best use Array.wrap from ActiveSupport.
First thing you could do is to write the unless
logic in a single line:
bars = bar_or_bars.is_a?(Array) ? bar_or_bars : [bar_or_bars]
As you see, I give it a new name here, as it's no longer a bar or bars, it's now definitely a collection.
The problem with this and your original approach is that although your function could work on any Enumerable
, you will force your users to give you an argument of a specific type, which breaks duck typing.
A neat trick to partially solve that issue is the following:
def foo(bar_or_bars)
bars = [*bar_or_bars]
bars.each { |baz| ... }
end
I wouldn't exactly call that readable, though. It actually smells a lot like bad API design. Probably you should better take multiple arguments like this:
def foo(*bars)
bars.each { |baz| ... }
end
And let the caller decide whether he wants to pass a single object or an array:
foo("XYZ")
ary = ["abc", "def"]
foo(*ary)