用于打印类中所有方法调用和返回的 Ruby 模块

Posted

技术标签:

【中文标题】用于打印类中所有方法调用和返回的 Ruby 模块【英文标题】:Ruby module to print all method calls and returns in class 【发布时间】:2020-09-08 11:55:19 【问题描述】:

我正在尝试编写一个模块,无论其内容如何,​​它都可以用于每个类:

该模块应执行以下操作:

每当调用方法时打印名称方法及其参数。

打印该方法的返回值。

举个例子:

class A
  extend Loginator

  def add(a, b)
    a + b
  end

  def sub(a, b)
    a - b
  end
  logify_me #this where the "logging happens!"
end


a = A.new
a.add(3, 5)
a.sub(7, 4)

输出

Methode add(3, 5) called
returns 8
Methode sub(7, 4) called
returns 3

我不知道从哪里开始。 我已经阅读了以下链接:

Difference between a class and a module / *** ref.

Writing Module for Ruby / *** ref.

Ruby - Modules, and Mixins

Ruby modules: Include vs Prepend vs Extend

Modules in Ruby: Part I

所以我做了以下但我有点卡住了:

第一次尝试

module Loginator
  def logify_me(name)
    attr_reader name
    define_method("#name=") do |val|
      puts "#name=#val"
      instance_variable_set("@#name", val)
    end
  end
end

class Example
  extend Loginator
  logify_me :an_attribute
end

e = Example.new
e.an_attribute = 12
p e.an_attribute 

此代码的问题是,首先我必须为每个方法严格编写logify_me,如果我单独编写logify_me,则不会打印任何内容

第二次尝试

module Loginator
  def logify_me
    self.instance_methods.each do |method|
      define_method(method) do |*args|
        puts "Method #method(#args.join(', '))"
        puts "returns #args"
        args#problem this return the args not the results of each method?!
      end
    end
  end
end

请注意,我可以使用TracePoint.trace(:call),但这不是我们想要的:)。

感谢用户@pedrosfdcarneiro 指出包装模块 解决方案并提供此ref

【问题讨论】:

欢迎来到 SO。你的问题问得不好。您向我们提出了要求,但没有向我们展示自己解决问题的任何尝试。 SO 不是代码编写服务,我们帮助您调试您编写的代码。请参阅“How to Ask”、“Stack Overflow question checklist”和“MCVE”及其所有链接页面。 @theTinMan 添加了我目前所在的位置。因此,如果您打算提供帮助,这是正确的时间:) 【参考方案1】:

您可以通过为执行所需日志逻辑的类的每个公共实例方法定义一个新的包装器方法来实现。

module Loginator
  def logify_me
    self.public_instance_methods.each do |method|
      proxy = Module.new do
        define_method(method) do |*args|
          puts "Method #method(#args.join(', ')) called"
          # added join to match the exact desired output

          value = super *args

          puts "returns #value"

          value
        end
      end
      self.prepend proxy
    end
  end
end

class A
  extend Loginator

  def add(a, b)
    a + b
  end

  def sub(a, b)
    a - b
  end

  logify_me
end

产生以下输出:

>> A.new.sub(1,2)
Method 'sub' called with args '[1, 2]'
returns -1
=> -1

>> A.new.add(4,7)
Method 'add' called with args '[4, 7]'
returns 11
=> 11

对未来的解释:)

define_method(method)

在接收器中定义一个实例方法。方法参数可以是 Proc、Method 或 UnboundMethod 对象。如果指定了块,则将其用作方法体。此块使用 instance_eval ref. can be found here 评估

prepend

prepend 会将模块插入到链的底部,甚至在类本身之前。fourth ref on my post

@pedrosfdcarneiro 编辑的部分

请添加一些关于第二个内部模块proxy = Module.new do end 的简要说明,以及为什么它对返回值和 super 的使用很重要。

加上你为什么更喜欢使用public_instance_methods 而不是instance_methods

请添加一些关于它的解释,因为首先它对我来说有点不清楚,其次对于那里的其他菜鸟。 在此先感谢:)

这个答案很大程度上基于这个奇妙的答案:https://***.com/a/23898539/1781212。

【讨论】:

这工作得很好,但是请您添加一些关于proxy = Module.new do end 的用法以及为什么它对返回值很重要的解释。另外,我有点不明白super *args,所以如果你能为我以及未来的人添加一些关于这些事情的解释,我将不胜感激。再次感谢:)

以上是关于用于打印类中所有方法调用和返回的 Ruby 模块的主要内容,如果未能解决你的问题,请参考以下文章

Ruby - 在类中获取非祖先方法的数组

Ruby祖先链中的方法访问有啥问题

[NativeScript中用于PDF打印的Android实现

Ruby 中的私有模块方法

Ruby——输入&输出

使用 ruby​​、Sinatra 和 Mandrill 如何从 api 调用访问单个元素并打印为 HTML?