Search code examples
rubyconstructoroverloadingruby-1.9.2overriding

How to overload contructor in Ruby?


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

BigDecimal does not take a float as in initial value, so I am writing a constructor to handle it. It seems to be ignoring the initialize method and calling the default constructor.

It throws TypeError can't convert Float into String (TypeError)

The method format does work.

file BigDecimal.rb:

require 'bigdecimal'

class BigDecimal

    def initialize
        if self.class == Float
            super self.to_s
        end
    end

    def format
        sprintf("%.2f", self)
    end

end

Then, in file test.rb:

require 'BigDecimal' # => true
bd = BigDecimal.new('54.4477') # works
puts bd.format # 54.44
bd2 = BigDecimal.new(34.343) # TypeError: can't convert Float into String (TypeError)

Ruby 1.9.2


Solution

  • 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)>