class Locd::CLI::Command::Base
Abstract base for CLI
interface commands using the `thor` gem.
@abstract
@see whatisthor.com/
Protected Instance Methods
# File lib/locd/cli/command/base.rb, line 81 def agent_file agent agent.path.to_s.sub( /\A#{ Regexp.escape( ENV['HOME'] ) }/, '~' ) end
Swap `$stdout` for a {StringIO}, call `block`, swap original `$stdout` back in and return the string contents.
'Cause we got that damn threaded logging going on, we want to write output all as one string using `$stdout.write`, but some stuff like {Thor::Shell::Basic#print_table} just write to {#stdout} (which resolves to `$stdout`) and don't offer an option to returned a formatted string instead.
This seems like the simplest way to handle it, though it may still run into trouble with the threads, we shall seeā¦
@param [Proc] &block
Block to run that writes to `$stdout`
@return [String]
# File lib/locd/cli/command/base.rb, line 71 def capture_stdout &block io = StringIO.new stdout = $stdout $stdout = io block.call $stdout = stdout io.string end
# File lib/locd/cli/command/base.rb, line 269 def find_multi! pattern # Behavior depend on the `:all` option... if options[:all] # `:all` is set, so we find all the agents for the pattern, raising # if we don't find any Locd::Agent.find_all!( pattern, **option_kwds( groups: :pattern ) ).values else # `:all` is not set, so we need to find exactly one or error [find_only!( pattern )] end end
Find exactly one {Locd::Agent} for a `pattern`, using the any `:pattern` shared options provided, and raising if there are no matches or more than one.
@param pattern (see Locd::Agent.find_only!)
@return [Locd::Agent]
Matched agent.
@raise If more or less than one agent is matched.
# File lib/locd/cli/command/base.rb, line 264 def find_only! pattern Locd::Agent.find_only! pattern, **option_kwds( groups: :pattern ) end
Hook called from {Thor::Command#run} when an error occurs. Errors are not recoverable from my understanding, so this method should provide necessary feedback to the user and exit with an error code for errors it expects and re-raise those that it doesn't.
# File lib/locd/cli/command/base.rb, line 231 def on_run_error error, command, args case error when NRSER::CountError if error.count == 0 logger.error "No results" else logger.error "Too many results:\n\n#{ render_text error.value }\n" end # If the command supports `--all`, let them know that they can use it if command.options[:all] logger.info "You can use `--all` to to operate on ALL matches." logger.info "See `locd help #{ command.name }`\n" end exit 1 else raise error end end
# File lib/locd/cli/command/base.rb, line 170 def render_color src, lexer_name formatter = Rouge::Formatters::Terminal256.new lexer = Rouge::Lexers.const_get( lexer_name ).new formatter.format( lexer.lex( src ) ) end
# File lib/locd/cli/command/base.rb, line 161 def render_color? if options.key? :color options[:color] else $stdout.isatty end end
# File lib/locd/cli/command/base.rb, line 86 def render_table table string = capture_stdout do print_table table.to_a end width = string.lines.map( &:length ).max return ( string.lines[0] + "-" * width + "\n" + string.lines[1..-1].join + "---\n" + table.footer + "\n\n" ) string.lines.each_with_index.map { |line, index| if index == 0 '# ' + line else ' ' + line end }.join end
# File lib/locd/cli/command/base.rb, line 112 def render_text object # Does it look like an array/list? if NRSER.array_like? object # It does! Convert it to an actual array to make life easier array = object.to_a return "(EMPTY)" if array.empty? # Is it a list of agents? if array.all? { |entry| entry.is_a? Locd::Agent } # Ok, we want to display them. What mode are we in accoring to the # options? if options[:long] # We're in long-mode, render a table render_table agent_table( array ) else # We're in regular mode, render each agent on it's own line by # recurring array.map( &method( __method__ ) ).join( "\n" ) end # Is it a list of arrays? elsif array.all? { |entry| entry.is_a? Array } # It is, let's render that as a table render_table message else # It's not, let's just render each entry on it's own line by # recurring message.map( &method( __method__ ) ).join( "\n" ) end else # It's not array-ish. Special-case {Locd::Agent} instances and render # the rest as `#to_s` case object when Locd::Agent::Site # TODO Want to add options for this, but for now just render agent # URLs 'cause they have the label in them and are # `cmd+click`-able in iTerm2 to open, which is most useful object.url when Locd::Agent object.label else object.to_s end end end
# File lib/locd/cli/command/base.rb, line 177 def respond message, title: nil formatted = if options[:json] json = begin JSON.pretty_generate message rescue JSON::GeneratorError => error logger.debug "Error generating 'pretty' JSON, falling back to dump", error: error JSON.dump message end if render_color? render_color json, :JSON else json end elsif options[:yaml] || NRSER.hash_like?( message ) # TODO This sucks, but it's the easiest way to get "nice" YAML :/ yaml = YAML.dump JSON.load( JSON.dump message ) if render_color? # formatter = Rouge::Formatters::Terminal256.new # lexer = Rouge::Lexers::YAML.new # formatter.format( lexer.lex( yaml ) ) render_color yaml, :YAML else yaml end else render_text message end if title formatted = ( "# #{ title }\n#{ '#' * 78}\n#\n" + formatted ) end # Be puts-like unless formatted[-1] == "\n" formatted = formatted + "\n" end $stdout.write formatted end