Nu-hin/remote_ruby

View on GitHub
lib/remote_ruby/runner.rb

Summary

Maintainability
A
35 mins
Test Coverage
# frozen_string_literal: true

require 'colorize'

require 'remote_ruby/unmarshaler'

module RemoteRuby
  # Runner class is responsible for running a prepared Ruby code with given
  # connection adapter, reading output and unmarshalling result and local
  # variables values.
  class Runner
    def initialize(code:, adapter:, out_stream: $stdout, err_stream: $stderr)
      @code = code
      @adapter = adapter
      @out_stream = out_stream
      @err_stream = err_stream
    end

    def run
      locals = nil

      adapter.open(code) do |stdout, stderr|
        out_thread = read_stream(stdout, out_stream, :green)
        err_thread = read_stream(stderr, err_stream, :red)
        [out_thread, err_thread].each(&:join)
        locals = out_thread[:locals]
      end

      { result: locals[:__return_val__], locals: locals }
    end

    private

    attr_reader :code, :adapter, :out_stream, :err_stream

    def read_stream(read_from, write_to, color)
      Thread.new do
        until read_from.eof?
          line = read_from.readline

          if line.start_with?('%%%MARSHAL')
            Thread.current[:locals] ||= unmarshal(read_from)
          else
            write_to.puts "#{adapter.connection_name.send(color)}>\t#{line}"
          end
        end
      end
    end

    def unmarshal(stdout)
      unmarshaler = RemoteRuby::Unmarshaler.new(stdout)
      unmarshaler.unmarshal
    end
  end
end