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