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.