Class: Toys::Runner
- Inherits:
-
Object
- Object
- Toys::Runner
- Defined in:
- lib/toys/runner.rb
Overview
An object that runs tools.
A Runner holds the environment in which tools run. This includes the Loader that resolves a tool name to a tool definition, along with run policy such as how to obtain a logger for a tool and what to do with an error the tool did not handle. This environment is fixed when the Runner is constructed.
Everything specific to a single invocation, such as which tool to run and
the arguments to pass to it, is passed to #run. A Runner is thus itself
immutable and holds no per-run state, so one Runner can run any number of
tools and is safe to share. This says nothing about the tools it runs,
however; running two tools at the same time is safe only if the tools
themselves are, since a tool can modify global state such as the load path
or a shared logger. See the logger parameter of CLI#initialize
for one such caveat.
The Runner that is running a tool is available to that tool as Context#runner, so a tool can use it to run a sibling tool in the same process. For example:
# My .toys.rb
tool "foo" do
def run
puts "in foo"
end
end
tool "bar" do
def run
puts "in bar"
runner.run(["foo"])
end
end
Constant Summary collapse
- DEFAULT_LOGGER_FACTORY =
The singleton default logger_factory Proc, which simply returns a new logger writing to the current stderr.
proc { |_tool| logger = ::Logger.new($stderr) logger.level = ::Logger::WARN logger }.freeze
- DEFAULT_ERROR_HANDLER =
The singleton default error_handler Proc, which simply reraises the error it is given. A ContextualError is reraised as itself, so that a rescue block has access to the context information. An unhandled
SignalException(or a subclass such asInterrupt) is also reraised as itself, so that the Ruby VM has a chance to handle it normally. proc { |error| raise(error) }.freeze
Instance Method Summary collapse
-
#initialize(loader, logger_factory: nil, base_level: nil, error_handler: nil, executable_name: nil, external_data: {}) ⇒ Runner
constructor
Create a Runner.
-
#run(args, verbosity: 0, wrap_errors: true, handle_errors: true) {|context| ... } ⇒ Integer
Run a tool.
Constructor Details
#initialize(loader, logger_factory: nil, base_level: nil, error_handler: nil, executable_name: nil, external_data: {}) ⇒ Runner
Create a Runner.
This performs no I/O and raises nothing. Tools are looked up, loaded, and run only when #run is called.
104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/toys/runner.rb', line 104 def initialize(loader, logger_factory: nil, base_level: nil, error_handler: nil, executable_name: nil, external_data: {}) @loader = loader @logger_factory = logger_factory || DEFAULT_LOGGER_FACTORY @base_level = base_level @error_handler = error_handler || DEFAULT_ERROR_HANDLER @executable_name = executable_name || ::File.basename($PROGRAM_NAME) @external_data = external_data end |
Instance Method Details
#run(args, verbosity: 0, wrap_errors: true, handle_errors: true) {|context| ... } ⇒ Integer
Run a tool.
The tool is looked up by matching a tool name at the beginning of the given arguments. The remaining arguments are then parsed into a Context, the tool's middleware is applied, and the tool is run.
If a block is passed, the runtime context is simply yielded to it in place of the tool's run handler, with the tool's middleware still applied. This is useful for testing parts of the tool runtime in isolation.
If the tool raises a SignalException that it does not handle itself,
that exception propagates unwrapped, even when wrap_errors is enabled.
This lets each tool in a nested execution dispatch it to its own
on_interrupt or on_signal handler, and lets the error handler, and
ultimately the Ruby VM, recognize it as a signal.
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/toys/runner.rb', line 160 def run(args, verbosity: 0, wrap_errors: true, handle_errors: true, &block) unless args.is_a?(::Array) raise ::ArgumentError, "Tool arguments must be an array of strings: #{args.inspect}" end Invocation.new(runner: self, args: args, verbosity: verbosity, wrap_errors: wrap_errors, handle_errors: handle_errors, delegated_from: nil, block: block).run end |