How to run a method right after method dispatch and before the method is called?
When I have the following:
class Foo
def bar
puts "#{__method__} was called and found within #{self}"
end
def method_missing(meth, *args, &blk)
puts "#{meth} was called and was not found within #{self}"
end
end
foo = Foo.new
foo.bar
# => bar was called and found within #<Foo:0x100138a98>
foo.baz
# => baz was called and was not found within #<Foo:0x100138a98>
I assume that when the method is found, the method dispatch looks a bit like so:
foo.bar was asked to be called
Search methods defined within #<Foo:0x100138a98>
Method `bar` found
Call the `bar` method
And for methods not found:
foo.baz was asked to be called
Search methods defined within #<Foo:0x100138a98>
Method `baz` not found
Search methods defined within the parent of #<Foo:0x100138a98>
Method `baz` not found
And so on until it hits the parent that has no parent
Loop back around and see if #<Foo:0x100138a98> has a `method_missing` method define开发者_C百科d
Method `method_missing` found
Call the `method_missing` method
I would like to step in like so:
foo.bar was asked to be called
Search methods defined within #<Foo:0x100138a98> to see it has a `method_dispatched` method
Method `method_dispatched` found
Calling `method_dispatched`
Search methods defined within #<Foo:0x100138a98>
...
This would allow developers to do something like below:
class Foo
def bar
puts "#{__method__} was called and found within #{self}"
end
def method_missing(meth, *args, &blk)
puts "#{meth} was called and was not found within #{self}"
end
def method_dispatched(meth, *args, &blk)
puts "#{meth} was called within #{self}..."
puts "continuing with the method dispatch..."
end
end
foo = Foo.new
foo.bar
# => bar was called within #<Foo:0x100138a98>...
# => continuing with the method dispatch...
# => bar was called and found within #<Foo:0x100138a98>
foo.baz
# => bar was called within #<Foo:0x100138a98>...
# => continuing with the method dispatch...
# => baz was called and was not found within #<Foo:0x100138a98>
This brings me to the question..
Is this possible?
I'm not aware of a callback for what you're looking for. I would read up on Ruby Delegators for a possible solution that's more elegant than what I've sketched below.
You can wrap the object and intercept on method_missing.
class A
def foo
puts "hi there, I'm A"
end
end
maybe have B inherit A?
class B
def initialize
@a = A.new
end
def method_missing(m, *args, &block)
puts "hi there, I'm in B"
@a.send(m, *args, &block) if @a.respond_to? m
puts "all done"
end
end
Sort of a Ruby beginner here, but I have some working solution. So please comment on the code if you think something is not ruby-esque.
The idea is to alias the methods that you have and undef_method the original method. This will create a alias for all your instance methods. You can then have a method_missing that can call method_dispatched and then the actual method.
class Test
def foo
"foo"
end
def method_dispatched(meth, *args, &blk)
puts "#{meth} was called within #{self}..."
puts "continuing with the method dispatch..."
end
def method_missing(name, *args, &block)
method_dispatched(name, args, block) #Calls your standard dispatcher
if (respond_to?('_' + name.to_s)) # Check if we have a aliased method
send('_' + name.to_s)
else
"undef" #Let the caller know that we cant handle this.
end
end
instance_methods(false).each do |meth|
if meth != 'method_missing' and meth != 'method_dispatched'
alias_method "_#{meth}", meth
remove_method(meth)
end
end
end
t = Test.new
puts t.foo()
puts t.undefined_method()
精彩评论