How to alias_method_chain initialize from a mixin

Hi,

I’ve been having a little trouble the past couple days getting an
alias_method_chain working on the initialize method via a module getting
mixed into a class. For example:


module Dsl
def self.included(base)
base.instance_eval do
include InstanceMethods

  alias_method :initialize_without_block_support, :initialize
  alias_method :initialize, :initialize_with_block_support
end

end

module InstanceMethods
def initialize_with_block_support
p “initialize_with_block_support”
initialize_without_block_support
end
end
end

class Something
include Dsl

def initialize
p “initialize_without_block_support”
end
end

Something.new #=> prints “initialize_without_block_support”, should
print “initialize_with_block_support” as well.


The problem in this contrived example (I believe) is that initialize is
redefined in the Something class body after the module is mixed in, thus
negating the alias_method_chain that was set up previously. So one
option is to just include modules after the initialize method is
defined, but that’s not at all elegant, so I’ve been trying to find a
better way.

The next possibility I considered was using the method_added hook.
Something that would work like the following:


module Dsl
def self.included(base)
base.extend ClassMethods
base.instance_eval { include InstanceMethods }
end

module ClassMethods
def method_added(name)
if name == :initialize && !@redefining_initialize
@redefining_initialize = :stop_those_infinite_loops!

    alias_method :initialize_without_block_support, :initialize
    alias_method :initialize, :initialize_with_block_support

    remove_instance_variable :@redefining_initialize
  end
end

end

module InstanceMethods
def initialize_with_block_support
p “initialize_with_block_support”
initialize_without_block_support
end
end
end

class Something
include Dsl

def initialize
p “initialize_without_block_support”
end
end

Something.new


This works, but feels pretty hackety. If you happen to have any
experience doing something like this and have an alternative solution,
or feedback on this implementation using method_added, I’d appreciate
hearing it. As a note, the @redefining_initialize guard is necessary
because alias_method triggers the method_added hook.

Thanks for your time!

On Nov 21, 2009, at 23:09, Evan Senter wrote:

Hi,

I’ve been having a little trouble the past couple days getting an
alias_method_chain working on the initialize method via a module
getting
mixed into a class. For example:

Don’t use alias_method_chain.

Ever.

You already have inheritance.

It works far better.

Use super:

module DSL
def initialize
puts “dsl initialize”
end
end

class Something
include DSL

def initialize
super
puts “something initialize”
end
end

Oh, inheritance also works when you don’t bother to define initialize
in Something.

I explored your idea of using inheritance a bit further, and though it’s
not entirely transparent to the consuming class (super has to be called)
it is a clean implementation, and would look something like this:

class A
def initialize(&block)
@_original_self = block.binding.eval(“self”)
instance_eval(&block) if block_given?
remove_instance_variable :@_original_self
finish_init if defined? :finish_init
end

def print_me_a
p “print_me_a”
end

def method_missing(name, *args, &block)
@_original_self ? @_original_self.send(name, *args, &block) : super
end
end

class B < A
def initialize
super
end

def finish_init
p “finish_init”
end

def print_me_b
p “print_me_b”
end
end

class C
def self.run
B.new do
print_me_a
print_me_b
print_me_c
end
end

def self.print_me_c
p “print_me_c”
end
end

b = C.run

#=> “print_me_a”
#=> “print_me_b”
#=> “print_me_c”
#=> “finish_init”

Thanks for the input, it was helpful!