加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – 使用method_added了解ruby元编程以动态覆盖实

发布时间:2020-12-17 02:34:36 所属栏目:百科 来源:网络整理
导读:我有来自Programming Ruby 1.9的以下代码(略微适应)我只是想确保我的思维过程是准确的 module Trace def self.included(culprit) #Inject existing methods with tracing code: culprit.instance_methods(false).each do |func| inject(culprit,func) end #O
我有来自Programming Ruby 1.9的以下代码(略微适应)我只是想确保我的思维过程是准确的

module Trace
  def self.included(culprit)
    #Inject existing methods with tracing code:
    culprit.instance_methods(false).each do |func|
      inject(culprit,func)
    end

    #Override the singletons method_added to ensure all future methods are injected.
    def culprit.method_added(meth)
      unless @trace_calls_internal
        @trace_calls_internal = true
        Trace.inject(self,meth) #This will call method_added itself,the condition prevents infinite recursion.
        @trace_calls_internal = false
      end
    end
  end

  def self.inject(target,meth)
    target.instance_eval do
      #Get the method
      method_object = instance_method(meth)
      #Rewrite dat sheet
      define_method(meth) do |*args,&block|
        puts "==> Called #{meth} with #{args.inspect}"
        #the bind to self will put the context back to the class.
        result = method_object.bind(self).call(*args,&block)
        puts "<== #{meth} returned #{result.inspect}"
        result
      end
    end
  end
end

class Example
  def one(arg)
    puts "One called with #{arg}"
  end
end
#No tracing for the above.
ex1 = Example.new
ex1.one("Sup") #Not affected by Trace::inject

class Example #extend the class to include tracing.
  include Trace #calls Trace::inject on existing methods via Trace::included
  def two(a1,a2) #triggers Example::method_added(two) and by extension Trace::inject
    a1 + a2
  end
end

ex1.one("Sup again") #Affected by Trace::inject
puts ex1.two(5,4) #Affected by Trace::inject

我仍然试图围绕它是如何工作的.我希望有人能确认我的思考过程,因为我想确保我理解这里发生了什么.评论是我自己添加的.我真的认为我对方法绑定,单例类和元编程的理解充其量只是新手.

首先,Trace :: included由包含它的任何类调用.此方法执行两项操作,获取该类中现有函数的列表(如果有)并使用inject重写其方法.然后它修改包含模块的类的单例类,并覆盖默认的method_added方法,以确保每次添加方法时,附加的include注入都会影响它.此方法使用变量来防止无限递归,因为对inject的调用将根据其性质唤起method_added.

Trace ::的工作原理如下:使用instance_eval将self设置为类定义中存在的上下文.因此,范围(?)被修改为在该类定义中的范围.

然后我们将method_object设置为instance_method(meth),它将获得将添加的原始方法.由于instance_method没有显式接收器,它将默认为self,这与在类定义中的上下文相同?

然后我们使用define_method来定义具有相同名称的方法.因为我们位于instance_eval的上下文中,所以这相当于定义实例方法并将覆盖现有方法.我们的方法接受任意数量的参数和一个块(如果存在).

我们添加了一些flare来放置我们的“Tracing”,然后我们也调用我们存储在method_object中的原始方法,因为原始文件被覆盖了.这个方法是未绑定的,所以我们必须使用bind(self)将它绑定到当前上下文,因此它具有与最初相同的上下文?然后我们使用call并传递参数和块,存储其返回值,并在打印后返回其返回值.

我真的希望我能够充分地描述这一点.我的描述准确吗?我特别不确定粗体内容和以下内容:

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

解决方法

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.

使用实例eval,您可以自我绑定对象来评估块,在这种情况下,该块将是包含模块的类. (即罪魁祸首).为了清楚起见,两者之间存在差异:

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

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

答:所以是的,你在这个假设中是正确的!

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?

是的,你的假设是正确的.请注意,询问:

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

在这个上下文中调用实例方法确实与调用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?

是.当您以跟踪模块中定义的方式获取方法时,您将获得一个未绑定的方法对象,该对象可以按照描述再次绑定.

如果你想深入研究Ruby的元编程,我推荐下面的书:http://pragprog.com/book/ppmetr/metaprogramming-ruby它解释了Ruby的对象系统,mixins,块和你能想象的任何东西背后的所有细节.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读