Ravn::CLI::
Subcommand
module
Convenience module for subcommand registration syntax sugar.
Extension callback – register the extending object as a subcommand.
def self::extended( mod )
Ravn::CLI.log.debug "Registering subcommands from %p" % [ mod ]
Ravn::CLI.register_subcommands( mod )
end
display_table( header, rows )
Output a table with the given header
(an array) and rows
(an array of arrays).
def display_table( header, rows )
table = TTY::Table.new( header, rows )
renderer = nil
if hl.enabled?
renderer = TTY::Table::Renderer::Unicode.new(
table,
multiline: true,
padding: [0,1,0,1]
)
renderer.border.style = :dim
else
renderer = TTY::Table::Renderer::ASCII.new(
table,
multiline: true,
padding: [0,1,0,1]
)
end
puts renderer.render
end
Invoke the user’s editor on a copy of the file at path
(if it exists), and after the process exits move the copy back over the original unless it’s a zero-byte file.
def edit( path )
raise "Shell is not interactive" unless $stdin.isatty
edit_cmd = find_editor() or exit_now!( "Couldn't find a suitable editor!" )
path = Pathname( path ).expand_path
tmpfile = Tempfile.create( [path.basename, 'edit'] )
edit_cmd += [ tmpfile.path ]
FileUtils.cp( path, tmpfile.path ) if path.exist?
if system( *edit_cmd )
if File.size?( tmpfile.path )
FileUtils.mv( tmpfile.path, path )
return true
else
return false
end
else
return false
end
ensure
File.unlink( tmpfile.path ) if tmpfile && File.exist?( tmpfile.path )
end
edit_and_read( filename, initial_content: '' )
Invoke the user’s editor on a tempfile with a name based on filename
, then read it in and return the contents.
def edit_and_read( filename, initial_content: '' )
raise "Shell is not interactive" unless $stdin.isatty
edit_cmd = find_editor() or exit_now!( "Couldn't find a suitable editor!" )
tmpname = [
File.basename( filename, File.extname(filename) ),
File.extname( filename )
]
Tempfile.create( tmpname ) do |tmpfile|
tmpfile.write( initial_content )
tmpfile.flush
edit_cmd += [ tmpfile.path ]
if system( *edit_cmd )
tmpfile.reopen( tmpfile.path, 'r' )
return tmpfile.read
else
return nil
end
end
end
Return the specified string
in the ‘error’ ANSI color.
def error_string( string )
return hl.error( string )
end
Execute the specified command
.
def execute( *command )
command.flatten!( 1 )
options = command.pop if command.last.is_a?( Hash )
options ||= {}
ignore_errors = options.delete( :ignore_errors )
command = Shellwords.split( command.first ) if command.size == 1
command_desc = highlight_string( Shellwords.join(command) )
errlog = Tempfile.new( 'ravn-cli.log' )
options[:err] = errlog
options[:out] ||= errlog unless $VERBOSE || $DEBUG
self.log.debug "Running command `%s`" % [ command_desc ]
rval = system( *command, options )
errlog.rewind
if rval
self.log.debug " command executed successfully."
elsif rval.nil?
Ravn::CLI.exit_now! "%s `%s` (%d)" % [
error_string( "Failed to execute" ),
command_desc,
$?.exitstatus
]
elsif !ignore_errors
Ravn::CLI.prompt.say "%s `%s`: \n%s" % [
error_string( "Error while running" ),
command_desc,
errlog.read
]
end
return rval
ensure
errlog.unlink if errlog
end
exit_now!( message, exit_code=1 )
Exit with the specified exit_code
after printing the given message
.
def exit_now!( message, exit_code=1 )
raise GLI::CustomExit.new( message, exit_code )
end
Find the path to the user’s configured text editor and return it as a command Array (suitable for passing to Kernel.spawn). If no editor could be found, returns nil
.
def find_editor
unless @found_editor
raw_cmd =
Ravn::CLI.editor ||
Ravn::Subcommand.git_config_editor ||
EDITOR_ENV_VARS.map {|var| ENV[var] }.compact.first ||
FALLBACK_EDITOR_COMMAND
self.log.debug "Raw editor command is: %p" % [ raw_cmd ]
@found_editor = Shellwords.split( raw_cmd )
end
return @found_editor
end
Return the core.editor setting from the current environment’s git config.
def git_config_editor
results = self.read_output_of( ['git', 'config', 'core.editor'], ignore_errors: true ) or
return nil
return results.chomp
end
headline_string( string )
Return the specified string
in the ‘headline’ ANSI color.
def headline_string( string )
return hl.headline( string )
end
Exit with a helpful message
and display the usage.
def help_now!( message=nil )
exception = OptionParser::ParseError.new( message )
def exception.exit_code; 64; end
raise exception
end
highlight_string( string )
Return the specified string
in the ‘highlight’ ANSI color.
def highlight_string( string )
return hl.highlight( string )
end
Return the global Pastel object for convenient formatting, color, etc.
def hl
return Ravn::CLI.pastel
end
Get the prompt (a TTY::Prompt object)
def prompt
return Ravn::CLI.prompt
end
read_output_of( *command )
Execute the specified command
and return what it wrote to STDOUT.
def read_output_of( *command )
command.flatten!( 1 )
options = command.pop if command.last.is_a?( Hash )
options ||= {}
r, w = IO.pipe
reader = Thread.new { r.read }
options.merge!( out: w, r => :close )
execute( command, options ) or return nil
w.close
return reader.value
end
Use this if the following command should not have the around block executed. By default, the around block is executed, but for commands that might not want the setup to happen, this can be handy
def skips_around
@skips_around = true
end
Use this if the following command should not have the post block executed. By default, the post block is executed after each command. Using this will avoid that behavior for the following command
def skips_post
@skips_post = true
end
Use this if the following command should not have the pre block executed. By default, the pre block is executed before each command and can result in aborting the call. Using this will avoid that behavior for the following command
def skips_pre
@skips_pre = true
end
Return the specified string
in the ‘success’ ANSI color.
def success_string( string )
return hl.success( string )
end
Return the count of visible (i.e., non-control) characters in the given string
.
def visible_chars( string )
return string.to_s.gsub(/\e\[.*?m/, '').scan( /\P{Cntrl}/ ).size
end