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

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

Asfand Yar Qazi wrote:

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

Asfand Yar Qazi wrote:

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.

Joel VanderWerf wrote:

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

Joel VanderWerf wrote:

Joel VanderWerf wrote:

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

Asfand Yar Qazi wrote:

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.

Asfand Yar Qazi wrote:

Asfand Yar Qazi wrote:

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.