In Ruby is there a way to overload the initialize constructor?

Viewed 48186

In Java you can overload constructors:

public Person(String name) {
  this.name = name;
}
public Person(String firstName, String lastName) {
   this(firstName + " " + lastName);
}

Is there a way in Ruby to achieve this same result: two constructors that take different arguments?

7 Answers

checkout functional-ruby gem which is inspired by Elixir pattern matching features.

   class Person
     include Functional::PatternMatching

     defn(:initialize, String) { |name| 
       @name = name 
     }

     defn(:initialize, String, String) {|first_name, last_name| 
      @name = first_name + ' ' + last_name
     }
   end

You could use the double splat operator ** in conjunction with logical or (double pipes) || inside the initialize method to achieve the same effect.

class Person
  def initialize(**options)
    @name = options[:name] || options[:first_name] << ' ' << options[:last_name]
  end
end

james = Person.new(name: 'James')
#=> #<Person @name="James">

jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
#=> #<Person @name="Jill Masterson">

However, if a new Person is created without a first_name, then the append << operation will fail with NoMethodError: undefined method '<<' for nil:NilClass. Here is a refactored initialize method to handle this case (using strip to remove whitespace if either option is excluded).

class Person
  def initialize(**options)
    @name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
  end
end

goldfinger = Person.new(last_name: 'Goldfinger')
#=> #<Person @name="Goldfinger">

oddjob = Person.new(first_name: 'Oddjob')
#=> #<Person @name="Oddjob">

In fact, this approach handles calling Person.new without arguments or with an unexpected key to return the new instance with @name set to an empty string:

nameless = Person.new
#=> <#Person @name="">

middle_malcom = Person.new(middle_name: 'Malcom')
#=> <#Person @name="">
Related