In Ruby How to Overload the Initialize Constructor

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:

  1. You use a monkey patch instead of inheriting, so in your initialize method, super will call the initialize method of Object, which is the super class of BigDecimal. To call the default constructor, you have to use some other method as showing below.

  2. You did not put arguments for the initialize method.

  3. BigDecimal DOES take float as constructor argument

Therefore,

  1. You can use directly the default constructor and pass a float as:

    BigDecimal.new(34.343, 5) # 5 is the precision
  2. 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 assign nil, and do whatever modification to it when you print it. Suppose you do this.

  • Then is_pet can be deduced from whether name is nil or not, so it is redundant to have that as an instance variable. You can simply apply !! to name in order to get is_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



Leave a reply



Submit