18

Take this example:

write_as_string { puts 'x' }

I then want to be able to do

def write_as_string(&block)
  puts block.to_s
end

When I execute this, I want the output to be:

"puts 'x'"

I want to be able to receive the block and get the actual code for the block instead of executing it.

Motivation: Creating a DSL, I want to the mock to be converted into a number of other method calls, hidden from the calling code - using existing objects and methods without monkey patching them.

Any ideas on this would be great!

Thanks

Ben

Phil Ross
  • 25,590
  • 9
  • 67
  • 77
Ben Hall
  • 1,927
  • 5
  • 25
  • 39

3 Answers3

19

If you're on Ruby 1.9, you can use the sourcify gem. It provides Proc#to_source, which is like ParseTree's Proc#to_ruby.

When using sourcify, if you have nested procs in your source code, you might have to help it along with the :attached_to option:

## (Works in Ruby 1.8) Using ParseTree (with parse_tree_extensions)
block.to_ruby
## (Works in Ruby 1.9) Using sourcify
block.to_source
## Try this if you get Sourcify::NoMatchingProcError or Sourcify::MultipleMatchingProcsPerLineError
block.to_source :attached_to => :name_of_block_in_source_code

I posted about ParseTree and Ruby 1.9 in my company's blog.

Seamus Abshere
  • 8,326
  • 4
  • 44
  • 61
  • 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:30
3

Duplicate: Printing the source code of a Ruby block

sudo gem install ParseTree
sudo gem install ruby2ruby

then

require 'rubygems'
require 'parse_tree'
require 'parse_tree_extensions'
require 'ruby2ruby'

def block_as_string &block
    block.to_ruby
end

results in

irb(main):008:0> block_as_string {puts 'x'}
=> "proc { puts(\"x\") }"
Community
  • 1
  • 1
mletterle
  • 3,968
  • 1
  • 24
  • 24
1

You want the ruby2ruby gem, which does this nicely. Unfortunately, to analyze a block this gem depends on ParseTree, which is unsupported in Ruby 1.9.

Sam
  • 1,205
  • 1
  • 21
  • 39
austinfromboston
  • 3,791
  • 25
  • 25
  • That seems cool. But given the following example, I need to somehow get the defined block as a string. How could I do this? ruby = "def a\n puts 'A'\nend\n\ndef b\n a\nend" parser = RubyParser.new ruby2ruby = Ruby2Ruby.new sexp = parser.process(ruby) Or have I missed something? – Ben Hall Jan 17 '10 at 21:42
  • if you're not concerned about 1.9 compatibility, i'll recommend @mletterle's answer. – austinfromboston Jan 20 '10 at 04:01