Last Updated: February 25, 2016
·
1.531K
· koraktor

Running destructive Ruby code in a sandbox

Trying to build on Ruby code that isn't properly namespaced and additionally doesn't provide a real API for external users might be frustrating. I made this experience while building braumeister.org a package browser for the well-known OS X package manager Homebrew. Homebrew is a CLI program written in Ruby that provides a great user interface, but doesn't feature an API for third-party access.

The term "destructive Ruby code" might sound a bit extreme, but it's pretty much dead-on for Homebrew. Importing the packages (aka. formulae) from Homebrew will always dirty the global namespace and cleaning up manually might be a bit tedious. Additionally, formulae might be broken from time to time because of the vast number of updates they get every day. So, how to bypass these problems?

The solution is called IO.pipe and it ships with Ruby. ;)

# Open a pipe that can be used to communicate between two processes
pipe_main, pipe_fork = IO.pipe

pid = fork do
  # The code in the fork block runs in another, forked Ruby process
  # It won't interfere with your main process

  begin
    # First, close the main process' end of the pipe
    pipe_main.close

    # Run the evil code here and dump any objects of interest into the pipe
    Marshal.dump some_object, pipe_fork
  rescue
    # Dump any exception to the pipe
    Marshal.dump $!, pipe_fork
  end

  # Close the pipe end of the forked process and exit
  pipe_fork.flush
  pipe_fork.close
  exit!
end

# The following code will run in parallel to the forked process above

# First, close the forked process' end of the pipe
pipe_fork.close
# Read objects of interest from the pipe
some_object = Marshal.load pipe_main
# Wait for the forked process to end
Process.wait pid
# Close the pipe end of the main process
pipe_main.close

# The forked process might also raise an exception, so you might want to re-raise it
raise some_object if some_object.is_a? Exception

That's it, the external code will not break your code any more.