Today I wanted to have a closer look at the different ways for passing boolean arguments and assigning default values to them.
Problem
Let’s imagine that you working with a calculator class that sums processed payment amounts. That’s the only thing it does, by default.
But what if you want to include processing payments as well - and not always by default?
Solution
You can pass an argument to perform processing in a couple of different ways — for example, as a function parameter, or a hash argument.
If you’re dealing with boolean arguments, you can use one of these strategies for assigning a default value:
a) Keyword arguments - they’re available in Ruby 2.0+ version. Here’s an example of a keyword argument:
class Calculator
def initialize(user:, include_processing_payments: true)
@user = user
@include_processing_payments = include_processing_payments
end
end
b) A hash - you typically use a hash for passing multiple arguments or additional values. Here’s how it looks like in our example:
class Calculator
def initialize(options)
@user = options[:user]
# the second argument of fetch is going to be the default value
@include_processing_payments = options.fetch(:include_processing_payments, true)
end
end
Now, both solutions might look pretty neat at first glance. But can you be 100% sure that they will handle every possible default value?
The strategies will work for cases where you pass true or false, there’s no doubt about it.
But what about nil?
The first approach I showed you assigns the default value to the parameter only when it’s not passed. So even if you pass `nil`, you’re still not going to get a default value assigned.
The second approach looks promising, but it’s not going to work either when you try to pass `nil` in your hash explicitly: `{ include_processing_payments: nil }`.
Is there a way to fix that problem and make sure that you get a boolean value assigned correctly?
Consider these two strategies:
a) Double negation
class Calculator
def initialize(user:, include_processing_payments: true)
@user = user
@include_processing_payments = !!include_processing_payments
end
end
In this solution, you’ll be negating the parameter value twice. When you pass a value, you can be sure that it’s turned into a `true` or `false`.
For example, if you pass a `nil` value, you’re going to get a false. And that’s what you’ll be looking for most of the time.
b) Compact method
class Calculator
def initialize(options)
@user = options[:user]
@include_processing_payments = options.compact.fetch(:include_processing_payments, true)
end
end
For hash parameters, you can use try use `compact` method. It basically removes all the key-value pairs where the value is `nil`. This way, the fetch method won't be able to find the required key and so will assign true as the default value.
However, this approach isn’t going to work for arguments that aren’t supplied with `nil` or `boolean` values. To support this kind of case, you’re still going to need double negation: `!!options.fetch(:include_processing_payments, true)`.
Note: You might spot that type of code snippets in your code:
class Calculator
def initialize(user:, include_processing_payments: true)
@user = user
@include_processing_payments = include_processing_payments || true
end
end
It might look correct at first, but you should avoid that kind of implementation. Sure, it solves your problem with the `nil` value, but it also generates a new issue. The `include_processing_payments` instance variable will always be evaluated to true, even if you pass `false` instead of `nil` as argument.
Key takeaway
Boolean arguments can be tricky.
That’s why you should always take extra care when passing them in your code. Just make sure to choose the right implementation method, so you won’t encounter any problems when assigning default values from boolean arguments.
Happy hacking!