Module intercepts containing class's initialize method?

L

Lloyd Zusman

I'm wondering if something like this is possible:

module Intercept
def intercept_initialize
puts "performing Intercept#intercept_initialize"
# somehow call containing class's 'initialize' method here
end
end

class Foo
def initialize
puts "performing Foo#initialize"
end
end

class Bar
include Intercept
def initialize
puts "performing Bar#initialize"
end
end

puts "instatiating Foo"
Foo.new
puts "instatiating Bar"
Bar.new
puts "done"

Desired results:

instantiating Foo
performing Foo#initialize
instantiating Bar
performing Intercept#intercept_initialize
performing Bar#initialize
done

In other words, simply by including the "Intercept" module in a class,
I'd like to intercept that class's call to "initialize" and have the
Intercept module's code get invoked first, and _then_ the containing
class's "initialize" method should be called as it normally would.

Is this possible? If so, how?

Thanks in advance.
 
F

Florian Gross

Lloyd said:
class Bar
include Intercept
def initialize
puts "performing Bar#initialize"
end
end

In other words, simply by including the "Intercept" module in a class,
I'd like to intercept that class's call to "initialize" and have the
Intercept module's code get invoked first, and _then_ the containing
class's "initialize" method should be called as it normally would.

I think the only way of doing that without putting the include() behind
the initialize def would be using a method_added hook...

I'd also like there to be a better way of doing this and if I'm not
completely wrong and confusing things matz is already thinking about
this issue.
 
L

Lloyd Zusman

Florian Gross said:
I think the only way of doing that without putting the include() behind
the initialize def would be using a method_added hook...

Could you explain how this could be done with a method_added hook?

I'd also like there to be a better way of doing this and if I'm not
completely wrong and confusing things matz is already thinking about
this issue.

Yes, that would be great.


Thank you very much.
 
F

Florian Gross

Lloyd said:
Could you explain how this could be done with a method_added hook?

Like this:

require 'thread'

module Intercept
def self.included(other)
install_initialize = lambda do
other.class_eval do
alias :eek:ld_initialize :initialize

def initialize(*args, &block)
intercept_initialize
old_initialize(*args, &block)
end
end
end

class << other; self; end.class_eval do
alias :eek:ld_method_added :method_added

ignore = false
define_method:)method_added) do |name|
return if ignore

case name
when :initialize then
Thread.exclusive do
ignore = true
install_initialize.call
ignore = false
end
else
old_method_added(name)
end
end
end

install_initialize.call
super
end

def intercept_initialize
puts "performing Intercept#intercept_initialize"
end
end

class Bar
include Intercept
def initialize
puts "performing Bar#initialize"
end
end

Bar.new

But note that in that case the user could still redefine
Bar.method_added() and circumvent the restore logic. I think adding a
singleton_method_added handler would fix that.

I'm also not sure if the Thread.exclusive stuff can be done in a cleaner
way...
 
A

Assaph Mehr

Lloyd said:
I'm wondering if something like this is possible:

module Intercept
def intercept_initialize
puts "performing Intercept#intercept_initialize"
# somehow call containing class's 'initialize' method here
end
end

class Foo
def initialize
puts "performing Foo#initialize"
end
end

class Bar
include Intercept
def initialize
puts "performing Bar#initialize"
end
end

puts "instatiating Foo"
Foo.new
puts "instatiating Bar"
Bar.new
puts "done"

Desired results:

instantiating Foo
performing Foo#initialize
instantiating Bar
performing Intercept#intercept_initialize
performing Bar#initialize
done

In other words, simply by including the "Intercept" module in a class,
I'd like to intercept that class's call to "initialize" and have the
Intercept module's code get invoked first, and _then_ the containing
class's "initialize" method should be called as it normally would.

Simply redefine IncludingClass::new like this:

.. module Intercept
.. def self.included(mod)
.. super(mod)
.. def mod.intercept_initialize
.. puts "performing Intercept#intercept_initialize"
.. # somehow call containing class's 'initialize' method here
.. end
.. def mod.new *args
.. inst = super *args
.. intercept_initialize
.. inst
.. end
.. end
.. end
..
.. class Foo
.. def initialize
.. puts "performing Foo#initialize"
.. end
.. end
..
.. class Bar
.. include Intercept
.. def initialize
.. puts "performing Bar#initialize"
.. end
.. end
..
.. puts "instatiating Foo"
.. Foo.new
.. puts "instatiating Bar"
.. Bar.new
.. puts "done"
 
R

Robert Klemme

Lloyd Zusman said:
I'm wondering if something like this is possible:

module Intercept
def intercept_initialize
puts "performing Intercept#intercept_initialize"
# somehow call containing class's 'initialize' method here
end
end

class Foo
def initialize
puts "performing Foo#initialize"
end
end

class Bar
include Intercept
def initialize
puts "performing Bar#initialize"
end
end

puts "instatiating Foo"
Foo.new
puts "instatiating Bar"
Bar.new
puts "done"

Desired results:

instantiating Foo
performing Foo#initialize
instantiating Bar
performing Intercept#intercept_initialize
performing Bar#initialize
done

In other words, simply by including the "Intercept" module in a class,
I'd like to intercept that class's call to "initialize" and have the
Intercept module's code get invoked first, and _then_ the containing
class's "initialize" method should be called as it normally would.

Is this possible? If so, how?

The proper way is to use super IMHO:

module Intercept
def initialize(*a,&b)
super
puts "performing Intercept#intercept_initialize"
# somehow call containing class's 'initialize' method here
end
end

class Foo
def initialize
super
puts "performing Foo#initialize"
end
end

class Bar
include Intercept
def initialize
super
puts "performing Bar#initialize"
end
end

All other suggested approaches (changing a class's new or using a hook)
are more complicated than necessary IMHO.

Kind regards

robert
 

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,979
Messages
2,570,185
Members
46,727
Latest member
FelicaTole

Latest Threads

Top