Evaluating a string of Ruby code within an anonymous module: donecorrectly?

A

Asfand Yar Qazi

Hi,

I'd like to evaluate a string of Ruby code (stored in a YAML file, as
it happens) safely. Note the purpose is simply to avoid variable name
clashes and stop lots of eval'ed Ruby code from overloading the heap
with lots of objects (i.e. when the anonymous module is deleted, the
temporary variables defined within it are also gone.)

The purpose is NOT for safety reasons, so we don't have to worry about
that.

Am I doing it the right way? Or does anybody know a better way? Any
better way of passing in arguments to the code in the string?

Anyway, here goes:

def eval_string(s, *args_to_pass)
m = Module.new
m.const_set("ARGS", arg_to_pass)
m.class_eval(s.to_str)
end # Soon m will be GC'ed, and all temp objects gone! MUHAHA

s.eval_string(<<EOL, [1, 2, 3, 4, 5])
a = ARGS
b = a.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL



Thanks,
Asfand Yar
 
A

Asfand Yar Qazi

Asfand said:
Hi,

I'd like to evaluate a string of Ruby code (stored in a YAML file, as it
happens) safely. Note the purpose is simply to avoid variable name
clashes and stop lots of eval'ed Ruby code from overloading the heap
with lots of objects (i.e. when the anonymous module is deleted, the
temporary variables defined within it are also gone.)

The purpose is NOT for safety reasons, so we don't have to worry about
that.

Am I doing it the right way? Or does anybody know a better way? Any
better way of passing in arguments to the code in the string?

Actually, I forgot to mention the other main very important reason for
doing it this way: passing arguments to the eval'ed code. Also, I may
need to be able to eval several strings at once (kind of) so storing
the arguments in global variables is also a no-no.
Anyway, here goes:

def eval_string(s, *args_to_pass)
m = Module.new
m.const_set("ARGS", arg_to_pass)
m.class_eval(s.to_str)
end # Soon m will be GC'ed, and all temp objects gone! MUHAHA

s.eval_string(<<EOL, [1, 2, 3, 4, 5])
a = ARGS
b = a.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL



Thanks,
Asfand Yar
 
J

Joel VanderWerf

Asfand said:
Hi,

I'd like to evaluate a string of Ruby code (stored in a YAML file, as it
happens) safely. Note the purpose is simply to avoid variable name
clashes and stop lots of eval'ed Ruby code from overloading the heap
with lots of objects (i.e. when the anonymous module is deleted, the
temporary variables defined within it are also gone.)

The purpose is NOT for safety reasons, so we don't have to worry about
that.

Am I doing it the right way? Or does anybody know a better way? Any
better way of passing in arguments to the code in the string?

Anyway, here goes:

def eval_string(s, *args_to_pass)
m = Module.new
m.const_set("ARGS", arg_to_pass)
m.class_eval(s.to_str)
end # Soon m will be GC'ed, and all temp objects gone! MUHAHA

s.eval_string(<<EOL, [1, 2, 3, 4, 5])
a = ARGS
b = a.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL

Here's another way of passing in args, using a fixed method name
("main") rather than a fixed constant name ("ARGS"):

def eval_string(s, *args_to_pass)
m = Module.new
def m.method_added(name)
module_function(name)
end
m.class_eval(s.to_str)
m.main(*args_to_pass)
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
def main(*args)
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
end
EOL

That's nice if you want to break up main into several methods, define
constants, etc.

Here's an even simpler way, just wrapping the code in a proc, which may
be suitable for small snippets of code:

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end")
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL

Warning: the binding of the "pr" local variable and the two arg
variables is still in effect wthin the proc, so this may not be
suitable. Or you could use very obscure names and hope.
 
J

Joel VanderWerf

Joel said:
Here's an even simpler way, just wrapping the code in a proc, which may
be suitable for small snippets of code:

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end")
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL

Warning: the binding of the "pr" local variable and the two arg
variables is still in effect wthin the proc, so this may not be
suitable. Or you could use very obscure names and hope.

Just realized how to fix that:

def empty_binding
binding
end

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end", empty_binding)
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
#p pr #<-- now this fails
EOL
 
A

Asfand Yar Qazi

Joel said:
Joel said:
Here's an even simpler way, just wrapping the code in a proc, which
may be suitable for small snippets of code:

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end")
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
EOL

Warning: the binding of the "pr" local variable and the two arg
variables is still in effect wthin the proc, so this may not be
suitable. Or you could use very obscure names and hope.


Just realized how to fix that:

def empty_binding
binding
end

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end", empty_binding)
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
#p pr #<-- now this fails
EOL

I think I like this method best. very nice!

Thanks
 
A

Asfand Yar Qazi

Asfand said:
Just realized how to fix that:

def empty_binding
binding
end

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end", empty_binding)
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
#p pr #<-- now this fails
EOL

Just one thing: why the use of 'proc do ... end ' rather than lambda?
I know there's a difference, but I'm not sure what.
 
J

Joel VanderWerf

Asfand said:
Asfand said:
Just realized how to fix that:

def empty_binding
binding
end

def eval_string(s, *args_to_pass)
pr = eval("proc do |*args| #{s.to_str} end", empty_binding)
pr[*args_to_pass]
end

eval_string(<<EOL, [1, 2, 3, 4, 5])
b = args.collect {|i| i*3}
c = b.join("||")
puts(c)
#p pr #<-- now this fails
EOL

Just one thing: why the use of 'proc do ... end ' rather than lambda? I
know there's a difference, but I'm not sure what.


AFAIK, lambda and proc are the same in ruby. But there may have been
some talk about deprecating proc in the future.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,708
Latest member
SherleneF1

Latest Threads

Top