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!