Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to override #initialize in a Ruby module?

Tags:

ruby

I have been trying to figure out how to extend the behavior of initialize from a module. I want to do it without calling super in initialize of the class that is being mixed into. I want to support the normal pattern of calling include I can't figure it out. I've read everything I can find on the matter and, while people to have suggestions, none of them actually seem to work (in my hands at least).

Here is what I (think) I know:

  • If it can be done at all, it has to be done using the hook on include (i.e. Module.included(base)).
  • The include hook will execute before the including class defines initialize so there is no point to simply trying to define initialize with base.instance_eval because it will get overwritten.
  • A suggestion was made to make use of the method_added hook and deal with it in there. That is what I'm trying now but it appears that the hook executes at the beginning of method definition so you end up with what you see below.

    module Mo
      def self.included(klass)
        klass.instance_eval do
          def method_added(method)
            puts "Starting creation of #{method} for #{self.name}"
            case method
            when :initialize
              alias_method :original_initialize, :initialize
              puts "About to define initialize in Mo"
              def initialize
                original_initialize
                puts "Hello from Mo#initialize"
              end
              puts "Finished defining initialize in Mo"
            end
            puts "Finishing creation of #{method} for #{self.name}"
          end
        end
      end
    end
    
    class Foo
      include Mo
      def initialize
        puts "Hello from Foo#initialize"
      end
    end
    
    foo = Foo.new
    

This results in the following output:

    Starting creation of initialize for Foo
    Starting creation of original_initialize for Foo
    Finishing creation of original_initialize for Foo
    About to define initialize in Mo
    Finished defining initialize in Mo
    Finishing creation of initialize for Foo
    Hello from Foo#initialize

It looks to me like initialize from class Foo is still overwriting the definition from the module. I'm guessing that this is because the definition is still open, suggesting that it isn't a matter of which block is started last be which is finished last that "wins".

If anyone out there really knows how to do this and have it work please enlighten me.

FWIW, yes, I think I have a good reason for wanting to do this.

like image 216
Huliax Avatar asked Jul 05 '13 22:07

Huliax


People also ask

Is it possible to override main method?

Overriding main methodYou cannot override static methods and since the public static void main() method is static we cannot override it.

Can we override possible in the same class?

The answer is No, you cannot override the same method in one class.

What is an example of override?

Verb Congress overrode the President's veto. These new rules override the old ones.

How do you overcome overriding?

The final way of preventing overriding is by using the final keyword in your method. The final keyword puts a stop to being an inheritance. Hence, if a method is made final it will be considered final implementation and no other class can override the behavior.


2 Answers

Ok, well in Ruby 1.9 you could add functionality to the new class method...

module Mo
  def new(*var)
    additional_initialize(*var)
    super(*var)
  end
  def additional_initialize(*var)
    puts "Hello from Mo"
  end
 end

class Foo
  extend Mo
  def initialize
    puts "Hello from Foo"
  end
end

foo = Foo.new

That returns...

Hello from Mo
Hello from Foo
like image 114
SteveTurczyn Avatar answered Nov 15 '22 18:11

SteveTurczyn


If you're on Ruby 2.0 or later, you can just use prepend. Either require the user to prepend rather than include, or do:

module Mo
  module Initializer
    def initialize
      puts "Hello from Mo#initialize"
      super
    end
  end

  def self.included(klass)
    klass.send :prepend, Initializer
  end
end
like image 36
Chuck Avatar answered Nov 15 '22 18:11

Chuck