在一个proc中能否看到Ruby代码?

15
p = Proc.new{ puts 'ok' }

请问在proc中能看到Ruby代码吗?

inspect会返回内存位置:

puts p.inspect
#<Proc:0x007f9e42980b88@(irb):2>

Ruby 1.9.3

5个回答

9

看一下 sourcify gem:

proc { x + y }.to_source
# >> "proc { (x + y) }"

2
Sourcify对Ruby 1.9之后的任何内容都有一个大警告。我在Ruby 2.1.2中使用了source_method gem(pry将其作为依赖项包含,因此已经在我们的测试基础设施中可用),我在这里有一个详细的答案:https://dev59.com/SnI-5IYBdhLWcg3wxruQ#36654421 - Nick B

9
您是指原始的源代码还是其字节码表示?对于前者,您可以使用标准Proc方法source_location
p.source_location
=> ["test.rb", 21]

"and read the appropriate lines of code." 可以翻译成“并阅读相应的代码行。”
对于后者,RubyVM::InstructionSequence及其类方法disassemble可能会很方便。
irb> RubyVM::InstructionSequence.disasm p
=> "== disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>
=====\n== catch table\n| catch type: redo   st: 0000 ed: 0011 sp: 0000
cont: 0000\n| catch type: next   st: 0000 ed: 0011 sp: 0000 cont:
0011\n|------------------------------------------------------------------------\n
0000 trace            1                                               
(   1)\n0002 putself          \n0003 putstring        \"ok\"\n0005 
send             :puts, 1, nil, 8, <ic:0>\n0011 leave            \n"

2
RubyVM 是 YARV 的非标准扩展。它不是 Ruby 语言规范的一部分。MRI 没有它,Rubinius 没有它,MagLev 没有它,JRuby 没有它,IronRuby 没有它。我宁愿不依赖于仅适用于单个 Ruby 实现的东西。 - Jörg W Mittag
@JörgWMittag,所以MRI卡在1.8了,Ruby 1.9不是官方实现吗? - David Unric
@JörgWMittag 另一方面,我通常同意不依赖于特定的实现特性,但有时很难做到。 - David Unric
2
是的,MRI停留在1.8版本。MRI没有支持任何更高版本的Ruby的意图。Matz本人很久以前就停止了在MRI上的工作,他现在专注于YARV(由Koichi Sasada编写)和最近的MRuby(他自己编写的ISO Ruby实现)。Ruby 1.9不是一种实现,而是一种语言。没有官方的Ruby 1.9实现,只有一种语言规范,符合该规范的每个实现都与其他实现一样官方。其中一些实现包括YARV、MacRuby、JRuby和Rubinius。 - Jörg W Mittag

5
不,用Ruby没有办法做到那一点。 某些Ruby实现也许有特定于实现的方法来获取源代码。 你也可以尝试使用 Proc#source_location 查找定义 Proc 的文件,然后解析该文件找到源代码。但是,如果 Proc 没有在文件中定义(例如使用 eval 动态定义),或者源文件不再存在(例如因为您正在运行程序的 AOT 编译版本),那么这种方法将无效。 因此,简短的答案是:不,没有办法。 长答案是:有些方法可能会或可能不会起作用,具体取决于太多的因素,以至于无法可靠地进行操作。 这甚至还没有考虑那些由本地代码定义而根本没有 Ruby 源代码的 Proc。

2

虽然这是一个老问题,但我仍想分享我的想法。

您可以使用Pry gem,最终得到类似于以下内容:

[11] pry> p = Proc.new{ puts 'ok' }
=> #<Proc:0x007febe00e6360@(pry):23>

[12] pry> show-source p

From: (pry)
Number of lines: 1

p = Proc.new{ puts 'ok' }

此外,如果您要在Rails上下文中使用它,则可以放置以下内容:
::Kernel.binding.pry

在你的控制器或模型中,以及...
- require 'pry'; binding.pry

在您的观点中,您希望从哪里开始调试。

在测试中,我使用了一种组合方式,首先在顶部添加require 'pry',然后在需要的地方添加::Kernel.binding.pry

参考:


1
如果proc定义在文件中,您可以获取proc的文件位置并将其序列化,然后在反序列化后使用该位置再次返回到proc。

proc_location_array = proc.source_location

反序列化后:

file_name = proc_location_array[0]

line_number = proc_location_array[1]

proc_line_code = IO.readlines(file_name)[line_number - 1]

proc_hash_string = proc_line_code[proc_line_code.index("{")..proc_line_code.length]

proc = eval("lambda #{proc_hash_string}")


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接