toys-core 0.11.5 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +62 -0
- data/LICENSE.md +1 -1
- data/README.md +5 -2
- data/docs/guide.md +1 -1
- data/lib/toys/acceptor.rb +13 -4
- data/lib/toys/arg_parser.rb +7 -7
- data/lib/toys/cli.rb +170 -120
- data/lib/toys/compat.rb +71 -23
- data/lib/toys/completion.rb +18 -6
- data/lib/toys/context.rb +24 -15
- data/lib/toys/core.rb +6 -2
- data/lib/toys/dsl/base.rb +87 -0
- data/lib/toys/dsl/flag.rb +26 -20
- data/lib/toys/dsl/flag_group.rb +18 -14
- data/lib/toys/dsl/internal.rb +206 -0
- data/lib/toys/dsl/positional_arg.rb +26 -16
- data/lib/toys/dsl/tool.rb +180 -218
- data/lib/toys/errors.rb +64 -8
- data/lib/toys/flag.rb +662 -656
- data/lib/toys/flag_group.rb +24 -10
- data/lib/toys/input_file.rb +13 -7
- data/lib/toys/loader.rb +293 -140
- data/lib/toys/middleware.rb +46 -22
- data/lib/toys/mixin.rb +10 -8
- data/lib/toys/positional_arg.rb +21 -20
- data/lib/toys/settings.rb +914 -0
- data/lib/toys/source_info.rb +147 -35
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
- data/lib/toys/standard_middleware/apply_config.rb +6 -4
- data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
- data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
- data/lib/toys/standard_middleware/show_help.rb +19 -5
- data/lib/toys/standard_middleware/show_root_version.rb +2 -0
- data/lib/toys/standard_mixins/bundler.rb +24 -15
- data/lib/toys/standard_mixins/exec.rb +43 -34
- data/lib/toys/standard_mixins/fileutils.rb +3 -1
- data/lib/toys/standard_mixins/gems.rb +21 -17
- data/lib/toys/standard_mixins/git_cache.rb +46 -0
- data/lib/toys/standard_mixins/highline.rb +8 -8
- data/lib/toys/standard_mixins/terminal.rb +5 -5
- data/lib/toys/standard_mixins/xdg.rb +56 -0
- data/lib/toys/template.rb +11 -9
- data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
- data/lib/toys/utils/completion_engine.rb +7 -2
- data/lib/toys/utils/exec.rb +162 -132
- data/lib/toys/utils/gems.rb +85 -60
- data/lib/toys/utils/git_cache.rb +813 -0
- data/lib/toys/utils/help_text.rb +117 -37
- data/lib/toys/utils/terminal.rb +11 -3
- data/lib/toys/utils/xdg.rb +293 -0
- data/lib/toys/wrappable_string.rb +9 -2
- data/lib/toys-core.rb +18 -6
- metadata +14 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68a552244c25e03216c7bf958a7ccd2a35bb7fb3795a02e77c4a839b7720e258
|
4
|
+
data.tar.gz: 58513ce7d5d60646a1b9e51235d04df470ee5d9317a9a72048a8e95872227da0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 299c193de07d0802046029c7b305a1e4cecac715eee32d259a917b6ecc2505975ba10a2aca90b510036076d9f32b58849a02381ae61837336eee5361cd2126ea
|
7
|
+
data.tar.gz: b42a4d81db4a71a136deafd4a62f0e7087325097a1c3e7473a5b7edda699b08ee1c8d81377765a70d8a18fd0a2693874781418f9953ca52f92cced670bec1421
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,67 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### v0.13.0 / 2022-02-08
|
4
|
+
|
5
|
+
Toys-Core 0.13.0 is a major release with significant improvements to the git cache, along with compatibility improvements and bug fixes.
|
6
|
+
|
7
|
+
New functionality:
|
8
|
+
|
9
|
+
* The `load_git` directive and the underlying `Toys::Utils::GitCache` class now support updating from git based on cache age.
|
10
|
+
* The `Toys::Utils::GitCache` class supports copying git content into a provided directory, querying repo information, and deleting cache data.
|
11
|
+
* The `Toys::Utils::GitCache` class makes files read-only, to help prevent clients from interfering with one another.
|
12
|
+
* The `:terminal` mixin and the underlying `Toys::Utils::Terminal` class now honor the `NO_COLOR` environment variable.
|
13
|
+
* Added `Toys::CLI#load_tool`, which is useful for testing tools.
|
14
|
+
|
15
|
+
Fixes and compatibility updates:
|
16
|
+
|
17
|
+
* Bundler install/updates are now spawned in subprocesses for compatibility with bundler 2.3. The bundler integration also now requires bundler 2.2 or later.
|
18
|
+
* The `exec_tool` and `exec_proc` methods in the `:exec` mixin now log their execution in the same way as other exec functions.
|
19
|
+
* Minor compatibility fixes to provide partial support for TruffleRuby.
|
20
|
+
|
21
|
+
Other notes:
|
22
|
+
|
23
|
+
* The internal GitCache representation has changed significantly to support additional features and improve robustness and performance. This will force existing caches to update, but should not break existing usage.
|
24
|
+
|
25
|
+
### v0.12.2 / 2021-08-30
|
26
|
+
|
27
|
+
* FIXED: Tool context inspect string is no longer overwhelmingly long
|
28
|
+
* FIXED: Fixed an exception in GitCache when updating a changed ref
|
29
|
+
|
30
|
+
### v0.12.1 / 2021-08-17
|
31
|
+
|
32
|
+
* FIXED: Fixed a regression in 0.12.0 where bundler could use the wrong Gemfile if you set a custom context directory
|
33
|
+
|
34
|
+
### v0.12.0 / 2021-08-05
|
35
|
+
|
36
|
+
Toys-Core 0.12.0 is a major release with significant new features and bug fixes, and a few breaking interface changes. Additionally, this release now requires Ruby 2.4 or later.
|
37
|
+
|
38
|
+
Breaking interface changes:
|
39
|
+
|
40
|
+
* The Toys::Tool class has been renamed Toys::ToolDefinition so that the old name can be used for class-based tool definition.
|
41
|
+
* Tool definition now raises ToolDefinitionError if whitespace, control characters, or certain punctuation are used in a tool name.
|
42
|
+
* Toys::Loader#add_path no longer supports multiple paths. Use add_path_set instead.
|
43
|
+
* The "name" argument was renamed to "source_name" in Toys::Loader#add_block and Toys::CLI#add_config_block
|
44
|
+
|
45
|
+
New functionality:
|
46
|
+
|
47
|
+
* The DSL now supports a class-based tool definition syntax (in addition to the existing block-based syntax). Some users may prefer this new class-based style as more Ruby-like.
|
48
|
+
* You can now load tools from a remote git repository using the load_git directive.
|
49
|
+
* Whitespace is now automatically considered a name delimiter when defining tools.
|
50
|
+
* There is now an extensible settings mechanism to activate less-common tool behavior. Currently there is one setting, which causes subtools to inherit their parent's methods by default.
|
51
|
+
* The load directive can load into a new tool.
|
52
|
+
* Added a new utility class and mixin that provides XDG Base Directory information.
|
53
|
+
* Added a new utility class and mixin that provides cached access to remote git repos.
|
54
|
+
* The help text generator now supports splitting the subtool list by source.
|
55
|
+
* Loader and CLI methods that add tool configs now uniformly provide optional source_name and context_directory arguments.
|
56
|
+
* Toys::SourceInfo now supports getting the root ancestor and priority of a source.
|
57
|
+
* Toys::ToolDefinition now has a direct accessor for the source root. This is always set for a tool, even if it isn't marked as finished.
|
58
|
+
|
59
|
+
Fixes:
|
60
|
+
|
61
|
+
* Fixed some bundler integration issues that occurred when the bundle is being installed in a separate path such as a vendor directory.
|
62
|
+
* Toys::ContextualError now includes the full backtrace of the cause.
|
63
|
+
* Cleaned up some unused memory objects during tool loading and lookup.
|
64
|
+
|
3
65
|
### v0.11.5 / 2021-03-28
|
4
66
|
|
5
67
|
* BREAKING CHANGE: The exit_on_nonzero_status option to exec now exits on signals and failures to spawn, in addition to error codes.
|
data/LICENSE.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# License
|
2
2
|
|
3
|
-
Copyright 2019-
|
3
|
+
Copyright 2019-2022 Daniel Azuma and the Toys contributors
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -34,11 +34,14 @@ Install the **toys-core** gem using:
|
|
34
34
|
You may also install the **toys** gem, which brings in **toys-core** as a
|
35
35
|
dependency.
|
36
36
|
|
37
|
-
Toys-Core requires Ruby 2.
|
37
|
+
Toys-Core requires Ruby 2.4 or later.
|
38
38
|
|
39
39
|
Most parts of Toys-Core work on JRuby. However, JRuby is not recommended
|
40
40
|
because of JVM boot latency, lack of support for Kernel#fork, and other issues.
|
41
41
|
|
42
|
+
Most parts of Toys-Core work on TruffleRuby. However, TruffleRuby is not
|
43
|
+
recommended because it has a few known bugs that affect Toys.
|
44
|
+
|
42
45
|
### Create a new executable
|
43
46
|
|
44
47
|
We'll start by creating an executable Ruby script. Using your favorite text
|
@@ -338,7 +341,7 @@ templates, and middleware, in the
|
|
338
341
|
|
339
342
|
## License
|
340
343
|
|
341
|
-
Copyright 2019-
|
344
|
+
Copyright 2019-2022 Daniel Azuma and the Toys contributors
|
342
345
|
|
343
346
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
344
347
|
of this software and associated documentation files (the "Software"), to deal
|
data/docs/guide.md
CHANGED
@@ -31,7 +31,7 @@ sophisticated command line tools.
|
|
31
31
|
|
32
32
|
Toys-Core is a command line *framework* in the traditional sense. It is
|
33
33
|
intended to be used to write custom command line executables in Ruby. The
|
34
|
-
framework provides common facilities such as
|
34
|
+
framework provides common facilities such as argument parsing and online help,
|
35
35
|
while your executable chooses and configures those facilities, and implements
|
36
36
|
the actual behavior.
|
37
37
|
|
data/lib/toys/acceptor.rb
CHANGED
@@ -178,6 +178,7 @@ module Toys
|
|
178
178
|
|
179
179
|
##
|
180
180
|
# Overrides {Toys::Acceptor::Base#match} to use the given function.
|
181
|
+
#
|
181
182
|
# @private
|
182
183
|
#
|
183
184
|
def match(str)
|
@@ -188,6 +189,7 @@ module Toys
|
|
188
189
|
##
|
189
190
|
# Overrides {Toys::Acceptor::Base#convert} to use the given function's
|
190
191
|
# result.
|
192
|
+
#
|
191
193
|
# @private
|
192
194
|
#
|
193
195
|
def convert(_str, result)
|
@@ -234,6 +236,7 @@ module Toys
|
|
234
236
|
|
235
237
|
##
|
236
238
|
# Overrides {Toys::Acceptor::Base#match} to use the given regex.
|
239
|
+
#
|
237
240
|
# @private
|
238
241
|
#
|
239
242
|
def match(str)
|
@@ -242,6 +245,7 @@ module Toys
|
|
242
245
|
|
243
246
|
##
|
244
247
|
# Overrides {Toys::Acceptor::Base#convert} to use the given converter.
|
248
|
+
#
|
245
249
|
# @private
|
246
250
|
#
|
247
251
|
def convert(str, *extra)
|
@@ -285,6 +289,7 @@ module Toys
|
|
285
289
|
|
286
290
|
##
|
287
291
|
# Overrides {Toys::Acceptor::Base#match} to find the value.
|
292
|
+
#
|
288
293
|
# @private
|
289
294
|
#
|
290
295
|
def match(str)
|
@@ -294,6 +299,7 @@ module Toys
|
|
294
299
|
##
|
295
300
|
# Overrides {Toys::Acceptor::Base#convert} to return the actual enum
|
296
301
|
# element.
|
302
|
+
#
|
297
303
|
# @private
|
298
304
|
#
|
299
305
|
def convert(_str, elem)
|
@@ -303,6 +309,7 @@ module Toys
|
|
303
309
|
##
|
304
310
|
# Overrides {Toys::Acceptor::Base#suggestions} to return close matches
|
305
311
|
# from the enum.
|
312
|
+
#
|
306
313
|
# @private
|
307
314
|
#
|
308
315
|
def suggestions(str)
|
@@ -513,7 +520,9 @@ module Toys
|
|
513
520
|
internal_create(spec, options, block)
|
514
521
|
end
|
515
522
|
|
516
|
-
##
|
523
|
+
##
|
524
|
+
# @private
|
525
|
+
#
|
517
526
|
def scalarize_spec(spec, options, block)
|
518
527
|
spec ||= block
|
519
528
|
if options.empty?
|
@@ -602,11 +611,11 @@ module Toys
|
|
602
611
|
Simple.new(type_desc: "boolean", well_known_spec: spec) do |s|
|
603
612
|
if s.nil?
|
604
613
|
default
|
614
|
+
elsif s.empty?
|
615
|
+
REJECT
|
605
616
|
else
|
606
617
|
s = s.downcase
|
607
|
-
if
|
608
|
-
REJECT
|
609
|
-
elsif TRUE_STRINGS.any? { |t| t.start_with?(s) }
|
618
|
+
if TRUE_STRINGS.any? { |t| t.start_with?(s) }
|
610
619
|
true
|
611
620
|
elsif FALSE_STRINGS.any? { |f| f.start_with?(s) }
|
612
621
|
false
|
data/lib/toys/arg_parser.rb
CHANGED
@@ -261,7 +261,7 @@ module Toys
|
|
261
261
|
# @param message [String] The message. Required.
|
262
262
|
#
|
263
263
|
def initialize(message)
|
264
|
-
super(message)
|
264
|
+
super(message, name: nil)
|
265
265
|
end
|
266
266
|
end
|
267
267
|
|
@@ -269,7 +269,7 @@ module Toys
|
|
269
269
|
# Create an argument parser for a particular tool.
|
270
270
|
#
|
271
271
|
# @param cli [Toys::CLI] The CLI in effect.
|
272
|
-
# @param tool [Toys::
|
272
|
+
# @param tool [Toys::ToolDefinition] The tool defining the argument format.
|
273
273
|
# @param default_data [Hash] Additional initial data (such as verbosity).
|
274
274
|
# @param require_exact_flag_match [Boolean] Whether to require flag matches
|
275
275
|
# be exact (not partial). Default is false.
|
@@ -295,7 +295,7 @@ module Toys
|
|
295
295
|
|
296
296
|
##
|
297
297
|
# The tool definition governing this parser.
|
298
|
-
# @return [Toys::
|
298
|
+
# @return [Toys::ToolDefinition]
|
299
299
|
#
|
300
300
|
attr_reader :tool
|
301
301
|
|
@@ -417,6 +417,7 @@ module Toys
|
|
417
417
|
|
418
418
|
REMAINING_HANDLER = ->(val, prev) { prev.is_a?(::Array) ? prev << val : [val] }
|
419
419
|
ARG_HANDLER = ->(val, _prev) { val }
|
420
|
+
private_constant :REMAINING_HANDLER, :ARG_HANDLER
|
420
421
|
|
421
422
|
def initial_data(cli, tool, default_data)
|
422
423
|
data = {
|
@@ -429,7 +430,7 @@ module Toys
|
|
429
430
|
Context::Key::TOOL_NAME => tool.full_name,
|
430
431
|
Context::Key::USAGE_ERRORS => [],
|
431
432
|
}
|
432
|
-
|
433
|
+
tool.default_data.each { |k, v| data[k] = v.clone }
|
433
434
|
default_data.each { |k, v| data[k] ||= v }
|
434
435
|
data
|
435
436
|
end
|
@@ -450,7 +451,7 @@ module Toys
|
|
450
451
|
case arg
|
451
452
|
when "--"
|
452
453
|
@flags_allowed = false
|
453
|
-
when /\A(--\w[
|
454
|
+
when /\A(--\w[?\w-]*)=(.*)\z/
|
454
455
|
handle_valued_flag(::Regexp.last_match(1), ::Regexp.last_match(2))
|
455
456
|
when /\A--.+\z/
|
456
457
|
handle_plain_flag(arg)
|
@@ -474,8 +475,7 @@ module Toys
|
|
474
475
|
return "" unless flag_def
|
475
476
|
@seen_flag_keys << flag_def.key
|
476
477
|
if flag_def.flag_type == :boolean
|
477
|
-
add_data(flag_def.key, flag_def.handler, nil, !flag_result.unique_flag_negative?,
|
478
|
-
:flag, name)
|
478
|
+
add_data(flag_def.key, flag_def.handler, nil, !flag_result.unique_flag_negative?, :flag, name)
|
479
479
|
elsif following.empty?
|
480
480
|
if flag_def.value_type == :required || flag_result.unique_flag_syntax.value_delim == " "
|
481
481
|
@active_flag_def = flag_def
|
data/lib/toys/cli.rb
CHANGED
@@ -78,8 +78,8 @@ module Toys
|
|
78
78
|
# with different verbosity settings (since the logger cannot have
|
79
79
|
# multiple level settings simultaneously). In that case, do not set a
|
80
80
|
# global logger, but use the `logger_factory` parameter instead.
|
81
|
-
# @param logger_factory [Proc] A proc that takes a {Toys::
|
82
|
-
# argument, and returns a `Logger` to use when running that tool.
|
81
|
+
# @param logger_factory [Proc] A proc that takes a {Toys::ToolDefinition}
|
82
|
+
# as an argument, and returns a `Logger` to use when running that tool.
|
83
83
|
# Optional. If not provided (and no global logger is set), CLI will use
|
84
84
|
# a default factory that writes generates loggers writing formatted
|
85
85
|
# output to `STDERR`, as defined by {Toys::CLI.default_logger_factory}.
|
@@ -175,26 +175,24 @@ module Toys
|
|
175
175
|
# Optional. If not provided, lib directories are disabled.
|
176
176
|
# Note: the standard toys executable sets this to `".lib"`.
|
177
177
|
#
|
178
|
-
def initialize( # rubocop:disable Metrics/MethodLength
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
completion: nil
|
197
|
-
)
|
178
|
+
def initialize(executable_name: nil, # rubocop:disable Metrics/MethodLength
|
179
|
+
middleware_stack: nil,
|
180
|
+
extra_delimiters: "",
|
181
|
+
config_dir_name: nil,
|
182
|
+
config_file_name: nil,
|
183
|
+
index_file_name: nil,
|
184
|
+
preload_file_name: nil,
|
185
|
+
preload_dir_name: nil,
|
186
|
+
data_dir_name: nil,
|
187
|
+
lib_dir_name: nil,
|
188
|
+
mixin_lookup: nil,
|
189
|
+
middleware_lookup: nil,
|
190
|
+
template_lookup: nil,
|
191
|
+
logger_factory: nil,
|
192
|
+
logger: nil,
|
193
|
+
base_level: nil,
|
194
|
+
error_handler: nil,
|
195
|
+
completion: nil)
|
198
196
|
@executable_name = executable_name || ::File.basename($PROGRAM_NAME)
|
199
197
|
@middleware_stack = middleware_stack || CLI.default_middleware_stack
|
200
198
|
@mixin_lookup = mixin_lookup || CLI.default_mixin_lookup
|
@@ -321,10 +319,22 @@ module Toys
|
|
321
319
|
# a Toys directory.
|
322
320
|
# @param high_priority [Boolean] Add the config at the head of the priority
|
323
321
|
# list rather than the tail.
|
322
|
+
# @param source_name [String] A custom name for the root source. Optional.
|
323
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
324
|
+
# for tools loaded from this path. You can pass a directory path as a
|
325
|
+
# string, `:path` to denote the given path, `:parent` to denote the
|
326
|
+
# given path's parent directory, or `nil` to denote no context.
|
327
|
+
# Defaults to `:parent`.
|
324
328
|
# @return [self]
|
325
329
|
#
|
326
|
-
def add_config_path(path,
|
327
|
-
|
330
|
+
def add_config_path(path,
|
331
|
+
high_priority: false,
|
332
|
+
source_name: nil,
|
333
|
+
context_directory: :parent)
|
334
|
+
@loader.add_path(path,
|
335
|
+
high_priority: high_priority,
|
336
|
+
source_name: source_name,
|
337
|
+
context_directory: context_directory)
|
328
338
|
self
|
329
339
|
end
|
330
340
|
|
@@ -336,15 +346,24 @@ module Toys
|
|
336
346
|
#
|
337
347
|
# @param high_priority [Boolean] Add the config at the head of the priority
|
338
348
|
# list rather than the tail.
|
339
|
-
# @param
|
340
|
-
# for tools defined in this block. If omitted, a default
|
341
|
-
# will be generated.
|
349
|
+
# @param source_name [String] The source name that will be shown in
|
350
|
+
# documentation for tools defined in this block. If omitted, a default
|
351
|
+
# unique string will be generated.
|
342
352
|
# @param block [Proc] The block of configuration, executed in the context
|
343
353
|
# of the tool DSL {Toys::DSL::Tool}.
|
354
|
+
# @param context_directory [String,nil] The context directory for tools
|
355
|
+
# loaded from this block. You can pass a directory path as a string, or
|
356
|
+
# `nil` to denote no context. Defaults to `nil`.
|
344
357
|
# @return [self]
|
345
358
|
#
|
346
|
-
def add_config_block(high_priority: false,
|
347
|
-
|
359
|
+
def add_config_block(high_priority: false,
|
360
|
+
source_name: nil,
|
361
|
+
context_directory: nil,
|
362
|
+
&block)
|
363
|
+
@loader.add_block(high_priority: high_priority,
|
364
|
+
source_name: source_name,
|
365
|
+
context_directory: context_directory,
|
366
|
+
&block)
|
348
367
|
self
|
349
368
|
end
|
350
369
|
|
@@ -358,19 +377,28 @@ module Toys
|
|
358
377
|
# @param search_path [String] A path to search for configs.
|
359
378
|
# @param high_priority [Boolean] Add the configs at the head of the
|
360
379
|
# priority list rather than the tail.
|
380
|
+
# @param context_directory [String,nil,:path,:parent] The context directory
|
381
|
+
# for tools loaded from this path. You can pass a directory path as a
|
382
|
+
# string, `:path` to denote the given path, `:parent` to denote the
|
383
|
+
# given path's parent directory, or `nil` to denote no context.
|
384
|
+
# Defaults to `:path`.
|
361
385
|
# @return [self]
|
362
386
|
#
|
363
|
-
def add_search_path(search_path,
|
387
|
+
def add_search_path(search_path,
|
388
|
+
high_priority: false,
|
389
|
+
context_directory: :path)
|
364
390
|
paths = []
|
365
391
|
if @config_file_name
|
366
392
|
file_path = ::File.join(search_path, @config_file_name)
|
367
|
-
paths <<
|
393
|
+
paths << @config_file_name if !::File.directory?(file_path) && ::File.readable?(file_path)
|
368
394
|
end
|
369
395
|
if @config_dir_name
|
370
396
|
dir_path = ::File.join(search_path, @config_dir_name)
|
371
|
-
paths <<
|
397
|
+
paths << @config_dir_name if ::File.directory?(dir_path) && ::File.readable?(dir_path)
|
372
398
|
end
|
373
|
-
@loader.
|
399
|
+
@loader.add_path_set(search_path, paths,
|
400
|
+
high_priority: high_priority,
|
401
|
+
context_directory: context_directory)
|
374
402
|
self
|
375
403
|
end
|
376
404
|
|
@@ -416,6 +444,9 @@ module Toys
|
|
416
444
|
# run and what arguments to pass to it. You may pass either a single
|
417
445
|
# array of strings, or a series of string arguments.
|
418
446
|
# @param verbosity [Integer] Initial verbosity. Default is 0.
|
447
|
+
# @param delegated_from [Toys::Context] The context from which this
|
448
|
+
# execution is delegated. Optional. Should be set only if this is a
|
449
|
+
# delegated execution.
|
419
450
|
#
|
420
451
|
# @return [Integer] The resulting process status code (i.e. 0 for success).
|
421
452
|
#
|
@@ -427,101 +458,32 @@ module Toys
|
|
427
458
|
"Error during tool execution!", tool.source_info&.source_path,
|
428
459
|
tool_name: tool.full_name, tool_args: remaining
|
429
460
|
) do
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
run_tool(tool, remaining, default_data)
|
461
|
+
context = build_context(tool, remaining,
|
462
|
+
verbosity: verbosity,
|
463
|
+
delegated_from: delegated_from)
|
464
|
+
execute_tool(tool, context, &:run)
|
435
465
|
end
|
436
466
|
rescue ContextualError, ::Interrupt => e
|
437
467
|
@error_handler.call(e).to_i
|
438
468
|
end
|
439
469
|
|
440
|
-
private
|
441
|
-
|
442
470
|
##
|
443
|
-
#
|
444
|
-
#
|
471
|
+
# Prepare a tool to be run, but just execute the given block rather than
|
472
|
+
# performing a full run of the tool. This is intended for testing tools.
|
473
|
+
# Unlike {#run}, this does not catch errors and perform error handling.
|
445
474
|
#
|
446
|
-
# @param
|
447
|
-
#
|
448
|
-
#
|
449
|
-
# @
|
475
|
+
# @param args [String...] Command line arguments specifying which tool to
|
476
|
+
# run and what arguments to pass to it. You may pass either a single
|
477
|
+
# array of strings, or a series of string arguments.
|
478
|
+
# @yieldparam context [Toys::Context] Yields the tool context.
|
450
479
|
#
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
context
|
457
|
-
|
458
|
-
tool.run_initializers(context)
|
459
|
-
|
460
|
-
cur_logger = context[Context::Key::LOGGER]
|
461
|
-
if cur_logger
|
462
|
-
original_level = cur_logger.level
|
463
|
-
cur_logger.level = (base_level || original_level) - context[Context::Key::VERBOSITY].to_i
|
464
|
-
end
|
465
|
-
begin
|
466
|
-
execute_tool_in_context(context, tool)
|
467
|
-
ensure
|
468
|
-
cur_logger.level = original_level if cur_logger
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def execute_tool_in_context(context, tool)
|
473
|
-
executor = proc do
|
474
|
-
begin
|
475
|
-
if !context[Context::Key::USAGE_ERRORS].empty?
|
476
|
-
handle_usage_errors(context, tool)
|
477
|
-
elsif !tool.runnable?
|
478
|
-
raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
|
479
|
-
else
|
480
|
-
context.run
|
481
|
-
end
|
482
|
-
rescue ::Interrupt => e
|
483
|
-
raise e unless tool.handles_interrupts?
|
484
|
-
handle_interrupt(context, tool.interrupt_handler, e)
|
485
|
-
end
|
486
|
-
end
|
487
|
-
tool.built_middleware.reverse_each do |middleware|
|
488
|
-
executor = make_executor(middleware, context, executor)
|
489
|
-
end
|
490
|
-
catch(:result) do
|
491
|
-
executor.call
|
492
|
-
0
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
def handle_usage_errors(context, tool)
|
497
|
-
usage_errors = context[Context::Key::USAGE_ERRORS]
|
498
|
-
handler = tool.usage_error_handler
|
499
|
-
raise ArgParsingError, usage_errors if handler.nil?
|
500
|
-
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
501
|
-
if handler.arity.zero?
|
502
|
-
context.instance_exec(&handler)
|
503
|
-
else
|
504
|
-
context.instance_exec(usage_errors, &handler)
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
def handle_interrupt(context, handler, exception)
|
509
|
-
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
510
|
-
if handler.arity.zero?
|
511
|
-
context.instance_exec(&handler)
|
512
|
-
else
|
513
|
-
context.instance_exec(exception, &handler)
|
514
|
-
end
|
515
|
-
rescue ::Interrupt => e
|
516
|
-
raise e if e.equal?(exception)
|
517
|
-
handle_interrupt(context, handler, e)
|
518
|
-
end
|
519
|
-
|
520
|
-
def make_executor(middleware, context, next_executor)
|
521
|
-
if middleware.respond_to?(:run)
|
522
|
-
proc { middleware.run(context, &next_executor) }
|
523
|
-
else
|
524
|
-
next_executor
|
480
|
+
# @return [Object] The value returned from the block.
|
481
|
+
#
|
482
|
+
def load_tool(*args)
|
483
|
+
tool, remaining = @loader.lookup(args.flatten)
|
484
|
+
context = build_context(tool, remaining)
|
485
|
+
execute_tool(tool, context) do |ctx|
|
486
|
+
ctx.exit(yield ctx)
|
525
487
|
end
|
526
488
|
end
|
527
489
|
|
@@ -727,5 +689,93 @@ module Toys
|
|
727
689
|
"#{styled_header} #{msg}\n"
|
728
690
|
end
|
729
691
|
end
|
692
|
+
|
693
|
+
private
|
694
|
+
|
695
|
+
def build_context(tool, args, verbosity: 0, delegated_from: nil)
|
696
|
+
default_data = {
|
697
|
+
Context::Key::VERBOSITY => verbosity,
|
698
|
+
Context::Key::DELEGATED_FROM => delegated_from,
|
699
|
+
}
|
700
|
+
arg_parser = ArgParser.new(self, tool,
|
701
|
+
default_data: default_data,
|
702
|
+
require_exact_flag_match: tool.exact_flag_match_required?)
|
703
|
+
arg_parser.parse(args).finish
|
704
|
+
tool.tool_class.new(arg_parser.data)
|
705
|
+
end
|
706
|
+
|
707
|
+
def execute_tool(tool, context)
|
708
|
+
tool.source_info&.apply_lib_paths
|
709
|
+
tool.run_initializers(context)
|
710
|
+
cur_logger = context[Context::Key::LOGGER]
|
711
|
+
if cur_logger
|
712
|
+
original_level = cur_logger.level
|
713
|
+
cur_logger.level = (base_level || original_level) - context[Context::Key::VERBOSITY].to_i
|
714
|
+
end
|
715
|
+
begin
|
716
|
+
executor = build_executor(tool, context) do
|
717
|
+
yield context
|
718
|
+
end
|
719
|
+
catch(:result) do
|
720
|
+
executor.call
|
721
|
+
0
|
722
|
+
end
|
723
|
+
ensure
|
724
|
+
cur_logger.level = original_level if cur_logger
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
def build_executor(tool, context)
|
729
|
+
executor = proc do
|
730
|
+
begin
|
731
|
+
if !context[Context::Key::USAGE_ERRORS].empty?
|
732
|
+
handle_usage_errors(context, tool)
|
733
|
+
elsif !tool.runnable?
|
734
|
+
raise NotRunnableError, "No implementation for tool #{tool.display_name.inspect}"
|
735
|
+
else
|
736
|
+
yield
|
737
|
+
end
|
738
|
+
rescue ::Interrupt => e
|
739
|
+
raise e unless tool.handles_interrupts?
|
740
|
+
handle_interrupt(context, tool.interrupt_handler, e)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
tool.built_middleware.reverse_each do |middleware|
|
744
|
+
executor = make_executor(middleware, context, executor)
|
745
|
+
end
|
746
|
+
executor
|
747
|
+
end
|
748
|
+
|
749
|
+
def handle_usage_errors(context, tool)
|
750
|
+
usage_errors = context[Context::Key::USAGE_ERRORS]
|
751
|
+
handler = tool.usage_error_handler
|
752
|
+
raise ArgParsingError, usage_errors if handler.nil?
|
753
|
+
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
754
|
+
if handler.arity.zero?
|
755
|
+
context.instance_exec(&handler)
|
756
|
+
else
|
757
|
+
context.instance_exec(usage_errors, &handler)
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
def handle_interrupt(context, handler, exception)
|
762
|
+
handler = context.method(handler).to_proc if handler.is_a?(::Symbol)
|
763
|
+
if handler.arity.zero?
|
764
|
+
context.instance_exec(&handler)
|
765
|
+
else
|
766
|
+
context.instance_exec(exception, &handler)
|
767
|
+
end
|
768
|
+
rescue ::Interrupt => e
|
769
|
+
raise e if e.equal?(exception)
|
770
|
+
handle_interrupt(context, handler, e)
|
771
|
+
end
|
772
|
+
|
773
|
+
def make_executor(middleware, context, next_executor)
|
774
|
+
if middleware.respond_to?(:run)
|
775
|
+
proc { middleware.run(context, &next_executor) }
|
776
|
+
else
|
777
|
+
next_executor
|
778
|
+
end
|
779
|
+
end
|
730
780
|
end
|
731
781
|
end
|