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.
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.
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"
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.
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...
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.
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...
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 :old_initialize :initialize
def initialize(*args, &block)
intercept_initialize
old_initialize(*args, &block)
end
end
end
class << other; self; end.class_eval do
alias :old_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...