class BaseScript

A base class for implementing CLI scripts. ARGV and in/out IO’s are injected, so can be mocked & tested. Basic signal handling by calling exit_on_signals inside work loops etc. Requires Ruby 2.0.0+ for keyword args etc.

Constants

EXIT_SUCCESS
INDENT
VERSION

Attributes

error_output[R]
input[R]

I/O

output[R]

Public Class Methods

new(argv, stdin: $stdin, stdout: $stdout, stderr: $stderr) click to toggle source
# File lib/base_script.rb, line 12
def initialize(argv, stdin: $stdin, stdout: $stdout, stderr: $stderr)
  @argv = argv

  @input = stdin
  @output = stdout
  @error_output = stderr
  sync_io!

  @indentation = 0
end

Private Instance Methods

arg(key, default = nil) click to toggle source

Fetch a –key or –key=value argument. Returns the provided default if not set.

# File lib/base_script.rb, line 51
def arg(key, default = nil)
  args.fetch(key.to_s, default)
end
arg!(key) click to toggle source

Like arg, but raises KeyError if missing.

# File lib/base_script.rb, line 56
def arg!(key)
  args.fetch(key.to_s) do
    raise KeyError, "--#{key} argument required"
  end
end
args() click to toggle source

Argument handling.

# File lib/base_script.rb, line 40
def args
  @_args ||= @argv.reduce({}) do |memo, argument|
    key, value = argument.split("=", 2)
    option = key.gsub(/\A-+/, "") # strip leading hyphens.
    memo[option] = value || true # store as true for value-less options.
    memo
  end
end
colorize(text, code) click to toggle source

Colorize text if output is a tty.

# File lib/base_script.rb, line 91
def colorize(text, code)
  if output.respond_to?(:tty?) && output.tty?
    "\033[#{code}m#{text}\033[0m"
  else
    text
  end
end
cross() click to toggle source

A red cross.

# File lib/base_script.rb, line 103
def cross; colorize("✘", 31) end
dry?() click to toggle source

Dry run support

# File lib/base_script.rb, line 137
def dry?; arg("dry-run") end
exit_on_signals() click to toggle source

Call this method prior to doing work inside a loop. Alternatively, call at start of script to install handlers, and then at safe-exit points throughout script. Don’t set up signal handlers (first call) and then fail to call again.

# File lib/base_script.rb, line 112
def exit_on_signals
  install_signal_handlers unless defined?(@_signal)

  if @_signal
    log "Exiting due to SIG#{@_signal}"
    exit(1)
  end
end
indent_string(content) click to toggle source
# File lib/base_script.rb, line 85
def indent_string(content)
  spaces = INDENT * @indentation
  content.each_line.map {|line| "#{spaces}#{line}" }.join
end
indented() { || ... } click to toggle source
# File lib/base_script.rb, line 78
def indented
  @indentation += 1
  yield
ensure
  @indentation -= 1
end
install_signal_handlers() click to toggle source
# File lib/base_script.rb, line 121
def install_signal_handlers
  @_signal = nil
  @_previous_signal_handlers = {}
  %w{INT TERM}.each do |signal|
    log "Installing #{signal} handler" if verbose?
    @_previous_signal_handlers[signal] = Signal.trap(signal) do
      log "Received SIG#{signal}, will exit at next opportunity"
      @_signal = signal
      Signal.trap(signal, @_previous_signal_handlers[signal])
    end
  end
end
log(message) click to toggle source

Logging.

# File lib/base_script.rb, line 69
def log(message)
  message += "\n" unless message[-1] == ?\n
  output << indent_string(message)
end
sync_io!() click to toggle source

Set I/O streams as unbuffered if they support it.

# File lib/base_script.rb, line 31
def sync_io!
  [input, output, error_output].each do |io|
    io.sync = true if io.respond_to?(:sync=)
  end
end
tick() click to toggle source

A green tick.

# File lib/base_script.rb, line 100
def tick; colorize("✔", 32) end
unless_dry_run(message) { || ... } click to toggle source

Execute block unless dry run

# File lib/base_script.rb, line 140
def unless_dry_run(message)
  if dry?
    vlog "Skipping due to dry run: #{message}"
  else
    vlog message
    yield
  end
end
verbose?() click to toggle source
# File lib/base_script.rb, line 62
def verbose?
  arg("v") || arg("verbose")
end
vlog(message) click to toggle source
# File lib/base_script.rb, line 74
def vlog(message)
  log(message) if verbose?
end