是否可以在 proc 中看到 ruby​​ 代码?

Posted

技术标签:

【中文标题】是否可以在 proc 中看到 ruby​​ 代码?【英文标题】:Is it possible to see the ruby code in a proc? 【发布时间】:2013-02-08 02:10:24 【问题描述】:
p = Proc.new puts 'ok' 

inspect返回内存位置:

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

Ruby 1.9.3

【问题讨论】:

【参考方案1】:

看看sourcify gem:

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

【讨论】:

sourcify 对 Ruby 1.9 之后的任何内容都有一个很大的警告。我对 Ruby 2.1.2 的 source_method gem 很幸运(它作为依赖项包含在我们需要它的测试基础设施中)。我这里有详细回答:***.com/questions/1675053/…【参考方案2】:

你是指原始源代码还是它的字节码表示?

对于前者,您可以使用标准 Proc 方法source_location

p.source_location
=> ["test.rb", 21]

并阅读相应的代码行。

对于后者,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"

【讨论】:

RubyVM 是 YARV 的非标准扩展。它不是 Ruby 语言规范的一部分。 MRI 没有,Rubinius 没有,MagLev 没有,JRuby 没有,IronRuby 没有。我宁愿不依赖仅适用于单个 Ruby 实现的东西。 @JörgWMittag 所以 MRI 卡在 1.8 并且 Ruby 1.9 不是官方实现? @JörgWMittag 另一方面,我通常同意不依赖特定的实现功能,但有时很难保持。 是的,MRI 卡在 1.8。 MRI 无意支持任何更高版本的 Ruby。 Matz 本人很久以前就停止了 MRI 的工作,他专注于 YARV(由 Koichi Sasada 编写),最近专注于 MRuby(他自己编写的 ISO Ruby 的实现)。 Ruby 1.9 不是一种实现,它是一种语言。 Ruby 1.9 没有正式的实现,有一个语言规范,每个实现该规范的实现都和其他的一样正式。其中一些实现是 YARV、MacRuby、JRuby 和 Rubinius。【参考方案3】:

不,在 Ruby 中没有办法做到这一点。

某些 Ruby 实现可能有也可能没有特定于实现的方法来获取源代码。

您也可以尝试使用Proc#source_location 查找定义Proc 的文件,然后解析该文件以查找源代码。但是如果Proc 没有在文件中定义(例如,如果它是用eval 动态定义的)或者如果源文件不再存在,那么这将不起作用,例如因为您正在运行程序的 AOT 编译版本。

所以,简短的回答是:不,没有办法。 长答案是:有些方法有时可能会起作用,也可能不会起作用,这取决于太多的因素,甚至无法开始使这项工作可靠。

这还没有考虑到Procs,它甚至没有 Ruby 源代码,因为它们是在本机代码中定义的。

【讨论】:

【参考方案4】:

如果proc被定义到一个文件中,U可以得到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")

【讨论】:

【参考方案5】:

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

您可以使用 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 在需要的地方。

参考文献:

http://pryrepl.org https://github.com/pry/pry https://github.com/pry/pry/wiki/Source-browsing

【讨论】:

以上是关于是否可以在 proc 中看到 ruby​​ 代码?的主要内容,如果未能解决你的问题,请参考以下文章

如何存储 ruby​​ 代码块

在 Ruby 中是不是可以显式创建局部变量

procs 可以与 Ruby 2.0 中的 case 语句一起使用吗?

ruby 重构其他代码的结果,使用时间库和功能proc。

Ruby - lambda 与 Proc.new [重复]

为啥 to_proc 在 Ruby 改进中不起作用?