toys-core 0.23.0 → 0.25.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/.yardopts +0 -1
- data/CHANGELOG.md +37 -0
- data/docs/guide.md +6 -3
- data/lib/toys/arg_parser.rb +2 -1
- data/lib/toys/cli.rb +10 -5
- data/lib/toys/compat.rb +111 -105
- data/lib/toys/core.rb +1 -8
- data/lib/toys/dsl/tool.rb +20 -35
- data/lib/toys/flag.rb +4 -2
- data/lib/toys/runner.rb +36 -10
- data/lib/toys/source_info.rb +3 -1
- data/lib/toys/source_spec.rb +48 -4
- data/lib/toys/standard_middleware/handle_usage_errors.rb +3 -2
- data/lib/toys/standard_middleware/show_help.rb +7 -5
- data/lib/toys/standard_mixins/gems.rb +2 -4
- data/lib/toys/standard_mixins/highline.rb +9 -2
- data/lib/toys/utils/gems.rb +44 -21
- data/lib/toys/utils/git_cache.rb +165 -72
- data/lib/toys/utils/pager.rb +41 -9
- data/lib/toys/utils/standard_ui.rb +152 -23
- data/lib/toys/utils/terminal.rb +49 -2
- data/lib/toys/utils/xdg.rb +22 -2
- data/lib/toys/warnings.rb +161 -0
- data/lib/toys-core.rb +3 -1
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c9a692b91f8fcd9e2aee00a360ac14b676ec1876bbf889468440e3827ce8877b
|
|
4
|
+
data.tar.gz: 6fc4cbd7769db055405a17be755dd0dc0e9612f9e5fd00451c3a1480c2924c6c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f3850a2d2d3eaf18f9ace3730a4bc34355400b8ff75308e0e739d77a5e664a7377c0fa9bacbd9726be3b99fc4bb5c5c6e742f5171d3ff4185dab90fe50f61f5
|
|
7
|
+
data.tar.gz: f0337ba0809e6e56f233fe22f2e4b423f19b36df4f1163498c8bd1ef6e545301bbbb0e51b12a92a2f218d001d5a9092e739ff088ea5caf6547e07568c4692a04
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.25.0 / 2026-10-10
|
|
4
|
+
|
|
5
|
+
This is a release candidate release toward version 1.0. It includes a variety of fixes and small features, largely around adherence to clig.dev and other common CLI guidelines.
|
|
6
|
+
|
|
7
|
+
* ADDED: Added `Toys::Utils::Terminal.infer_styled`, the rule a terminal uses to decide whether to style output
|
|
8
|
+
* ADDED: The `FORCE_COLOR` environment variable forces styled output on, or off when set to 0 or false
|
|
9
|
+
* ADDED: The `show_help` middleware includes `-h` in the default help flags
|
|
10
|
+
* ADDED: `StandardUI` logs with a simple severity prefix unless verbosity is positive
|
|
11
|
+
* ADDED: Issue warnings for deprecated interfaces
|
|
12
|
+
* ADDED: `Utils::Pager` supports an output option
|
|
13
|
+
* FIXED: The `:push` handler creates a new array rather than appending in place, so that a frozen or non-cloneable default doesn't cause problems
|
|
14
|
+
* FIXED: An empty `NO_COLOR` environment variable no longer disables styled output, per the no-color.org spec
|
|
15
|
+
* FIXED: Styled output is disabled when the `TERM` environment variable is set to `dumb`
|
|
16
|
+
* FIXED: The highline mixin decides whether to use color from its own output stream, and honors `NO_COLOR`, `FORCE_COLOR`, and `TERM=dumb`
|
|
17
|
+
* FIXED: Help text displayed through a pager honors the styled output setting, including `NO_COLOR`, `FORCE_COLOR`, and `TERM=dumb`
|
|
18
|
+
* FIXED: Tool-not-found suggestions include namespaces
|
|
19
|
+
* FIXED: Tool initializers run within the logger level implied by the verbosity
|
|
20
|
+
* FIXED: `StandardUI` no longer colors the `DEBUG` log header
|
|
21
|
+
* FIXED: Re-vendor `simple_xdg` 0.1.2 to pick up home directory fix
|
|
22
|
+
* FIXED: Re-vendor `git_cache` 0.2.0 to report an unwritable cache directory clearly and fix repo lock races
|
|
23
|
+
* FIXED: Show_help writes paged help to the configured stream
|
|
24
|
+
* DOCS: Documentation no longer references a few obsolete features such as the `TOYS_PATH` variable
|
|
25
|
+
|
|
26
|
+
### v0.24.0 / 2026-09-11
|
|
27
|
+
|
|
28
|
+
Feature updates:
|
|
29
|
+
|
|
30
|
+
* `SourceSpec::Gem` now includes optional `on_missing:` and `default_confirm:` fields to control how missing gems are handled.
|
|
31
|
+
* The `load_gem` directive now takes optional `on_missing:` and `default_confirm:` arguments to control how missing gems are handled.
|
|
32
|
+
* Passing options when activating a gem using the `:gems` mixin no longer clobbers options passed when instantiating the mixin.
|
|
33
|
+
|
|
34
|
+
Removed some old deprecated items. (All technically breaking changes)
|
|
35
|
+
|
|
36
|
+
* Removed deprecated `Toys::CORE_VERSION`. Use `Toys::Core::VERSION` instead.
|
|
37
|
+
* Removed deprecated `alias_tool` directive. Pass `delegate_relative:` to the `tool` directive instead.
|
|
38
|
+
* Removed deprecated `suppress_confirm:` argument to the `Gems` utility. Use `on_missing:` instead.
|
|
39
|
+
|
|
3
40
|
### v0.23.0 / 2026-09-09
|
|
4
41
|
|
|
5
42
|
This is a major release, with several new features, a number of fixes, and a significant refactor of the tool loading and execution layers, including several breaking interface changes. It is a release candidate for the upcoming version 1.0.
|
data/docs/guide.md
CHANGED
|
@@ -705,7 +705,8 @@ cli = Toys::CLI.new(logger_factory: my_logger_factory)
|
|
|
705
705
|
#### StandardUI logging
|
|
706
706
|
|
|
707
707
|
{Toys::Utils::StandardUI} implements the logger used by the `toys` gem, which
|
|
708
|
-
formats log entries with
|
|
708
|
+
formats log entries with a severity prefix using ANSI coloring, and adds a
|
|
709
|
+
timestamp when the tool is run with positive verbosity.
|
|
709
710
|
|
|
710
711
|
You can use this logger by passing the proc returned by
|
|
711
712
|
{Toys::Utils::StandardUI#logger_factory_proc} to the CLI constructor:
|
|
@@ -718,8 +719,10 @@ cli = Toys::CLI.new(logger_factory: standard_ui.logger_factory_proc)
|
|
|
718
719
|
You can also customize the logger by subclassing StandardUI and overriding its
|
|
719
720
|
methods or adjusting its parameters. In particular, you can alter the
|
|
720
721
|
{Toys::Utils::StandardUI#log_header_severity_styles} mapping to adjust styling,
|
|
721
|
-
or override {Toys::Utils::StandardUI#create_logger}
|
|
722
|
-
{Toys::Utils::StandardUI#
|
|
722
|
+
or override {Toys::Utils::StandardUI#create_logger},
|
|
723
|
+
{Toys::Utils::StandardUI#verbose_log_format?},
|
|
724
|
+
{Toys::Utils::StandardUI#format_simple_log_entry}, or
|
|
725
|
+
{Toys::Utils::StandardUI#format_verbose_log_entry} to adjust content and
|
|
723
726
|
formatting.
|
|
724
727
|
|
|
725
728
|
### Handling errors
|
data/lib/toys/arg_parser.rb
CHANGED
|
@@ -641,7 +641,8 @@ module Toys
|
|
|
641
641
|
if @tool.runnable? || !@seen_flags.empty?
|
|
642
642
|
ExtraArgumentsError.new(arguments: @unmatched_positional)
|
|
643
643
|
else
|
|
644
|
-
|
|
644
|
+
subtools = @loader.list_subtools(@tool.full_name, include_namespaces: true)
|
|
645
|
+
dictionary = subtools.map(&:simple_name)
|
|
645
646
|
first_arg = @unmatched_positional.first
|
|
646
647
|
ToolUnrecognizedError.new(full_name: @tool.full_name + [first_arg],
|
|
647
648
|
suggestions: Compat.suggestions(first_arg, dictionary))
|
data/lib/toys/cli.rb
CHANGED
|
@@ -88,6 +88,8 @@ module Toys
|
|
|
88
88
|
# as an argument, and returns a `Logger` to use when running that tool.
|
|
89
89
|
# Optional. If not provided (and no global logger is set),
|
|
90
90
|
# {Toys::CLI.default_logger_factory} is called to get a basic default.
|
|
91
|
+
# See {Toys::Runner} for how a logger can receive the verbosity of
|
|
92
|
+
# the run.
|
|
91
93
|
# @param base_level [Integer] The logger level that should correspond
|
|
92
94
|
# to zero verbosity.
|
|
93
95
|
# Optional. If not provided, defaults to the level the logger has
|
|
@@ -351,8 +353,8 @@ module Toys
|
|
|
351
353
|
# source list. If the given search directory path does not exist or does
|
|
352
354
|
# not contain either the file or directory, nothing is added.
|
|
353
355
|
#
|
|
354
|
-
# The main Toys executable uses this method to load tools from
|
|
355
|
-
# in the
|
|
356
|
+
# The main Toys executable uses this method to load tools from the
|
|
357
|
+
# directories in its upward walk and from the home directory.
|
|
356
358
|
#
|
|
357
359
|
# @param search_path [String,Pathname] A directory path to search for the
|
|
358
360
|
# well-known source file and directory. Must be a String or a Pathname.
|
|
@@ -405,9 +407,6 @@ module Toys
|
|
|
405
407
|
# given directory and works up through parent directories until it reaches
|
|
406
408
|
# the file system root or it encounters one of the "terminate" directories.
|
|
407
409
|
#
|
|
408
|
-
# The main Toys executable uses this method to load tools from the current
|
|
409
|
-
# directory and its ancestors.
|
|
410
|
-
#
|
|
411
410
|
# @param start [String,Pathname,nil] The first directory path to search.
|
|
412
411
|
# If not given, defaults to the current working directory. If provided,
|
|
413
412
|
# must be a String or a Pathname. Paths should generally be absolute.
|
|
@@ -581,6 +580,9 @@ module Toys
|
|
|
581
580
|
high_priority: false,
|
|
582
581
|
source_name: nil,
|
|
583
582
|
context_directory: :parent)
|
|
583
|
+
Warnings.warn(:add_config_path_deprecated,
|
|
584
|
+
"CLI#add_config_path is deprecated. Use add_source instead.",
|
|
585
|
+
uplevel: 1)
|
|
584
586
|
path = SourceSpec.check_and_normalize_path(path)
|
|
585
587
|
context_directory = resolve_context_directory(context_directory, path)
|
|
586
588
|
spec = SourceSpec.path(path, source_name: source_name, context_directory: context_directory)
|
|
@@ -631,6 +633,9 @@ module Toys
|
|
|
631
633
|
source_name: nil,
|
|
632
634
|
context_directory: nil,
|
|
633
635
|
&block)
|
|
636
|
+
Warnings.warn(:add_config_block_deprecated,
|
|
637
|
+
"CLI#add_config_block is deprecated. Use add_source instead.",
|
|
638
|
+
uplevel: 1)
|
|
634
639
|
spec = SourceSpec.block(source_name: source_name, context_directory: context_directory, &block)
|
|
635
640
|
add_source(spec, high_priority: high_priority)
|
|
636
641
|
end
|
data/lib/toys/compat.rb
CHANGED
|
@@ -22,127 +22,133 @@ module Toys
|
|
|
22
22
|
#
|
|
23
23
|
RUBY_VERSION_CODE = ruby_version
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
class << self
|
|
26
|
+
##
|
|
27
|
+
# @private
|
|
28
|
+
# Whether the current Ruby implementation is JRuby
|
|
29
|
+
#
|
|
30
|
+
# @return [boolean]
|
|
31
|
+
#
|
|
32
|
+
def jruby?
|
|
33
|
+
::RUBY_ENGINE == "jruby"
|
|
34
|
+
end
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
##
|
|
37
|
+
# @private
|
|
38
|
+
# Whether the current Ruby implementation is TruffleRuby
|
|
39
|
+
#
|
|
40
|
+
# @return [boolean]
|
|
41
|
+
#
|
|
42
|
+
def truffleruby?
|
|
43
|
+
::RUBY_ENGINE == "truffleruby"
|
|
44
|
+
end
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
##
|
|
47
|
+
# @private
|
|
48
|
+
# Whether we are running on Windows
|
|
49
|
+
#
|
|
50
|
+
# @return [boolean]
|
|
51
|
+
#
|
|
52
|
+
def windows?
|
|
53
|
+
::RbConfig::CONFIG["host_os"] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
|
54
|
+
end
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
##
|
|
57
|
+
# @private
|
|
58
|
+
# Whether we are running on Mac OS
|
|
59
|
+
#
|
|
60
|
+
# @return [boolean]
|
|
61
|
+
#
|
|
62
|
+
def macos?
|
|
63
|
+
::RbConfig::CONFIG["host_os"] =~ /darwin/
|
|
64
|
+
end
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
##
|
|
67
|
+
# @private
|
|
68
|
+
# Whether fork is supported on the current Ruby and OS
|
|
69
|
+
#
|
|
70
|
+
# @return [boolean]
|
|
71
|
+
#
|
|
72
|
+
def allow_fork?
|
|
73
|
+
!jruby? && !truffleruby? && !windows?
|
|
74
|
+
end
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
begin
|
|
85
|
-
require "did_you_mean"
|
|
86
|
-
rescue ::LoadError
|
|
87
|
-
require "rubygems"
|
|
76
|
+
##
|
|
77
|
+
# @private
|
|
78
|
+
# Whether it is possible to get suggestions from DidYouMean. If this
|
|
79
|
+
# returns false, {Compat.suggestions} will always return the empty array.
|
|
80
|
+
#
|
|
81
|
+
# @return [boolean]
|
|
82
|
+
#
|
|
83
|
+
def supports_suggestions?
|
|
84
|
+
unless defined?(@supports_suggestions)
|
|
88
85
|
begin
|
|
89
86
|
require "did_you_mean"
|
|
90
87
|
rescue ::LoadError
|
|
91
|
-
|
|
88
|
+
require "rubygems"
|
|
89
|
+
begin
|
|
90
|
+
require "did_you_mean"
|
|
91
|
+
rescue ::LoadError
|
|
92
|
+
# Oh well, it's not available
|
|
93
|
+
end
|
|
92
94
|
end
|
|
95
|
+
@supports_suggestions = defined?(::DidYouMean::SpellChecker)
|
|
93
96
|
end
|
|
94
|
-
@supports_suggestions
|
|
97
|
+
@supports_suggestions
|
|
95
98
|
end
|
|
96
|
-
@supports_suggestions
|
|
97
|
-
end
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
##
|
|
101
|
+
# @private
|
|
102
|
+
# A list of suggestions from DidYouMean.
|
|
103
|
+
#
|
|
104
|
+
# @param word [String] A value that seems wrong
|
|
105
|
+
# @param list [Array<String>] A list of valid values
|
|
106
|
+
#
|
|
107
|
+
# @return [Array<String>] A possibly empty array of suggestions from the
|
|
108
|
+
# valid list that could match the given word.
|
|
109
|
+
#
|
|
110
|
+
def suggestions(word, list)
|
|
111
|
+
if supports_suggestions?
|
|
112
|
+
::DidYouMean::SpellChecker.new(dictionary: list).correct(word)
|
|
113
|
+
else
|
|
114
|
+
[]
|
|
115
|
+
end
|
|
114
116
|
end
|
|
115
|
-
end
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
##
|
|
119
|
+
# @private
|
|
120
|
+
# A list of gems that should generally not be included in a bundle,
|
|
121
|
+
# usually because the Ruby implementation handles the library specially
|
|
122
|
+
# and cannot install the real gem. Currently, this includes the
|
|
123
|
+
# `pathname` gem for TruffleRuby, since TruffleRuby includes a special
|
|
124
|
+
# version of it.
|
|
125
|
+
#
|
|
126
|
+
# @return [Array<String>]
|
|
127
|
+
#
|
|
128
|
+
def gems_to_omit_from_bundles
|
|
129
|
+
if truffleruby?
|
|
130
|
+
["pathname"]
|
|
131
|
+
else
|
|
132
|
+
[]
|
|
133
|
+
end
|
|
131
134
|
end
|
|
132
|
-
end
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
##
|
|
137
|
+
# @private
|
|
138
|
+
# Sets the backtrace on an exception. In Ruby `< 3.4`,
|
|
139
|
+
# `Exception#set_backtrace` required a string array, whereas in newer
|
|
140
|
+
# Rubies, it would accept an array of backtrace locations.
|
|
141
|
+
#
|
|
142
|
+
# @param [Exception] exception
|
|
143
|
+
# @param [nil,Array<String>,Array<Thread::Backtrace::Location>] backtrace
|
|
144
|
+
#
|
|
145
|
+
# @return [Exception] The modified exception
|
|
146
|
+
#
|
|
147
|
+
def set_backtrace(exception, backtrace)
|
|
148
|
+
backtrace = backtrace.map(&:to_s) if RUBY_VERSION_CODE < 30400 && !backtrace.nil?
|
|
149
|
+
exception.set_backtrace(backtrace)
|
|
150
|
+
exception
|
|
151
|
+
end
|
|
146
152
|
end
|
|
147
153
|
end
|
|
148
154
|
end
|
data/lib/toys/core.rb
CHANGED
data/lib/toys/dsl/tool.rb
CHANGED
|
@@ -329,39 +329,6 @@ module Toys
|
|
|
329
329
|
self
|
|
330
330
|
end
|
|
331
331
|
|
|
332
|
-
##
|
|
333
|
-
# Create an alias, representing an "alternate name" for a tool.
|
|
334
|
-
#
|
|
335
|
-
# Note: This is functionally equivalent to creating a tool with the
|
|
336
|
-
# `:delegate_relative` option. As such, `alias_tool` is considered
|
|
337
|
-
# deprecated.
|
|
338
|
-
#
|
|
339
|
-
# ### Example
|
|
340
|
-
#
|
|
341
|
-
# This example defines a tool and an alias pointing to it. Both the tool
|
|
342
|
-
# name `test` and the alias `t` will then refer to the same tool.
|
|
343
|
-
#
|
|
344
|
-
# tool "test" do
|
|
345
|
-
# def run
|
|
346
|
-
# puts "Running tests..."
|
|
347
|
-
# end
|
|
348
|
-
# end
|
|
349
|
-
# alias_tool "t", "test"
|
|
350
|
-
# # Note: the following is preferred over alias_tool:
|
|
351
|
-
# # tool "t", delegate_relative: "test"
|
|
352
|
-
#
|
|
353
|
-
# @param word [String] The name of the alias
|
|
354
|
-
# @param target [String,Array<String>] Relative path to the target of the
|
|
355
|
-
# alias. This path may be given as an array of strings, or a single
|
|
356
|
-
# string possibly delimited by path separators.
|
|
357
|
-
# @return [self]
|
|
358
|
-
# @deprecated Use {#tool} and pass `:delegate_relative` instead
|
|
359
|
-
#
|
|
360
|
-
def alias_tool(word, target)
|
|
361
|
-
tool(word, delegate_relative: target)
|
|
362
|
-
self
|
|
363
|
-
end
|
|
364
|
-
|
|
365
332
|
##
|
|
366
333
|
# Causes the current tool to delegate to another tool, specified by the
|
|
367
334
|
# full tool name. When run, it simply invokes the target tool with the
|
|
@@ -508,6 +475,18 @@ module Toys
|
|
|
508
475
|
# Context directory paths should generally be absolute. Relative
|
|
509
476
|
# paths will be converted to absolute, using the current working
|
|
510
477
|
# directory at the time of loading.
|
|
478
|
+
# @param on_missing [:confirm,:error,:install] What to do if the
|
|
479
|
+
# requested gem is not installed. Possible values:
|
|
480
|
+
#
|
|
481
|
+
# * `:confirm` - prompt the user on whether to install
|
|
482
|
+
# * `:error` - raise an exception
|
|
483
|
+
# * `:install` - just install the gem
|
|
484
|
+
#
|
|
485
|
+
# Defaults to the setting in the {Toys::Utils::Gems} utility used to
|
|
486
|
+
# activate the gem (usually `:confirm`).
|
|
487
|
+
# @param default_confirm [boolean] The default confirmation result, if
|
|
488
|
+
# `on_missing` is set to `:confirm`. Defaults to the setting in the
|
|
489
|
+
# {Toys::Utils::Gems} utility used to activate the gem (usually true).
|
|
511
490
|
# @param as [String] Load into the given tool/namespace, relative to the
|
|
512
491
|
# current namespace. If omitted, tools will be loaded into the
|
|
513
492
|
# current namespace.
|
|
@@ -519,6 +498,8 @@ module Toys
|
|
|
519
498
|
path: nil,
|
|
520
499
|
toys_dir: nil,
|
|
521
500
|
context_directory: nil,
|
|
501
|
+
on_missing: nil,
|
|
502
|
+
default_confirm: nil,
|
|
522
503
|
as: nil)
|
|
523
504
|
version = versions + Array(version)
|
|
524
505
|
if as
|
|
@@ -527,7 +508,9 @@ module Toys
|
|
|
527
508
|
version: version,
|
|
528
509
|
path: path,
|
|
529
510
|
toys_dir: toys_dir,
|
|
530
|
-
context_directory: context_directory
|
|
511
|
+
context_directory: context_directory,
|
|
512
|
+
on_missing: on_missing,
|
|
513
|
+
default_confirm: default_confirm)
|
|
531
514
|
end
|
|
532
515
|
return self
|
|
533
516
|
end
|
|
@@ -535,7 +518,9 @@ module Toys
|
|
|
535
518
|
version: version,
|
|
536
519
|
path: path,
|
|
537
520
|
toys_dir: toys_dir,
|
|
538
|
-
context_directory: context_directory
|
|
521
|
+
context_directory: context_directory,
|
|
522
|
+
on_missing: on_missing,
|
|
523
|
+
default_confirm: default_confirm)
|
|
539
524
|
DSL::Internal.current_load_state(self).load_source(spec)
|
|
540
525
|
self
|
|
541
526
|
end
|
data/lib/toys/flag.rb
CHANGED
|
@@ -377,10 +377,12 @@ module Toys
|
|
|
377
377
|
SET_HANDLER = proc { |val| val }
|
|
378
378
|
|
|
379
379
|
##
|
|
380
|
-
# The push handler
|
|
380
|
+
# The push handler appends the given value, returning a new array. The
|
|
381
|
+
# previous array is not modified, so a shared or frozen default array
|
|
382
|
+
# can be used safely.
|
|
381
383
|
# @return [Proc]
|
|
382
384
|
#
|
|
383
|
-
PUSH_HANDLER = proc { |val, prev| prev.nil? ? [val] : prev
|
|
385
|
+
PUSH_HANDLER = proc { |val, prev| prev.nil? ? [val] : prev + [val] }
|
|
384
386
|
|
|
385
387
|
##
|
|
386
388
|
# The default handler is the set handler, which replaces the previous value.
|
data/lib/toys/runner.rb
CHANGED
|
@@ -38,6 +38,15 @@ module Toys
|
|
|
38
38
|
# end
|
|
39
39
|
# end
|
|
40
40
|
#
|
|
41
|
+
# While a tool runs, the Runner sets the level of the tool's logger to reflect
|
|
42
|
+
# the verbosity, as described under the `base_level` parameter of
|
|
43
|
+
# {#initialize}. A logger can also receive the verbosity itself, by
|
|
44
|
+
# implementing a `verbosity` attribute: if the logger responds to both
|
|
45
|
+
# `verbosity` and `verbosity=`, the Runner sets it to the verbosity (an
|
|
46
|
+
# Integer) for the duration of the run, and restores the previous value
|
|
47
|
+
# afterward. This lets a logger vary its behavior, such as its formatting, by
|
|
48
|
+
# verbosity independently of its level.
|
|
49
|
+
#
|
|
41
50
|
class Runner
|
|
42
51
|
##
|
|
43
52
|
# The singleton default logger_factory Proc, which simply returns a new
|
|
@@ -74,7 +83,8 @@ module Toys
|
|
|
74
83
|
# @param logger_factory [Proc,nil] A proc that takes a
|
|
75
84
|
# {Toys::ToolDefinition} as an argument, and returns a logger to use
|
|
76
85
|
# when running that tool. If not given,
|
|
77
|
-
# {Toys::Runner::DEFAULT_LOGGER_FACTORY} is used.
|
|
86
|
+
# {Toys::Runner::DEFAULT_LOGGER_FACTORY} is used. See {Toys::Runner}
|
|
87
|
+
# for how a logger can receive the verbosity of the run.
|
|
78
88
|
# @param base_level [Integer,nil] The logger level that corresponds to zero
|
|
79
89
|
# verbosity. If not provided, the level the logger has before a run
|
|
80
90
|
# adjusts it is used (typically Logger::WARN). A run nested inside
|
|
@@ -404,15 +414,15 @@ module Toys
|
|
|
404
414
|
end
|
|
405
415
|
|
|
406
416
|
# Prepares the runtime environment for the tool, applying lib paths and
|
|
407
|
-
# initializers, and then calls the tool within its middleware stack
|
|
408
|
-
#
|
|
409
|
-
#
|
|
417
|
+
# initializers, and then calls the tool within its middleware stack,
|
|
418
|
+
# returning the resulting exit code. The initializers and the tool both
|
|
419
|
+
# run within the logger level implied by the verbosity.
|
|
410
420
|
def execute_tool(context, &block)
|
|
411
421
|
@tool.source_info&.find_lib_paths&.reverse_each do |path|
|
|
412
422
|
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
|
|
413
423
|
end
|
|
414
|
-
@tool.run_initializers(context)
|
|
415
424
|
with_logger_level(context) do
|
|
425
|
+
@tool.run_initializers(context)
|
|
416
426
|
executor = build_executor(context, &block)
|
|
417
427
|
catch(:result) do
|
|
418
428
|
executor.call
|
|
@@ -426,23 +436,39 @@ module Toys
|
|
|
426
436
|
# afterward. The restoration must cover the level assignment itself,
|
|
427
437
|
# because a base level that cannot be combined with the verbosity fails
|
|
428
438
|
# there, and a base level left recorded behind a failed run would be
|
|
429
|
-
# picked up by later runs sharing the logger.
|
|
430
|
-
#
|
|
431
|
-
|
|
439
|
+
# picked up by later runs sharing the logger. Also sets the verbosity on
|
|
440
|
+
# a logger that supports it, as described in {Runner}. Yields directly if
|
|
441
|
+
# the tool has no logger.
|
|
442
|
+
def with_logger_level(context, &block)
|
|
432
443
|
cur_logger = context[Context::Key::LOGGER]
|
|
433
444
|
return yield unless cur_logger
|
|
445
|
+
verbosity = context[Context::Key::VERBOSITY].to_i
|
|
434
446
|
original_level = cur_logger.level
|
|
435
447
|
base_level = @base_level || base_levels[cur_logger] || original_level
|
|
436
448
|
saved_base_level = set_base_level(cur_logger, base_level)
|
|
437
449
|
begin
|
|
438
|
-
cur_logger.level = base_level -
|
|
439
|
-
|
|
450
|
+
cur_logger.level = base_level - verbosity
|
|
451
|
+
with_logger_verbosity(cur_logger, verbosity, &block)
|
|
440
452
|
ensure
|
|
441
453
|
set_base_level(cur_logger, saved_base_level)
|
|
442
454
|
cur_logger.level = original_level
|
|
443
455
|
end
|
|
444
456
|
end
|
|
445
457
|
|
|
458
|
+
# Sets the verbosity on a logger that supports it for the duration of
|
|
459
|
+
# the block, and restores it afterward. Yields directly if the logger
|
|
460
|
+
# does not have both a reader and a writer for the verbosity.
|
|
461
|
+
def with_logger_verbosity(logger, verbosity)
|
|
462
|
+
return yield unless logger.respond_to?(:verbosity) && logger.respond_to?(:verbosity=)
|
|
463
|
+
original_verbosity = logger.verbosity
|
|
464
|
+
begin
|
|
465
|
+
logger.verbosity = verbosity
|
|
466
|
+
yield
|
|
467
|
+
ensure
|
|
468
|
+
logger.verbosity = original_verbosity
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
446
472
|
# The base levels currently in effect on this thread, keyed by logger
|
|
447
473
|
# identity. A run that adjusts a logger's level records the base
|
|
448
474
|
# level it used, for the extent of that run, because a nested run cannot
|
data/lib/toys/source_info.rb
CHANGED
|
@@ -345,6 +345,8 @@ module Toys
|
|
|
345
345
|
end
|
|
346
346
|
|
|
347
347
|
def resolve_gem_spec(spec, parent, priority, gems_util)
|
|
348
|
+
gems_util ||= default_gems_util
|
|
349
|
+
gems_util = gems_util.with(on_missing: spec.on_missing, default_confirm: spec.default_confirm)
|
|
348
350
|
gem_version, gem_path, source_path =
|
|
349
351
|
resolve_gem_info(gems_util, spec.name, spec.version, spec.path, spec.toys_dir)
|
|
350
352
|
source_path, type = check_path(source_path, false)
|
|
@@ -486,7 +488,7 @@ module Toys
|
|
|
486
488
|
def resolve_gem_info(gems_util, gem_name, gem_versions, gem_path, gem_toys_dir)
|
|
487
489
|
require "toys/utils/gems"
|
|
488
490
|
begin
|
|
489
|
-
|
|
491
|
+
gems_util.activate(gem_name, *gem_versions)
|
|
490
492
|
rescue ::Toys::Utils::Gems::ActivationFailedError => e
|
|
491
493
|
raise ToolSourceError, e.message
|
|
492
494
|
end
|