Reputation: 17160
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?
Upvotes: 91
Views: 53257
Reputation: 28392
The answer is both Yes and No.
You can achieve the same result as you can in other languages using a variety of mechanisms including:
The actual syntax of the language does not allow you to define a method twice, even if the arguments are different.
Considering the three options above these could be implemented with your example as follows
# As written by @Justice
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
@name = name
end
end
class Person
def initialize(args)
name = args["name"]
name = name + " " + args["lastName"] unless args["lastName"].nil?
@name = name
end
end
class Person
def initialize(*args)
#Process args (An array)
end
end
You will encounter the second mechanism frequently within Ruby code, particularly within Rails as it offers the best of both worlds and allows for some syntactic sugar to produce pretty code, particularly not having to enclose the passed hash within braces.
This wikibooks link provides some more reading
Upvotes: 92
Reputation: 1948
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
Upvotes: 1
Reputation: 409
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="">
Upvotes: 2
Reputation: 1173
You can use konstructor gem to declare multiple constructors in Ruby and imitate overloading:
class Person
def initialize(name)
@name = name
end
konstructor
def from_two_names(first_name, last_name)
@name = first_name + ' ' + last_name
end
end
Person.new('John Doe')
Person.from_two_names('John', 'Doe')
Upvotes: 2
Reputation: 1570
class StatementItem
attr_reader :category, :id, :time, :amount
def initialize(item)
case item
when Order
initialize_with_order(item)
when Transaction
initialize_with_transaction(item)
end
end
def valid?
!(@category && @id && @time && @amount).nil?
end
private
def initialize_with_order(order)
return nil if order.status != 'completed'
@category = 'order'
@id = order.id
@time = order.updated_at
@amount = order.price
end
def initialize_with_transaction(transaction)
@category = transaction.category
@id = transaction.id
@time = transaction.updated_at
@amount = transaction.amount
end
end
Upvotes: 3
Reputation: 81500
I tend to do
class Person
def self.new_using_both_names(first_name, last_name)
self.new([first_name, last_name].join(" "))
end
def self.new_using_single_name(single_name)
self.new(single_name)
end
def initialize(name)
@name = name
end
end
But I don't know if this is the best approach.
Upvotes: 37
Reputation: 65435
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
@name = name
end
end
Upvotes: 4