Understanding ruby metaprogramming using method_added to overwrite instance methods dynamically

一曲冷凌霜 提交于 2019-12-03 08:04:09

Trace::works as follows: set self to the context that exists within the class definition using instance_eval. Therefore the scope(?) is modified to how it would be within that class definition.

Using instance eval you evaluate the block with self bound to the object, which in this case will be the class that is including the module. (I.e. the culprit). Just for clarity, there is a difference between:

o = Object.new
o.instance_eval do
  puts self
end

and

class Foo < Object end
Foo.instance_eval do  puts self end

Answer: So yes you are correct in this assumption!


Then we set method_object to instance_method(meth) which will get the original method that will added. Since instance_method does not have an explicit receiver, it will default to self which will be the same as the context of being within the class definition?

Yes, you are correct in your assumption. Do note that, with asking:

culprit.instance_methods(false) => [:someselector, :someotherselector]

And calling instance method in this context is indeed the same as calling self.instance_method.


This method is unbound, so we must bind it to the current context using bind(self) so it has the same context as it would originally?

Yes. When you get a method in the way defined in the trace module, you get an unbound method object which can be bound again as described.


If you want to dive into Ruby's metaprogramming, I do recommend the following book: http://pragprog.com/book/ppmetr/metaprogramming-ruby it explains all the gritty details behind Ruby's object system, mixins, blocks and anything you can imagine.

Thiago Lewin

Your description is ok!

Basically, what Trace module does is wrap class methods, so you can run any code before/ after method execution; transparently for the caller.

The Trace module uses Ruby Hooks, some methods that are called when something happens (module include, method added, etc). You can find some information on web about it, ex:

In this code:

method_object.bind(self).call(*args, &block)

As you mentioned, you are calling an unbound method using the original method context (self make reference to the original object, since we are inside of instance_eval method) and "repassing" any argument or block to it.

It is important to observe, the method_object is an unbound method, so you must have to bind a context to it, otherwise will throw a NoMethodError exception.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!