In Ruby is there a way to overload the initialize constructor?
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:
- Default values for arguments
- Variable Argument lists (The splat operator)
- Defining your argument as a hash
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
How to overload contructor in Ruby?
Problems with your code:
You use a monkey patch instead of inheriting, so in your
initialize
method,super
will call the initialize method ofObject
, which is the super class of BigDecimal. To call the default constructor, you have to use some other method as showing below.You did not put arguments for the
initialize
method.BigDecimal DOES take float as constructor argument
Therefore,
You can use directly the default constructor and pass a float as:
BigDecimal.new(34.343, 5) # 5 is the precision
You can override the constructor in this way:
NOTE: we usually alias initialize
method. However in this case this does not seem to work (for some unknown reason that initialize
does not get called)... So we have to alias new
method which is more fundamental.
require 'bigdecimal'
class BigDecimal
class << self
alias_method :__new__, :new #alias the original constructor so we can call later
def new(*args)
if args.length == 1 && args[0].is_a?(Float)
__new__(args[0].to_s)
else
__new__(*args)
end
end
end
def format
sprintf("%.2f", self)
end
end
BigDecimal.new(12.334)
#<BigDecimal:10a9a48,'0.12334E2',18(18)>
Ruby make 2 different constructors with same ammount of parameters
You can create as many constructors as you want on the class with whatever name you want. There is one constructor new
, which is inherited from Object
, and that can be used to write other constructors. What other answers mention as the constructor, namely the instance method initialize
is not a constructor. That is the method called by the constructor method new
by default.
class Foo
def self.new1 text, name, array1
obj = new
# do something on obj with text, name, array1
obj
end
def self.new2 text, name, number
obj = new
# do something on obj with text, name, number
obj
end
end
Foo.new1(text, name, array1)
Foo.new2(text, name, number)
having multiple constructors in ruby
initialize
is actually not a constructor. You can indeed have two constructors.
class One
singletonclass.class_eval{alias old_new :new}
def self.new a
puts a
old_new
end
def self.new_1 a, b
puts a, b
old_new
end
end
Ruby - Overload solution for initialize with 3 params
This could be a solution, but well it's ugly!
def initialize(*args)
case (args.length)
when 1
super(XXX)
@y = args[0]
when 2
if args[0].kind_of?(A) then
super(args[0])
@x = args[0]
@y = args[1]
elsif args[0].kind_of?(B) then
super(XXX)
@y = args[0]
@z = args[1]
end
when 3
super(args[0])
@x = args[0]
@y = args[1]
@z = args[2]
end
end
How can I override Array Constructor in Ruby?
So the #initialize
override is failing because you're using object splat (**
) instead of array splat (*
).
But you shouldn't do this. Subclassing Ruby's core classes will lead to all sorts of counter-intuitive behavior because they have many methods that create new instances of that class -- that won't be updated to create new instances of your class. For example, Library.new.reverse
and Library.new + Library.new
will both return new arrays, not libraries.
Instead you can get the behavior you want by creating an array instance variable in your class and delegating to it. You can define #each
and get all the Ruby enumerable methods. You can also define any array methods you want.
class Library
include Enumerable
attr_accessor :authors
def initialize(*args)
@authors = {key1: 'val1', key2: 'val2'}
@array = args
end
def each(&block)
@array.each(&block)
end
def [](index)
@array[index]
end
end
Inherit initialize() method in ruby?
Umm... eh? Yes it does... Check this out:
class A
def initialize
@a = "foo"
end
end
class B < A
def to_s
@a
end
end
puts B.new
# "foo" is printed
This works because initialize
is inherited, just like any other method. If you override it by having a new sub-initialize
, it stops working. Then you can explicitly use super
to call the parent's initialize
.
Ruby class initialize override module initialize
It is not a good programming practice to hard code a fixed string like
"Unnamed"
as the value for@name
. In such case, you should assignnil
, and do whatever modification to it when you print it. Suppose you do this.Then
is_pet
can be deduced from whethername
isnil
or not, so it is redundant to have that as an instance variable. You can simply apply!!
toname
in order to getis_pet
. Therefore, you should get rid of such instance variable.You have
get_
prefixes as getter methods, but in Ruby, it is a better practice to have the same name as the instance variables (without the atmark) as the getter name.
This will give you:
module Pet
attr_reader :name
def initialize name; @name = name end
end
class Dog
include Pet
attr_reader :tricks
def initialize tricks, name
@tricks = tricks
super(name)
end
end
d = Dog.new ["roll", "speak", "play dead"], "Spots"
d.tricks #=> ["roll", "speak", "play dead"]
d.name #=> "Spots"
!!d.name #=> true (= `is_pet`)
How to override class initialize method and use FactoryBot?
FactoryBot
supports custom initializers looks like exactly your case:
FactoryBot.define do
factory :specific_keyword do
transient do
name { 'some default' }
end
initialize_with { new(attributes.merge(name: name)) }
end
end
https://robots.thoughtbot.com/factory-girl-2-5-gets-custom-constructors
Related Topics
Ruby - Parameters by Reference or by Value
Why How to Refer to a Variable Outside of an If/Unless/Case Statement That Never Ran
How to Break Out from a Ruby Block
In Rails, How to Render Json Using a View
Executing Code For Every Method Call in a Ruby Module
Tzinfo::Datasourcenotfound Error Starting Rails V4.1.0 Server on Windows
Converting a Nested Hash into a Flat Hash
How to Implement Enums in Ruby
Disable Activerecord For Rails 4
In Ruby on Rails, to Extend the String Class, Where Should the Code Be Put In
Difference Between Various Variables Scopes in Ruby
How to Test For (Activerecord) Object Equality
How to Create Multiple Submit Buttons For the Same Form in Rails