/ ruby

Preserving Named Arguments On Inheritance

I don't use inheritance very often, but sometimes it is the best solution. Ruby doesn't care very much carrying when you overwrite a method. The method is just replaced by the new method. It doesn't care about arguments at all.

Since 2.0, Ruby support named arguments. I'm starting to use them more and more. But they are a bit tricky when overwriting.

Let's say we have a class which creates user objects named UserBuilder:

class UserBuilder
  def create(name: )
    puts "name: #{name}"
  end
end

When I have to create a CustomerBuilder, which creates customers (user with address), I can write it like:

class CustomerBuilder < UserBuilder
  def create(address:, name:)
    super name: name
    puts "address: #{address}"
  end
end

This works but creates a coupling between UserBuilder and CustomerBuilder. This coupling is a bit tricky to catch. Imagine the following scenarios:

  • name become optional in UserBuilder
  • name is renamed to full_name in UserBuilder
  • name is split into first_name and last_name in UserBuilder

In all of those cases change in UserBuilder triggers changes in CustomerBuilder.

Such dependencies are toxic. Here is a better solution:

class CustomerBuilder < UserBuilder
  def create(address:, **args)
    super **args
    puts "address: #{address}"
  end
end

Now we can add, change, remove named arguments to UserBuilder#create and CustomerBuilder#create is not affected. Named arguments are just delegated down the change.