Class: Toys::CLI
- Inherits:
-
Object
- Object
- Toys::CLI
- Defined in:
- lib/toys/cli.rb
Overview
A Toys-based CLI.
Use this class to implement a CLI using Toys.
Defined Under Namespace
Classes: DefaultErrorHandler
Instance Attribute Summary collapse
-
#base_level ⇒ Integer
readonly
Return the initial logger level in this CLI, used as the level for verbosity 0.
-
#binary_name ⇒ String
readonly
Return the effective binary name used for usage text in this CLI.
-
#loader ⇒ Toys::Loader
readonly
Return the current loader for this CLI.
-
#logger ⇒ Logger
readonly
Return the logger used by this CLI.
Class Method Summary collapse
-
.default_logger(stream = $stderr) ⇒ Logger
Returns a default logger that logs to
$stderr. -
.default_middleware_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default ModuleLookup for middleware that points at the StandardMiddleware module.
-
.default_middleware_stack ⇒ Array
Returns a default set of middleware that may be used as a starting point for a typical CLI.
-
.default_mixin_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default ModuleLookup for mixins that points at the StandardMixins module.
-
.default_template_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default empty ModuleLookup for templates.
Instance Method Summary collapse
-
#add_config_block(high_priority: false, path: nil, &block) ⇒ Object
Add a configuration block to the loader.
-
#add_config_path(path, high_priority: false) ⇒ Object
Add a configuration file or directory to the loader.
-
#add_search_path(search_path, high_priority: false) ⇒ Object
Searches the given directory for a well-known config directory and/or config file.
-
#add_search_path_hierarchy(start: nil, terminate: [], high_priority: false) ⇒ Object
A convenience method that searches the current working directory, and all ancestor directories, for configs to add to the loader.
-
#child(_opts = {}) {|cli| ... } ⇒ Toys::CLI
Make a clone with the same settings but no paths in the loader.
-
#initialize(binary_name: nil, middleware_stack: nil, config_dir_name: nil, config_file_name: nil, index_file_name: nil, mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, logger: nil, base_level: nil, error_handler: nil) ⇒ CLI
constructor
Create a CLI.
-
#run(*args, verbosity: 0) ⇒ Integer
Run the CLI with the given command line arguments.
Constructor Details
#initialize(binary_name: nil, middleware_stack: nil, config_dir_name: nil, config_file_name: nil, index_file_name: nil, mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, logger: nil, base_level: nil, error_handler: nil) ⇒ CLI
Create a CLI.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/toys/cli.rb', line 83 def initialize( binary_name: nil, middleware_stack: nil, config_dir_name: nil, config_file_name: nil, index_file_name: nil, mixin_lookup: nil, middleware_lookup: nil, template_lookup: nil, logger: nil, base_level: nil, error_handler: nil ) @logger = logger || self.class.default_logger @base_level = base_level || @logger.level @middleware_stack = middleware_stack || self.class.default_middleware_stack @binary_name = binary_name || ::File.basename($PROGRAM_NAME) @config_dir_name = config_dir_name @config_file_name = config_file_name @index_file_name = index_file_name @mixin_lookup = mixin_lookup || self.class.default_mixin_lookup @middleware_lookup = middleware_lookup || self.class.default_middleware_lookup @template_lookup = template_lookup || self.class.default_template_lookup @loader = Loader.new( index_file_name: index_file_name, mixin_lookup: @mixin_lookup, template_lookup: @template_lookup, middleware_lookup: @middleware_lookup, middleware_stack: @middleware_stack ) @error_handler = error_handler || DefaultErrorHandler.new end |
Instance Attribute Details
#base_level ⇒ Integer (readonly)
Return the initial logger level in this CLI, used as the level for verbosity 0.
130 131 132 |
# File 'lib/toys/cli.rb', line 130 def base_level @base_level end |
#binary_name ⇒ String (readonly)
Return the effective binary name used for usage text in this CLI
117 118 119 |
# File 'lib/toys/cli.rb', line 117 def binary_name @binary_name end |
#loader ⇒ Toys::Loader (readonly)
Return the current loader for this CLI
111 112 113 |
# File 'lib/toys/cli.rb', line 111 def loader @loader end |
#logger ⇒ Logger (readonly)
Return the logger used by this CLI
123 124 125 |
# File 'lib/toys/cli.rb', line 123 def logger @logger end |
Class Method Details
.default_logger(stream = $stderr) ⇒ Logger
Returns a default logger that logs to $stderr.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/toys/cli.rb', line 379 def default_logger(stream = $stderr) logger = ::Logger.new(stream) terminal = Utils::Terminal.new(output: stream) logger.formatter = proc do |severity, time, _progname, msg| msg_str = case msg when ::String msg when ::Exception "#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n") else msg.inspect end format_log(terminal, time, severity, msg_str) end logger.level = ::Logger::WARN logger end |
.default_middleware_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default ModuleLookup for middleware that points at the StandardMiddleware module.
360 361 362 |
# File 'lib/toys/cli.rb', line 360 def default_middleware_lookup Utils::ModuleLookup.new.add_path("toys/standard_middleware") end |
.default_middleware_stack ⇒ Array
Returns a default set of middleware that may be used as a starting point for a typical CLI. This set includes the following in order:
- StandardMiddleware::SetDefaultDescriptions providing defaults for description fields
- StandardMiddleware::ShowHelp adding the
--helpflag - StandardMiddleware::HandleUsageErrors
- StandardMiddleware::ShowHelp providing default behavior for namespaces
- StandardMiddleware::AddVerbosityFlags adding the
--verboseand--quietflags for managing the logger level
334 335 336 337 338 339 340 341 342 |
# File 'lib/toys/cli.rb', line 334 def default_middleware_stack [ [:set_default_descriptions], [:show_help, help_flags: true], [:handle_usage_errors], [:show_help, fallback_execution: true], [:add_verbosity_flags] ] end |
.default_mixin_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default ModuleLookup for mixins that points at the StandardMixins module.
350 351 352 |
# File 'lib/toys/cli.rb', line 350 def default_mixin_lookup Utils::ModuleLookup.new.add_path("toys/standard_mixins") end |
.default_template_lookup ⇒ Toys::Utils::ModuleLookup
Returns a default empty ModuleLookup for templates.
369 370 371 |
# File 'lib/toys/cli.rb', line 369 def default_template_lookup Utils::ModuleLookup.new end |
Instance Method Details
#add_config_block(high_priority: false, path: nil, &block) ⇒ Object
Add a configuration block to the loader.
157 158 159 160 |
# File 'lib/toys/cli.rb', line 157 def add_config_block(high_priority: false, path: nil, &block) @loader.add_block(high_priority: high_priority, path: path, &block) self end |
#add_config_path(path, high_priority: false) ⇒ Object
Add a configuration file or directory to the loader.
If a CLI has a default tool set, it might use this to point to the directory that defines those tools. For example, the default Toys CLI uses this to load the builtin tools from the "builtins" directory.
143 144 145 146 |
# File 'lib/toys/cli.rb', line 143 def add_config_path(path, high_priority: false) @loader.add_path(path, high_priority: high_priority) self end |
#add_search_path(search_path, high_priority: false) ⇒ Object
Searches the given directory for a well-known config directory and/or config file. If found, these are added to the loader.
Typically, a CLI will use this to find toys configs in the current working directory, the user's home directory, or some other well-known general configuration-oriented directory such as "/etc".
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/toys/cli.rb', line 174 def add_search_path(search_path, high_priority: false) paths = [] if @config_file_name file_path = ::File.join(search_path, @config_file_name) paths << file_path if !::File.directory?(file_path) && ::File.readable?(file_path) end if @config_dir_name dir_path = ::File.join(search_path, @config_dir_name) paths << dir_path if ::File.directory?(dir_path) && ::File.readable?(dir_path) end @loader.add_path(paths, high_priority: high_priority) self end |
#add_search_path_hierarchy(start: nil, terminate: [], high_priority: false) ⇒ Object
A convenience method that searches the current working directory, and all ancestor directories, for configs to add to the loader.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/toys/cli.rb', line 199 def add_search_path_hierarchy(start: nil, terminate: [], high_priority: false) path = start || ::Dir.pwd paths = [] loop do break if terminate.include?(path) paths << path next_path = ::File.dirname(path) break if next_path == path path = next_path end paths.reverse! if high_priority paths.each do |p| add_search_path(p, high_priority: high_priority) end self end |
#child(_opts = {}) {|cli| ... } ⇒ Toys::CLI
Make a clone with the same settings but no paths in the loader. This is sometimes useful for running sub-tools.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/toys/cli.rb', line 249 def child(_opts = {}) cli = CLI.new(binary_name: @binary_name, config_dir_name: @config_dir_name, config_file_name: @config_file_name, index_file_name: @index_file_name, middleware_stack: @middleware_stack, mixin_lookup: @mixin_lookup, middleware_lookup: @middleware_lookup, template_lookup: @template_lookup, logger: @logger, base_level: @base_level, error_handler: @error_handler) yield cli if block_given? cli end |
#run(*args, verbosity: 0) ⇒ Integer
Run the CLI with the given command line arguments.
226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/toys/cli.rb', line 226 def run(*args, verbosity: 0) tool_definition, remaining = ContextualError.capture("Error finding tool definition") do @loader.lookup(args.flatten) end ContextualError.capture_path( "Error during tool execution!", tool_definition.source_path, tool_name: tool_definition.full_name, tool_args: remaining ) do Runner.new(self, tool_definition).run(remaining, verbosity: verbosity) end rescue ContextualError => e @error_handler.call(e) end |