class YleTf::System

Helpers to execute system commands with error handling

Constants

DEFAULT_ERROR_HANDLER
DEFAULT_IO_HANDLERS
ExecuteError

Public Class Methods

attach_input_handler(handler, io, progname) click to toggle source
# File lib/yle_tf/system.rb, line 77
def self.attach_input_handler(handler, io, progname)
  io_proc = IOHandlers.input_handler(handler)
  io_proc.call(io, progname)
end
attach_output_handler(handler, io, progname) click to toggle source
# File lib/yle_tf/system.rb, line 82
def self.attach_output_handler(handler, io, progname)
  io_proc = IOHandlers.output_handler(handler)
  io_proc.call(io, progname)
end
cmd(*args, **opts) click to toggle source

Executes the command and attaches IO streams

# File lib/yle_tf/system.rb, line 35
def self.cmd(*args, **opts)
  opts = DEFAULT_IO_HANDLERS.merge(opts)
  env = opts[:env] || {}
  progname = opts.fetch(:progname) { args.first }

  YleTf::Logger.debug { "Calling #{cmd_string(args, env)}" }

  status = Open3.popen3(env, *args, &handle_io(progname, opts))
  verify_exit_status(status, opts[:error_handler], cmd_string(args))
rescue Interrupt, Errno::ENOENT => e
  error(opts[:error_handler], "Failed to execute #{cmd_string(args)}: #{e}")
end
cmd_string(args, env = nil) click to toggle source
# File lib/yle_tf/system.rb, line 54
def self.cmd_string(args, env = nil)
  "`#{args.shelljoin}`#{" with env '#{env}'" if env && !env.empty?}"
end
console_cmd(*args, **opts) click to toggle source
# File lib/yle_tf/system.rb, line 25
def self.console_cmd(*args, **opts)
  env = opts[:env] || {}

  YleTf::Logger.debug { "Calling #{cmd_string(args, env)}" }

  system(env, *args)
  verify_exit_status($CHILD_STATUS, opts[:error_handler], cmd_string(args))
end
error(handler, error_msg, exit_code = nil) click to toggle source
# File lib/yle_tf/system.rb, line 94
def self.error(handler, error_msg, exit_code = nil)
  YleTf::Logger.debug(error_msg)

  handler ||= DEFAULT_ERROR_HANDLER
  handler.call(exit_code, ExecuteError.new(error_msg))
end
handle_io(progname, handlers) click to toggle source
# File lib/yle_tf/system.rb, line 58
def self.handle_io(progname, handlers)
  lambda do |stdin, stdout, stderr, wait_thr|
    in_thr = attach_input_handler(handlers[:stdin], stdin, progname)
    out_thr = [
      attach_output_handler(handlers[:stdout], stdout, progname),
      attach_output_handler(handlers[:stderr], stderr, progname)
    ]

    # Wait for the process to exit
    wait_thr.value.tap do
      YleTf::Logger.debug("`#{progname}` exited, killing input handler thread")
      in_thr.kill if in_thr.is_a?(Thread)

      YleTf::Logger.debug('Waiting for output handler threads to stop')
      ThreadsWait.all_waits(out_thr)
    end
  end
end
read_cmd(*args, **opts) click to toggle source
# File lib/yle_tf/system.rb, line 48
def self.read_cmd(*args, **opts)
  buffer = StringIO.new
  cmd(*args, **opts.merge(stdout: buffer))
  buffer.string
end
verify_exit_status(status, handler, cmd) click to toggle source
# File lib/yle_tf/system.rb, line 87
def self.verify_exit_status(status, handler, cmd)
  status.success? ||
    error(handler,
          "Failed to execute #{cmd} (#{status.exitstatus})",
          status.exitstatus)
end