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

Is is possible to see the ruby code in the proc?

inspect returns the memory location:

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

Ruby 1.9.3

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
B Seven
  • 44,484
  • 66
  • 240
  • 385

5 Answers5

9

Take a look at the sourcify gem:

proc { x + y }.to_source
# >> "proc { (x + y) }"
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • 2
    sourcify has a big warning for anything after Ruby 1.9. I had luck with the source_method gem for Ruby 2.1.2 (which pry includes as a dependency so was already available in our test infrastructure where I needed it). I have a detailed answer here: http://stackoverflow.com/questions/1675053/printing-the-source-code-of-a-ruby-block/36654421#36654421 – Nick B Apr 15 '16 at 18:31
9

Do you mean the original source code or its bytecode representation ?

For the former you may use standard Proc's method source_location

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

and read the appropriate lines of code.

For the latter it may come handy the RubyVM::InstructionSequence and its class method 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"
David Unric
  • 7,421
  • 1
  • 37
  • 65
  • 2
    `RubyVM` is a non-standard extension by YARV. It is not part of the Ruby Language Specification. MRI doesn't have it, Rubinius doesn't have it, MagLev doesn't have it, JRuby doesn't have it, IronRuby doesn't have it. I'd rather not rely on something that only works on a single Ruby implementation. – Jörg W Mittag Feb 22 '13 at 13:21
  • @JörgWMittag So MRI got stuck at 1.8 and Ruby 1.9 is not *the official* implementation ? – David Unric Feb 22 '13 at 13:27
  • @JörgWMittag On the other hand I generally agree to not rely on specific implementation features, but it's sometimes hard to keep. – David Unric Feb 22 '13 at 13:31
  • 2
    Yes, MRI is stuck at 1.8. There are no intentions for MRI to support any later versions of Ruby. Matz himself stopped working in MRI a long time ago, he is focusing on YARV (written by Koichi Sasada) and more recently on MRuby (an implementation of ISO Ruby written by himself). Ruby 1.9 is not an implementation, it is a language. There is no official implementation of Ruby 1.9, there is a language specification, and every implementation that fulfills that spec is every bit as official as every other. Some of those implementations are YARV, MacRuby, JRuby and Rubinius. – Jörg W Mittag Feb 22 '13 at 13:33
5

No, there is no way to do that in Ruby.

Some Ruby implementations may or may not have implementation-specific ways of getting the source code.

You can also try to use Proc#source_location to find the file that the Proc was defined in, and then parse that file to find the source code. But that won't work if the Proc wasn't defined in a file (e.g. if it was defined dynamically with eval) or if the source file no longer exists, e.g. because you are running an AOT-compiled version of your program.

So, the short answer is: no, there is no way. The long answer is: there are some ways that may or may not sometimes work depending on way too many factors to even begin to make this work reliably.

That's not even taking into account Procs which don't even have a Ruby source code because they were defined in native code.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
2

If proc is defined into a file, U can get the file location of proc then serialize it, then after deserialize use the location to get back to the proc again

proc_location_array = proc.source_location

after deserialize:

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}")

Shimaa Marzouk
  • 429
  • 4
  • 10
2

Although an old question, still, I wanted to share my thoughts.

You can use Pry gem and end up with something like this:

[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' }

Also, if you would use it from Rails context, you can put:

::Kernel.binding.pry

in your controllers or models, and

- require 'pry'; binding.pry

in your views, where you want to start debugging.

And in the tests, I use a combination, first require 'pry' at the top, and then ::Kernel.binding.pry where needed.

References:

Zalom
  • 696
  • 9
  • 18