How do you stringize/serialize Ruby code? How do you stringize/serialize Ruby code? ruby ruby

How do you stringize/serialize Ruby code?


Use Ruby2Ruby

def save_for_later(&block)  return nil unless block_given?  c = Class.new  c.class_eval do    define_method :serializable, &block  end  s = Ruby2Ruby.translate(c, :serializable)  s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1|').sub(/end$/, '}')endx = 40s = save_for_later { |y| x + y }# => "lambda { |y|\n  (x + y)\n}"g = eval(s)# => #<Proc:0x4037bb2c@(eval):1>g.call(2) # => 42

This is great, but it does not close over free variables (like x) and serialize them along with the lambda.

To serialize variables also, you can iterate over local_variables and serialize them as well. The problem, though, is that local_variables from within save_for_later accesses only c and s in the code above -- i.e. variables local to the serialization code, not the caller. So unfortunately, we must push the grabbing of local variables and their values to the caller.

Maybe this is a good thing, though, because in general, finding all free variables in a piece of Ruby code is undecidable. Plus, ideally we would also save global_variables and any loaded classes and their overridden methods. This seems impractical.

Using this simple approach, you get the following:

def save_for_later(local_vars, &block)  return nil unless block_given?  c = Class.new  c.class_eval do    define_method :serializable, &block  end  s = Ruby2Ruby.translate(c, :serializable)  locals = local_vars.map { |var,val| "#{var} = #{val.inspect}; " }.join  s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1| ' + locals).sub(/end$/, '}')endx = 40s = save_for_later(local_variables.map{ |v| [v,eval(v)] }) { |y| x + y }# => "lambda { |y| _ = 40; x = 40;\n  (x + y)\n}"# In a separate run of Ruby, where x is not defined...g = eval("lambda { |y| _ = 40; x = 40;\n  (x + y)\n}")# => #<Proc:0xb7cfe9c0@(eval):1>g.call(2)# => 42# Changing x does not affect it.x = 7g.call(3)# => 43


Use sourcify

This will work on Ruby 1.8 or 1.9.

def save_for_later(&block)  block.to_sourceendx = 40s = save_for_later {|y| x + y }# => "proc { |y| (x + y) }"g = eval(s)# => #<Proc:0x00000100e88450@(eval):1>g.call(2) # => 42

See my other answer for capturing free variables.

Update:Now you can also use the serializable_proc gem, which uses sourcify, and captures local, instance, class, and global variables.