开发者

How to run a method right after method dispatch and before the method is called?

开发者 https://www.devze.com 2023-03-16 00:17 出处:网络
When I have the following: class Foo def bar puts \"#{__method__} was called and found within #{self}\" end

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()
0

精彩评论

暂无评论...
验证码 换一张
取 消