Smart Array Arguments
I’m creating an application that ranks poker hands. To do this I’ve created a domain object called the PokerHandValue
that consists of the following attributes: hand_rank
, used_cards
, kickers
. The used_cards and kickers variables are arrays consisting of Card
objects.
Sometimes a made Hand consists of just a high card and sometimes all five cards are used. Sometimes the kickers array can have zero cards. The point is, the PokerHandValue
needs to account for the possibility of being passed a single object, an array, or even nil
.
Consider for a moment:
class PokerHandValue
def initialize(hand_rank, used_cards, kickers)
@hand_rank = hand_rank
@used_cards = used_cards
@kickers = kickers
end
def hand
@used_cards + @kickers
end
end
The way I’ve structured PokerHandValue, it is expecting both used_cards and kickers to be arrays. If we somehow pass PokerHandValue nil, we’ll get a TypeError when trying to call the hand method.
To avoid this, we can utilize a little known method: Array.
class PokerHandValue
def initialize(hand_rank, used_cards, kickers)
@hand_rank = hand_rank
@used_cards = Array(used_cards)
@kickers = Array(kickers)
end
def hand
@used_cards + @kickers
end
end
Simple and elegant! Now our used_cards and kickers will always return an array no matter what sort of object sneaks into our arguments.
So let’s break down exactly what the Array method does for us:
irb> Array(nil)
---> []
irb> Array([])
---> []
irb> Array(%w(Ace Ten King))
---> ["Ace", "Ten", "King"]
With such a small change in our code, we increase it’s flexibility, and reduce the potential of bugs. That’s a great win with few, if any, side effects.