Module: Sus::Fixtures::IsolatedRubyContext

Defined in:
lib/sus/fixtures/isolated_ruby_context.rb

Overview

Evaluates Ruby in a fresh process and returns its result through Marshal.

Defined Under Namespace

Classes: Error

Instance Method Summary collapse

Instance Method Details

#isolated_ruby(source, chdir: Dir.pwd, env: {}, requires: []) ⇒ Object

Evaluate source using the current Ruby interpreter, without sharing Ruby state or changing the caller's working directory. The final expression is serialized with Marshal.dump and restored with Marshal.load. Printed output goes to the inherited stderr. Exceptions are marshaled back and re-raised with their original backtraces. SystemExit follows the child process's exit status.

Raises:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/sus/fixtures/isolated_ruby_context.rb', line 37

def isolated_ruby(source, chdir: Dir.pwd, env: {}, requires: [])
	script = <<~'RUBY'
		->(output) do
			$stdout.reopen($stderr)
			begin
				source = $stdin.read
				ARGV.each{|feature| require feature}
				result = eval(source, TOPLEVEL_BINDING, File.join(Dir.pwd, "(isolated ruby)"))
				output.write(Marshal.dump([result, nil]))
			rescue SystemExit
				raise
			rescue Exception => error
				output.write(Marshal.dump([nil, error]))
			end
		end.call($stdout.dup.binmode)
	RUBY
	
	output = IO.popen([env, RbConfig.ruby, "-e", script, "--", *requires], "r+b", chdir: chdir) do |process|
		begin
			process.write(source)
		rescue Errno::EPIPE
			# A startup failure may close stdin before accepting the source:
		end
		process.close_write
		process.read
	end
	status = $?
	raise Error.new(status) unless status.success?
	return nil if output.empty?
	
	result, error = Marshal.load(output)
	raise error if error
	result
end