Forum: Ruby block as string

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
54185df1d348bbd34587fcd4f8e4779b?d=identicon&s=25 Louis-Philippe (Guest)
on 2009-06-02 22:56
(Received via mailing list)
Hi All,
Is there an easy way to get a block / proc / lambda as a string...
without
any fancy serialization, no binding... just the code block as string?

Thanks!

L-P
83ca41657a99b65d99889abe712ba5e2?d=identicon&s=25 Jason Roelofs (Guest)
on 2009-06-02 23:31
(Received via mailing list)
You'll want to check out ruby2ruby:
http://seattlerb.rubyforge.org/ruby2ruby/

There's nothing built into Ruby that will get you this.

Jason
54185df1d348bbd34587fcd4f8e4779b?d=identicon&s=25 Louis-Philippe (Guest)
on 2009-06-02 23:48
(Received via mailing list)
mmm, I'm looking for a jRuby compatible solution... Ruby2Ruby look like
its
C dependant.
any other idea?

2009/6/2 Jason Roelofs <jameskilton@gmail.com>
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-06-03 11:08
Louis-Philippe wrote:
> any other idea?

Well, you can remember the string form at the time you define the
lambda.

def fn(str)
  res = eval "lambda { #{str} }"
  class <<res; self; end.class_eval { define_method(:source) { str } }
  res
end

a = fn "|x| puts x+x"
a["Hello"]
puts a.source
54185df1d348bbd34587fcd4f8e4779b?d=identicon&s=25 Louis-Philippe (Guest)
on 2009-06-03 16:17
(Received via mailing list)
Thanks Brian,
It's a neat trick!  I'll remember it!

I'm not sure I'll use it though, as it require you define the function
inside a string...
a bit cumbersome.

what I need is some sort of macro system (the LISP macro, not the C
one),
to parse the block before calling it.  I was thinking to make it a
string,
then parsing with regex, would have done it... but seems not.  I'll work
out
an other logic around the problem then ;).

L-P

2009/6/3 Brian Candler <b.candler@pobox.com>
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-06-03 16:43
Louis-Philippe wrote:
> I'm not sure I'll use it though, as it require you define the function
> inside a string...
> a bit cumbersome.

How about using %{ ... } for the string? Then it looks more like a
normal lambda.

a = fn %{ |x| puts x+x }
a["Hello"]
puts a.source

Or you can read the source from its own file, or for longer snippets you
can use a here-doc.

a = fn <<'EOF'
{ |x| puts x }
EOF

> what I need is some sort of macro system (the LISP macro, not the C one),
> to parse the block before calling it.  I was thinking to make it a string,
> then parsing with regex, would have done it...

Ruby doesn't have macros. If you write a block in the usual way, the
Ruby interpreter will turn it into a Block object, and you cannot get
its source form back (in MRI anyway)

If you are only interested in simple regexp source transformations, then
you should start from a string form as above, transform, then eval.

For more complex transformations, still starting with a string, you
might be able to use one of the ruby-in-ruby implementations (e.g.
rubinius) or ParseTree to parse and transform it, I'm not sure.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-06-03 16:57
# Slightly enhanced version
module Kernel
  alias :orig_lambda :lambda
  def lambda(src=nil,*rest,&blk)
    if src.nil?
      orig_lambda(&blk)
    else
      res = eval "lambda { #{src} }", *rest
      class << res; self; end.class_eval { define_method(:source) { src
} }
      res
    end
  end
end

a = lambda { |x| puts x+x }
b = lambda %{ |x| puts x+x+x }
a["a"]
b["b"]
puts b.source

# %{...} performs substitutions as per double-quoted string
prefix = "hello"
c = lambda %{ |x| puts "#{prefix} " + x }
puts c.source
c["world"]

# %q{...} is like single-quoted string
d = lambda %q{ |x| puts "#{prefix} " + x }, binding
puts d.source
d["world"]
prefix = "goodbye"
d["world"]
5a837592409354297424994e8d62f722?d=identicon&s=25 Ryan Davis (Guest)
on 2009-06-03 20:50
(Received via mailing list)
On Jun 2, 2009, at 14:47 , Louis-Philippe wrote:

> mmm, I'm looking for a jRuby compatible solution... Ruby2Ruby look
> like its
> C dependant.
> any other idea?

jruby has their own parsetree equivalent, but I don't know of / doubt
they have their own r2r equivalent. I know they haven't bothered to
conform to the unified_ruby "spec" that I set up for all PT related
projects to ensure compatibility... so my guess is you're out of luck.
Ab870531383eea6e4d9110317f5401e7?d=identicon&s=25 Caleb Clausen (Guest)
on 2009-06-04 00:09
(Received via mailing list)
On 6/3/09, Louis-Philippe <default@spiralix.org> wrote:
> what I need is some sort of macro system (the LISP macro, not the C one),
> to parse the block before calling it.  I was thinking to make it a string,
> then parsing with regex, would have done it... but seems not.  I'll work out
> an other logic around the problem then ;).

RubyMacros is an implementation of lisp-like macros for ruby....
however, unlike ParseTree, it doesn't allow you to get at the contents
of a block. It does however, provide lisp-like forms as well. Maybe
you can put whatever code you need into a form? Take a look here:

http://github.com/coatl/rubymacros
54185df1d348bbd34587fcd4f8e4779b?d=identicon&s=25 Louis-Philippe (Guest)
on 2009-06-04 19:15
(Received via mailing list)
Thanks guys,
while I think RubyMacro is a great idea (!!!),
I will wait until it get a bit more maturity and speed before using it.

I'll probably stick one of your trick onto a block Brian, something
like:

-----------------------------
$var = "global var"

def parsed_execution(list)
cmd_array = list.split("\n").map { |i| i.chomp.lstrip.rstrip }
# process cmd_array here
cmd_array.each_with_index do |o,i|
puts "cmd #{i}:"
$var = "Finished!!" if i == cmd_array.size-1
eval o
end
end

parsed_execution <<-BLOCK
puts "Line 1"
puts $var
puts "replacing global var..."
$var = "new global var"  # will never get printed!!!
puts $var
BLOCK
---------------------------------

I choosed the <<-EOF string because it is less likely to introduce
conflict
with the inner syntax... though %Q with a rightly choosed symbol might
also
be interesting.
what would be even neater would be to alias the <<- or %Q as something
smaller or more intuitive for my DSL, but I believe this is
impossible...

Thanks!

L-P




2009/6/3 Caleb Clausen <vikkous@gmail.com>
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-06-04 21:38
Louis-Philippe wrote:
> I'll probably stick one of your trick onto a block Brian, something
> like:

OK - not sure why you're eval'ing it a line at a time though, instead of
all at once, as that would prevent you doing lots of interesting things,
like loops written across multiple lines.

I see you're using a global var. You may want to check out instance_eval
(to set the default method receiver to an arbitrary object), and the
'binding' parameter to eval (to be able to access local variables from a
different scope)

Regards,

Brian.
54185df1d348bbd34587fcd4f8e4779b?d=identicon&s=25 Louis-Philippe (Guest)
on 2009-06-04 21:52
(Received via mailing list)
Thanks Brian,
The single line evaluation will be usefull in my purpose built DSL,
I'm essentially trying to build an iterative execution structure on top
of a
recusive context-free one.
Too complex to explain in a couple lines, and still not all clear to
me...
so the code I pasted is only an example, my project is all built using
classes and more cleanly scoped variables.

L-P

2009/6/4 Brian Candler <b.candler@pobox.com>
This topic is locked and can not be replied to.