【问题标题】:Intercepting def in a block在块中截取def
【发布时间】:2013-04-09 21:51:15
【问题描述】:

我有一个方法需要在附加的块上做一些巫术。这种块的样本可能是

myMethod do 

   somemethod x
   someother y
   def name(a,b)
     a+b
   end
end

前两个方法调用(somemethod xsomeother y)应该正常执行。但是,我想在不实际定义新方法的情况下截取方法定义(作为 S 表达式)。如果我将整个块转换为 S 表达式然后搜索 AST,我可以做到这一点。但是,我需要弄清楚如何调用这些方法。任何一个的解决方案都可以。要么是

  • 截取定义,转换为S-expression(Ruby2Ruby可以理解)
  • 将块转换为 S 表达式并找到方法调用并执行这些

编辑 我正在寻找的 AST 类似于

[:defn,
 :name,
 [:args,:a,:b],
 [:call,
     [lvar,:a],
     :+
     [lvar,:b]]]

【问题讨论】:

  • 你能多说一些关于需求的事情吗? name 你想要什么?
  • @RubyLovely 我已经更新了一个示例
  • 看来Module#method_added 回调可能会让你为所欲为。
  • @dbenhur 据我从文档中可以看出,在添加方法后调用回调。这可能会通过 undefine/remove 解决,但是名称冲突将是一个问题,并且处理会提高复杂性,并且 method_add 仅提供方法的名称,因此我必须挖掘方法的实现(这似乎不过要容易)
  • 是的,这就是为什么我只提供指针而不提供答案的原因。看起来钩子可以让你抓住定义然后使用 ruby​​2ruby 的 AST 工具包来修改和替换方法。回调确实在任何有机会调用该方法之前执行。

标签: ruby metaprogramming


【解决方案1】:

如果我理解正确,您希望能够在传递给另一个方法的代码块中定义一个方法,但拦截该内部方法定义,以便它实际上没有被定义,而是转换为 S 表达式?所以你希望它的行为几乎就像它被注释掉并且一些外部过程已经通过源代码并将实现解析成一个 S 表达式?

类似:

myMethod do 

   somemethod x
   someother y
   # def name(a,b)
   #   a+b
   # end
end

somemethodsomeother 仍在执行的地方,name 的实现已定义,但被 Ruby 解释器忽略。然后,您想以某种方式将此实现捕获为 S 表达式。显然不会这样做,通过注释掉它,但它是我描绘行为的好方法。

嗯,RubyParser gem 可能会做你想做的事。它利用了将“here document”作为字符串传递给方法的能力,如下例所示:

def x (str)
  str
end

x( <<-EOF )
this is
   a here
document
EOF

# => "this is\n   a here\ndocument\n"

使用 RubyParser,您可以执行以下操作:

require 'ruby_parser'
require 'pp'

pp RubyParser.new.parse( <<-EOF )
def plus(x,y)
  x+y
end
EOF
# => s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))

然后,将它与您想要的代码合并是一件小事,如下所示:

require 'ruby_parser'

def myMethod
  x = yield
end

def somemethod( x )
  puts x
end

def someother( y )
  puts y
end

x = 'magic'
y = 'output'

sexp = myMethod do
  somemethod x
  someother y
  RubyParser.new.parse( <<-EOF )
  def name(a,b)
    a+b
  end
  EOF
end

pp sexp
name(1,2)
# magic
# output
# s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))
# NoMethodError: undefined local variable or method 'name' for main:Object

如你所见,方法定义本质上只是一个字符串,可以这样操作。

【讨论】:

  • 感谢您的建议,但是我已经有一个适用于像您这样的字符串的解决方案我需要它来处理实际的代码块
猜你喜欢
  • 2012-03-23
  • 2011-11-10
  • 1970-01-01
  • 2019-08-09
  • 2019-02-12
  • 2023-02-10
  • 1970-01-01
  • 1970-01-01
  • 2017-10-09
相关资源
最近更新 更多