How to alias_method_chain initialize from a mixin.

E

Evan Senter

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 :mad: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!
 
E

Eric Hodel

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.
 
E

Evan Senter

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 :mad:_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!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,998
Latest member
MarissaEub

Latest Threads

Top