Hooking on method invocation

Hello,

Is there a way to hook on invocation of any instance method of an
object?

My first attempt was to override the send method:

class Foo
#just ban them
def send(symbol, *args)
puts "blocked"
end
end

It works when I do:

foo = Foo.new
foo.send :something

But it has no effect on “conventional” method invocations. It seems
that send is not a part of the built-in method dispatch procedure.

I want such functionality to implement ‘write barriers’ for certain
objects. The idea was to track invocation of writer methods and notify
object’s observer of changes. If this is achievable by other means,
any hints will be greatly appreciated.

···


Eugene

Delegation maybe?

The “barrier keeper” object checks in method_missing whether a method
will modify the object or not, notifies the observers if so, and delegates
the actual work to another object (the one being observed).

The “actual” object would have to provide a list of the methods for which
notification is needed and what action to perform (possibly via a Proc).

···

On Wed, Oct 16, 2002 at 10:43:05PM +0900, Eugene Zaikonnikov wrote:

Hello,

Is there a way to hook on invocation of any instance method of an
object?

My first attempt was to override the send method:

class Foo
#just ban them
def send(symbol, *args)
puts “blocked”
end
end

It works when I do:

foo = Foo.new
foo.send :something

But it has no effect on “conventional” method invocations. It seems
that send is not a part of the built-in method dispatch procedure.

I want such functionality to implement ‘write barriers’ for certain
objects. The idea was to track invocation of writer methods and notify
object’s observer of changes. If this is achievable by other means,
any hints will be greatly appreciated.


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Turn right here. No! NO! The OTHER right!

viking@funcall.org (Eugene Zaikonnikov) wrote in message news:680a835d.0210160530.4ffa6227@posting.google.com

Hello,

Is there a way to hook on invocation of any instance method of an
object?

Hi,

I am sorry that I think the whole discussion has advanced to a new
level; but to answer the original question, can’t we do a simple thing
like:

$blocked = true
(Foo.new.methods - Object.methods).each do |met|
str = “class Foo\n”
str << " alias #{met}_original #{met}\n"
str << " def #{met} (*args)\n"
str << " if $blocked\n"
str << " puts ‘blocked’\n"
str << " else\n"
str << " #{met}_original(*args)"
str << " end\n"
str << " end\n"
str << “end”
eval str
end

? The code above can easily be modified to pick which methods to block
(by matching “met” with a regexp), to notify the observer, etc. This is
of course if we don’t want to go into some aspect-oriented programming, as
also has been suggested.

Regards,

Bill

···

============================================================================
Eugene Zaikonnikov viking@funcall.org wrote:

Hello,

Is there a way to hook on invocation of any instance method of an
object?

My first attempt was to override the send method:

class Foo
#just ban them
def send(symbol, *args)
puts “blocked”
end
end

It works when I do:

foo = Foo.new
foo.send :something

But it has no effect on “conventional” method invocations. It seems
that send is not a part of the built-in method dispatch procedure.

I want such functionality to implement ‘write barriers’ for certain
objects. The idea was to track invocation of writer methods and notify
object’s observer of changes. If this is achievable by other means,
any hints will be greatly appreciated.


Eugene

Code follows:

module Observable

pretty lame and useless, but I didn’t feel like removing it after all this

typing; it could probably automate the generation of the list in a

sensible way

def get_method_list

end
end

class BarrierKeeper
def initialize(obj)
@theobj = obj
end

def method_missing(sym,*args)
h = @theobj.get_method_list
ret = true
ret = h[sym].call if h[sym]
@theobj.send(sym, *args) if ret
end

end

class AnObject
include Observable

def initialize
@block_proc = proc { puts “ACTION: Blocked!”; false }
@notify_proc = proc do
puts “ACTION: notify the observers; w/ ‘require "observer"’”
true
end
end

def amethod
puts “METH: You won’t see this”
end

def another
puts “METH: This one is not blocked.”
end

def get_method_list
{ :amethod => @block_proc, :another => @notify_proc }
end

end

a = AnObject.new
theproxy = BarrierKeeper.new a
theproxy.amethod
theproxy.another

···

On Wed, Oct 16, 2002 at 11:17:58PM +0900, Mauricio Fernández wrote:

On Wed, Oct 16, 2002 at 10:43:05PM +0900, Eugene Zaikonnikov wrote:

Hello,

Is there a way to hook on invocation of any instance method of an
object?

My first attempt was to override the send method:

class Foo
#just ban them
def send(symbol, *args)
puts “blocked”
end
end

It works when I do:

foo = Foo.new
foo.send :something

But it has no effect on “conventional” method invocations. It seems
that send is not a part of the built-in method dispatch procedure.

I want such functionality to implement ‘write barriers’ for certain
objects. The idea was to track invocation of writer methods and notify
object’s observer of changes. If this is achievable by other means,
any hints will be greatly appreciated.

Delegation maybe?


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Q: What’s the big deal about rm, I have been deleting stuff for years? And
never lost anything… oops!
A: …
– From the Frequently Unasked Questions

beta4.com

Look at fwrap, I think that you can have a better interface with it

http://www.franz.com/support/documentation/6.2/doc/fwrappers-and-advice.htm

Guy Decoux

Is there a way to hook on invocation of any instance method of an
object?

[snip]

Code follows:

module Observable

[snip]

theproxy.another

that seems pretty complicated - it seems like what is needed is a
specialization of a class, espcially since only the assignment methods need
overridden. isn’t this the job of inheritence? eg:

def assignment_hook(*args)
re = /`([^‘]+)’/
m = re.match caller(1)[0]
m or raise ‘FAILED TO LOOKUP METHOD NAME’
methodname = m[1]

notify observer here…

puts “hook for #{methodname}(#{args.join ‘,’}) called…”
end

class Original
def foo=(*args)
# do something
end
end

class Hooked < Original
def foo=(*args)
assignment_hook *args
super *args
end
end

h = Hooked.new
h.foo= 0,1,2 # → hook for foo=(0,1,2) called…

simple : yes
fast : yes
safe : yes
typing : yes

-ara

···

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Is there a way to hook on invocation of any instance method of
an
object?

[snip]

Code follows:

module Observable

[snip]

theproxy.another

that seems pretty complicated - it seems like what is needed is a
specialization of a class, espcially since only the assignment methods
need
overridden. isn’t this the job of inheritence? eg:

In fact this could be meaningful w/ any method that changes the state
of an object, assignment or not.

def assignment_hook(*args)
re = /`([^‘]+)’/
m = re.match caller(1)[0]
m or raise ‘FAILED TO LOOKUP METHOD NAME’
methodname = m[1]

notify observer here…

puts “hook for #{methodname}(#{args.join ‘,’}) called…”
end

class Original
def foo=(*args)
# do something
end
end

class Hooked < Original
def foo=(*args)
assignment_hook *args
super *args
end
end

h = Hooked.new
h.foo= 0,1,2 # → hook for foo=(0,1,2) called…

simple : yes
fast : yes
safe : yes
typing : yes

Yes, but you have to redefine all the methods needing some kind of
“write barrier”. The way I showed before, the BarrierKeeper is in fact a
proxy which decides whether a method call should be forwarded or not;
and it is easily mixed with the Observer pattern.

What I don’t like about inheritance is having to type
def method(*args)
hook(*args)
super *args
end
which is of course more of a matter of taste. And then again, it would be
possible to create something like
set_barrier :method_name, proc { |method, args| … }
which could work either via inheritance or delegation.

Delegation gives you more freedom, for a BarrierKeeper could potentially
take care of different objects during runtime, even those of an
unknown class! If you use inheritance, you end up having to know the
class of the object or using lots of singleton methods. And you’d have
to use the evil eval :slight_smile:

Guess it’s just another case of object vs class design pattern…

···

On Wed, Oct 16, 2002 at 04:10:14PM +0000, ahoward wrote:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

I once witnessed a long-winded, month-long flamewar over the use of
mice vs. trackballs… It was very silly.
– Matt Welsh