toys-core 0.7.0 → 0.8.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 +98 -0
- data/LICENSE.md +16 -24
- data/README.md +307 -59
- data/docs/guide.md +44 -4
- data/lib/toys-core.rb +58 -49
- data/lib/toys/acceptor.rb +672 -0
- data/lib/toys/alias.rb +106 -0
- data/lib/toys/arg_parser.rb +624 -0
- data/lib/toys/cli.rb +422 -181
- data/lib/toys/compat.rb +83 -0
- data/lib/toys/completion.rb +442 -0
- data/lib/toys/context.rb +354 -0
- data/lib/toys/core_version.rb +18 -26
- data/lib/toys/dsl/flag.rb +213 -56
- data/lib/toys/dsl/flag_group.rb +237 -51
- data/lib/toys/dsl/positional_arg.rb +210 -0
- data/lib/toys/dsl/tool.rb +968 -317
- data/lib/toys/errors.rb +46 -28
- data/lib/toys/flag.rb +821 -0
- data/lib/toys/flag_group.rb +282 -0
- data/lib/toys/input_file.rb +18 -26
- data/lib/toys/loader.rb +110 -100
- data/lib/toys/middleware.rb +24 -31
- data/lib/toys/mixin.rb +90 -59
- data/lib/toys/module_lookup.rb +125 -0
- data/lib/toys/positional_arg.rb +184 -0
- data/lib/toys/source_info.rb +192 -0
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
- data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
- data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
- data/lib/toys/standard_middleware/show_help.rb +130 -113
- data/lib/toys/standard_middleware/show_root_version.rb +29 -35
- data/lib/toys/standard_mixins/exec.rb +116 -78
- data/lib/toys/standard_mixins/fileutils.rb +16 -24
- data/lib/toys/standard_mixins/gems.rb +29 -30
- data/lib/toys/standard_mixins/highline.rb +34 -41
- data/lib/toys/standard_mixins/terminal.rb +72 -26
- data/lib/toys/template.rb +51 -35
- data/lib/toys/tool.rb +1161 -206
- data/lib/toys/utils/completion_engine.rb +171 -0
- data/lib/toys/utils/exec.rb +279 -182
- data/lib/toys/utils/gems.rb +58 -49
- data/lib/toys/utils/help_text.rb +117 -111
- data/lib/toys/utils/terminal.rb +69 -62
- data/lib/toys/wrappable_string.rb +162 -0
- metadata +24 -22
- data/lib/toys/definition/acceptor.rb +0 -191
- data/lib/toys/definition/alias.rb +0 -112
- data/lib/toys/definition/arg.rb +0 -140
- data/lib/toys/definition/flag.rb +0 -370
- data/lib/toys/definition/flag_group.rb +0 -205
- data/lib/toys/definition/source_info.rb +0 -190
- data/lib/toys/definition/tool.rb +0 -842
- data/lib/toys/dsl/arg.rb +0 -132
- data/lib/toys/runner.rb +0 -188
- data/lib/toys/standard_middleware.rb +0 -47
- data/lib/toys/utils/module_lookup.rb +0 -135
- data/lib/toys/utils/wrappable_string.rb +0 -165
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
22
|
+
;
|
23
|
+
|
24
|
+
module Toys
|
25
|
+
##
|
26
|
+
# Information about source toys directories and files.
|
27
|
+
#
|
28
|
+
class SourceInfo
|
29
|
+
##
|
30
|
+
# Create a SourceInfo.
|
31
|
+
# @private
|
32
|
+
#
|
33
|
+
def initialize(parent, context_directory, source, source_type, source_name, data_dir_name)
|
34
|
+
@parent = parent
|
35
|
+
@context_directory = context_directory
|
36
|
+
@source = source
|
37
|
+
@source_type = source_type
|
38
|
+
@source_path = source if source.is_a?(::String)
|
39
|
+
@source_proc = source if source.is_a?(::Proc)
|
40
|
+
@source_name = source_name
|
41
|
+
@data_dir =
|
42
|
+
if data_dir_name && @source_path
|
43
|
+
dir = ::File.join(::File.dirname(@source_path), data_dir_name)
|
44
|
+
dir if ::File.directory?(dir) && ::File.readable?(dir)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# The parent of this SourceInfo.
|
50
|
+
#
|
51
|
+
# @return [Toys::SourceInfo] The parent.
|
52
|
+
# @return [nil] if this SourceInfo is the root.
|
53
|
+
#
|
54
|
+
attr_reader :parent
|
55
|
+
|
56
|
+
##
|
57
|
+
# The context directory path (normally the directory containing the
|
58
|
+
# toplevel toys file or directory).
|
59
|
+
#
|
60
|
+
# @return [String] The context directory path.
|
61
|
+
# @return [nil] if there is no context directory (perhaps because the tool
|
62
|
+
# is being defined from a block)
|
63
|
+
#
|
64
|
+
attr_reader :context_directory
|
65
|
+
|
66
|
+
##
|
67
|
+
# The source, which may be a path or a proc.
|
68
|
+
#
|
69
|
+
# @return [String] Path to the source file or directory.
|
70
|
+
# @return [Proc] The block serving as the source.
|
71
|
+
#
|
72
|
+
attr_reader :source
|
73
|
+
|
74
|
+
##
|
75
|
+
# Return the type of source.
|
76
|
+
#
|
77
|
+
# @return [:file,:directory,:proc]
|
78
|
+
#
|
79
|
+
attr_reader :source_type
|
80
|
+
|
81
|
+
##
|
82
|
+
# The path of the current source file or directory.
|
83
|
+
#
|
84
|
+
# @return [String] The source path
|
85
|
+
# @return [nil] if this source is not a file system path.
|
86
|
+
#
|
87
|
+
attr_reader :source_path
|
88
|
+
|
89
|
+
##
|
90
|
+
# The source proc.
|
91
|
+
#
|
92
|
+
# @return [Proc] The source proc
|
93
|
+
# @return [nil] if this source is not a proc.
|
94
|
+
#
|
95
|
+
attr_reader :source_proc
|
96
|
+
|
97
|
+
##
|
98
|
+
# The user-visible name of this source.
|
99
|
+
#
|
100
|
+
# @return [String]
|
101
|
+
#
|
102
|
+
attr_reader :source_name
|
103
|
+
alias to_s source_name
|
104
|
+
|
105
|
+
##
|
106
|
+
# Locate the given data file or directory and return an absolute path.
|
107
|
+
#
|
108
|
+
# @param path [String] The relative path to find
|
109
|
+
# @param type [nil,:file,:directory] Type of file system object to find,
|
110
|
+
# or nil (the default) to return any type.
|
111
|
+
# @return [String] Absolute path of the resulting data.
|
112
|
+
# @return [nil] if the data was not found.
|
113
|
+
#
|
114
|
+
def find_data(path, type: nil)
|
115
|
+
if @data_dir
|
116
|
+
full_path = ::File.join(@data_dir, path)
|
117
|
+
case type
|
118
|
+
when :file
|
119
|
+
return full_path if ::File.file?(full_path)
|
120
|
+
when :directory
|
121
|
+
return full_path if ::File.directory?(full_path)
|
122
|
+
else
|
123
|
+
return full_path if ::File.readable?(full_path)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
parent&.find_data(path, type: type)
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Create a child SourceInfo relative to the parent path.
|
131
|
+
# @private
|
132
|
+
#
|
133
|
+
def relative_child(filename, data_dir_name)
|
134
|
+
raise "Cannot create relative child of a proc" unless source_path
|
135
|
+
child_path = ::File.join(source_path, filename)
|
136
|
+
child_path, type = SourceInfo.check_path(child_path, true)
|
137
|
+
return nil unless child_path
|
138
|
+
SourceInfo.new(self, context_directory, child_path, type, child_path, data_dir_name)
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Create a child SourceInfo with an absolute path.
|
143
|
+
# @private
|
144
|
+
#
|
145
|
+
def absolute_child(child_path)
|
146
|
+
child_path, type = SourceInfo.check_path(child_path, false)
|
147
|
+
SourceInfo.new(self, context_directory, child_path, type, child_path, nil)
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Create a root source info for a file path.
|
152
|
+
# @private
|
153
|
+
#
|
154
|
+
def self.create_path_root(source_path)
|
155
|
+
source_path, type = check_path(source_path, false)
|
156
|
+
context_directory = ::File.dirname(source_path)
|
157
|
+
new(nil, context_directory, source_path, type, source_path, nil)
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Create a root source info for a proc.
|
162
|
+
# @private
|
163
|
+
#
|
164
|
+
def self.create_proc_root(source_proc, source_name)
|
165
|
+
new(nil, nil, source_proc, :proc, source_name, nil)
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Check a path and determine the canonical path and type.
|
170
|
+
# @private
|
171
|
+
#
|
172
|
+
def self.check_path(path, lenient)
|
173
|
+
path = ::File.expand_path(path)
|
174
|
+
unless ::File.readable?(path)
|
175
|
+
raise LoaderError, "Cannot read: #{path}" unless lenient
|
176
|
+
return [nil, nil]
|
177
|
+
end
|
178
|
+
if ::File.file?(path)
|
179
|
+
unless ::File.extname(path) == ".rb"
|
180
|
+
raise LoaderError, "File is not a ruby file: #{path}" unless lenient
|
181
|
+
return [nil, nil]
|
182
|
+
end
|
183
|
+
[path, :file]
|
184
|
+
elsif ::File.directory?(path)
|
185
|
+
[path, :directory]
|
186
|
+
else
|
187
|
+
raise LoaderError, "Unknown type: #{path}" unless lenient
|
188
|
+
[nil, nil]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
9
14
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
30
22
|
;
|
31
23
|
|
32
24
|
module Toys
|
@@ -36,7 +28,7 @@ module Toys
|
|
36
28
|
#
|
37
29
|
# This middleware adds `-v`, `--verbose`, `-q`, and `--quiet` flags, if
|
38
30
|
# not already defined by the tool. These flags affect the setting of
|
39
|
-
# {Toys::
|
31
|
+
# {Toys::Context::Key::VERBOSITY}, and, thus, the logger level.
|
40
32
|
#
|
41
33
|
class AddVerbosityFlags
|
42
34
|
include Middleware
|
@@ -56,7 +48,7 @@ module Toys
|
|
56
48
|
##
|
57
49
|
# Create a AddVerbosityFlags middleware.
|
58
50
|
#
|
59
|
-
# @param [Boolean,Array<String>,Proc]
|
51
|
+
# @param verbose_flags [Boolean,Array<String>,Proc] Specify flags
|
60
52
|
# to increase verbosity. The value may be any of the following:
|
61
53
|
#
|
62
54
|
# * An array of flags that increase verbosity.
|
@@ -64,7 +56,7 @@ module Toys
|
|
64
56
|
# * The `false` value to disable verbose flags.
|
65
57
|
# * A proc that takes a tool and returns any of the above.
|
66
58
|
#
|
67
|
-
# @param [Boolean,Array<String>,Proc]
|
59
|
+
# @param quiet_flags [Boolean,Array<String>,Proc] Specify flags
|
68
60
|
# to decrease verbosity. The value may be any of the following:
|
69
61
|
#
|
70
62
|
# * An array of flags that decrease verbosity.
|
@@ -79,26 +71,29 @@ module Toys
|
|
79
71
|
|
80
72
|
##
|
81
73
|
# Configure the tool flags.
|
74
|
+
# @private
|
82
75
|
#
|
83
|
-
def config(
|
84
|
-
unless
|
85
|
-
StandardMiddleware.append_common_flag_group(
|
86
|
-
add_verbose_flags(
|
87
|
-
add_quiet_flags(
|
76
|
+
def config(tool, _loader)
|
77
|
+
unless tool.argument_parsing_disabled?
|
78
|
+
StandardMiddleware.append_common_flag_group(tool)
|
79
|
+
add_verbose_flags(tool)
|
80
|
+
add_quiet_flags(tool)
|
88
81
|
end
|
89
82
|
yield
|
90
83
|
end
|
91
84
|
|
92
85
|
private
|
93
86
|
|
94
|
-
|
95
|
-
|
96
|
-
|
87
|
+
INCREMENT_HANDLER = ->(_val, cur) { cur.to_i + 1 }
|
88
|
+
DECREMENT_HANDLER = ->(_val, cur) { cur.to_i - 1 }
|
89
|
+
|
90
|
+
def add_verbose_flags(tool)
|
91
|
+
verbose_flags = resolve_flags_spec(@verbose_flags, tool, DEFAULT_VERBOSE_FLAGS)
|
97
92
|
unless verbose_flags.empty?
|
98
|
-
|
99
|
-
|
93
|
+
tool.add_flag(
|
94
|
+
Context::Key::VERBOSITY, verbose_flags,
|
100
95
|
report_collisions: false,
|
101
|
-
handler:
|
96
|
+
handler: INCREMENT_HANDLER,
|
102
97
|
desc: "Increase verbosity",
|
103
98
|
long_desc: "Increase verbosity, causing additional logging levels to display.",
|
104
99
|
group: StandardMiddleware::COMMON_FLAG_GROUP
|
@@ -106,13 +101,13 @@ module Toys
|
|
106
101
|
end
|
107
102
|
end
|
108
103
|
|
109
|
-
def add_quiet_flags(
|
110
|
-
quiet_flags = resolve_flags_spec(@quiet_flags,
|
104
|
+
def add_quiet_flags(tool)
|
105
|
+
quiet_flags = resolve_flags_spec(@quiet_flags, tool, DEFAULT_QUIET_FLAGS)
|
111
106
|
unless quiet_flags.empty?
|
112
|
-
|
113
|
-
|
107
|
+
tool.add_flag(
|
108
|
+
Context::Key::VERBOSITY, quiet_flags,
|
114
109
|
report_collisions: false,
|
115
|
-
handler:
|
110
|
+
handler: DECREMENT_HANDLER,
|
116
111
|
desc: "Decrease verbosity",
|
117
112
|
long_desc: "Decrease verbosity, causing fewer logging levels to display.",
|
118
113
|
group: StandardMiddleware::COMMON_FLAG_GROUP
|
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
9
14
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
30
22
|
;
|
31
23
|
|
32
24
|
module Toys
|
@@ -40,34 +32,41 @@ module Toys
|
|
40
32
|
class HandleUsageErrors
|
41
33
|
include Middleware
|
42
34
|
|
35
|
+
##
|
36
|
+
# Exit code for usage error. (2 by convention)
|
37
|
+
# @return [Integer]
|
38
|
+
#
|
39
|
+
USAGE_ERROR_EXIT_CODE = 2
|
40
|
+
|
43
41
|
##
|
44
42
|
# Create a HandleUsageErrors middleware.
|
45
43
|
#
|
46
|
-
# @param [
|
47
|
-
# occurs. Default is
|
48
|
-
# @param [IO]
|
49
|
-
# @param [Boolean,nil]
|
44
|
+
# @param exit_code [Integer] The exit code to return if a usage error
|
45
|
+
# occurs. Default is {USAGE_ERROR_EXIT_CODE}.
|
46
|
+
# @param stream [IO] Output stream to write to. Default is stderr.
|
47
|
+
# @param styled_output [Boolean,nil] Cause the tool to display help text
|
50
48
|
# with ansi styles. If `nil`, display styles if the output stream is
|
51
49
|
# a tty. Default is `nil`.
|
52
50
|
#
|
53
|
-
def initialize(exit_code:
|
54
|
-
|
51
|
+
def initialize(exit_code: nil, stream: $stderr, styled_output: nil)
|
52
|
+
require "toys/utils/terminal"
|
53
|
+
@exit_code = exit_code || USAGE_ERROR_EXIT_CODE
|
55
54
|
@terminal = Utils::Terminal.new(output: stream, styled: styled_output)
|
56
55
|
end
|
57
56
|
|
58
57
|
##
|
59
58
|
# Intercept and handle usage errors during execution.
|
59
|
+
# @private
|
60
60
|
#
|
61
|
-
def run(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
61
|
+
def run(context)
|
62
|
+
yield
|
63
|
+
rescue ArgParsingError => e
|
64
|
+
require "toys/utils/help_text"
|
65
|
+
help_text = Utils::HelpText.from_context(context)
|
66
|
+
@terminal.puts(e.usage_errors.join("\n"), :bright_red, :bold)
|
67
|
+
@terminal.puts("")
|
68
|
+
@terminal.puts(help_text.usage_string(wrap_width: @terminal.width))
|
69
|
+
Context.exit(@exit_code)
|
71
70
|
end
|
72
71
|
end
|
73
72
|
end
|
@@ -1,32 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2019 Daniel Azuma
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
9
14
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# derived from this software without specific prior written permission.
|
18
|
-
#
|
19
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
22
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
23
|
-
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
24
|
-
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
25
|
-
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
26
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
27
|
-
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
28
|
-
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
29
|
-
# POSSIBILITY OF SUCH DAMAGE.
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
+
# IN THE SOFTWARE.
|
30
22
|
;
|
31
23
|
|
32
24
|
module Toys
|
@@ -69,41 +61,27 @@ module Toys
|
|
69
61
|
" https://www.rubydoc.info/gems/toys-core for more info.",
|
70
62
|
"To replace this message, set the description and long description" \
|
71
63
|
" of the root tool, or configure the SetDefaultDescriptions" \
|
72
|
-
" middleware."
|
64
|
+
" middleware.",
|
73
65
|
].freeze
|
74
66
|
|
75
|
-
##
|
76
|
-
# A mapping of names for acceptable types
|
77
|
-
# @return [Hash]
|
78
|
-
#
|
79
|
-
ACCEPTABLE_NAMES = {
|
80
|
-
nil => "string",
|
81
|
-
::String => "nonempty string",
|
82
|
-
::TrueClass => "boolean",
|
83
|
-
::FalseClass => "boolean",
|
84
|
-
::OptionParser::DecimalInteger => "decimal integer",
|
85
|
-
::OptionParser::OctalInteger => "octal integer",
|
86
|
-
::OptionParser::DecimalNumeric => "decimal numeric"
|
87
|
-
}.freeze
|
88
|
-
|
89
67
|
##
|
90
68
|
# Create a SetDefaultDescriptions middleware given default descriptions.
|
91
69
|
#
|
92
|
-
# @param [String,nil]
|
70
|
+
# @param default_tool_desc [String,nil] The default short description for
|
93
71
|
# runnable tools, or `nil` not to set one. Defaults to
|
94
72
|
# {DEFAULT_TOOL_DESC}.
|
95
|
-
# @param [String,nil]
|
73
|
+
# @param default_tool_long_desc [String,nil] The default long description
|
96
74
|
# for runnable tools, or `nil` not to set one. Defaults to `nil`.
|
97
|
-
# @param [String,nil]
|
75
|
+
# @param default_namespace_desc [String,nil] The default short
|
98
76
|
# description for non-runnable tools, or `nil` not to set one.
|
99
77
|
# Defaults to {DEFAULT_TOOL_DESC}.
|
100
|
-
# @param [String,nil]
|
78
|
+
# @param default_namespace_long_desc [String,nil] The default long
|
101
79
|
# description for non-runnable tools, or `nil` not to set one.
|
102
80
|
# Defaults to `nil`.
|
103
|
-
# @param [String,nil]
|
81
|
+
# @param default_root_desc [String,nil] The default short description for
|
104
82
|
# the root tool, or `nil` not to set one. Defaults to
|
105
83
|
# {DEFAULT_ROOT_DESC}.
|
106
|
-
# @param [String,nil]
|
84
|
+
# @param default_root_long_desc [String,nil] The default long description
|
107
85
|
# for the root tool, or `nil` not to set one. Defaults to
|
108
86
|
# {DEFAULT_ROOT_LONG_DESC}.
|
109
87
|
#
|
@@ -123,15 +101,20 @@ module Toys
|
|
123
101
|
|
124
102
|
##
|
125
103
|
# Add default description text to tools.
|
104
|
+
# @private
|
126
105
|
#
|
127
106
|
def config(tool, loader)
|
128
107
|
data = {tool: tool, loader: loader}
|
129
|
-
tool.
|
108
|
+
tool.flags.each do |flag|
|
130
109
|
config_desc(flag, generate_flag_desc(flag, data), generate_flag_long_desc(flag, data))
|
131
110
|
end
|
132
|
-
tool.
|
111
|
+
tool.positional_args.each do |arg|
|
133
112
|
config_desc(arg, generate_arg_desc(arg, data), generate_arg_long_desc(arg, data))
|
134
113
|
end
|
114
|
+
tool.flag_groups.each do |flag_group|
|
115
|
+
config_desc(flag_group, generate_flag_group_desc(flag_group, data),
|
116
|
+
generate_flag_group_long_desc(flag_group, data))
|
117
|
+
end
|
135
118
|
config_desc(tool, generate_tool_desc(tool, data), generate_tool_long_desc(tool, data))
|
136
119
|
yield
|
137
120
|
end
|
@@ -143,13 +126,13 @@ module Toys
|
|
143
126
|
# By default, it uses the parameters given to the middleware object.
|
144
127
|
# Override this method to provide different logic.
|
145
128
|
#
|
146
|
-
# @param [Toys::Tool]
|
147
|
-
# @param [Hash]
|
129
|
+
# @param tool [Toys::Tool] The tool to document.
|
130
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
148
131
|
# the {Toys::Loader} is passed with key `:loader`. Future versions
|
149
132
|
# of Toys may provide additional information.
|
150
|
-
# @return [String,Array<String>,Toys::
|
151
|
-
#
|
152
|
-
#
|
133
|
+
# @return [String,Array<String>,Toys::WrappableString] The default
|
134
|
+
# description. See {Toys::Tool#desc=} for info on the format.
|
135
|
+
# @return [nil] if this middleware should not set the description.
|
153
136
|
#
|
154
137
|
def generate_tool_desc(tool, data)
|
155
138
|
if tool.root?
|
@@ -166,13 +149,14 @@ module Toys
|
|
166
149
|
# By default, it uses the parameters given to the middleware object.
|
167
150
|
# Override this method to provide different logic.
|
168
151
|
#
|
169
|
-
# @param [Toys::Tool]
|
170
|
-
# @param [Hash]
|
171
|
-
# the {Toys::Loader} is passed with key `:loader`. Future versions
|
172
|
-
#
|
173
|
-
# @return [Array<Toys::
|
174
|
-
#
|
175
|
-
#
|
152
|
+
# @param tool [Toys::Tool] The tool to document
|
153
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
154
|
+
# the {Toys::Loader} is passed with key `:loader`. Future versions of
|
155
|
+
# Toys may provide additional information.
|
156
|
+
# @return [Array<Toys::WrappableString,String,Array<String>>] The default
|
157
|
+
# long description. See {Toys::Tool#long_desc=} for info on the
|
158
|
+
# format.
|
159
|
+
# @return [nil] if this middleware should not set the long description.
|
176
160
|
#
|
177
161
|
def generate_tool_long_desc(tool, data)
|
178
162
|
if tool.root?
|
@@ -188,17 +172,17 @@ module Toys
|
|
188
172
|
# This method implements the logic for generating a flag description.
|
189
173
|
# Override this method to provide different logic.
|
190
174
|
#
|
191
|
-
# @param [Toys::
|
192
|
-
# @param [Hash]
|
193
|
-
# the {Toys::Tool} is passed with key `:tool`. Future
|
194
|
-
# Toys may provide additional information.
|
195
|
-
# @return [String,Array<String>,Toys::
|
196
|
-
#
|
197
|
-
#
|
175
|
+
# @param flag [Toys::Flag] The flag to document
|
176
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
177
|
+
# the {Toys::Tool} is passed with key `:tool`. Future
|
178
|
+
# versions of Toys may provide additional information.
|
179
|
+
# @return [String,Array<String>,Toys::WrappableString] The default
|
180
|
+
# description. See {Toys::Tool#desc=} for info on the format.
|
181
|
+
# @return [nil] if this middleware should not set the description.
|
198
182
|
#
|
199
183
|
def generate_flag_desc(flag, data) # rubocop:disable Lint/UnusedMethodArgument
|
200
184
|
name = flag.key.to_s.tr("_", "-").gsub(/[^\w-]/, "").downcase.inspect
|
201
|
-
acceptable = flag.flag_type == :value ?
|
185
|
+
acceptable = flag.flag_type == :value ? flag.acceptor.type_desc : "boolean flag"
|
202
186
|
default_clause = flag.default ? " (default is #{flag.default.inspect})" : ""
|
203
187
|
"Sets the #{name} option as type #{acceptable}#{default_clause}."
|
204
188
|
end
|
@@ -207,13 +191,14 @@ module Toys
|
|
207
191
|
# This method implements logic for generating a flag long description.
|
208
192
|
# Override this method to provide different logic.
|
209
193
|
#
|
210
|
-
# @param [Toys::
|
211
|
-
# @param [Hash]
|
194
|
+
# @param flag [Toys::Flag] The flag to document
|
195
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
212
196
|
# the {Toys::Tool} is passed with key `:tool`. Future versions of
|
213
|
-
# Toys may provide additional information.
|
214
|
-
# @return [Array<Toys::
|
215
|
-
#
|
216
|
-
#
|
197
|
+
# versions of Toys may provide additional information.
|
198
|
+
# @return [Array<Toys::WrappableString,String,Array<String>>] The default
|
199
|
+
# long description. See {Toys::Tool#long_desc=} for info on the
|
200
|
+
# format.
|
201
|
+
# @return [nil] if this middleware should not set the long description.
|
217
202
|
#
|
218
203
|
def generate_flag_long_desc(flag, data) # rubocop:disable Lint/UnusedMethodArgument
|
219
204
|
nil
|
@@ -223,16 +208,16 @@ module Toys
|
|
223
208
|
# This method implements the logic for generating an arg description.
|
224
209
|
# Override this method to provide different logic.
|
225
210
|
#
|
226
|
-
# @param [Toys::
|
227
|
-
# @param [Hash]
|
211
|
+
# @param arg [Toys::PositionalArg] The arg to document
|
212
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
228
213
|
# the {Toys::Tool} is passed with key `:tool`. Future versions of
|
229
214
|
# Toys may provide additional information.
|
230
|
-
# @return [String,Array<String>,Toys::
|
231
|
-
#
|
232
|
-
#
|
215
|
+
# @return [String,Array<String>,Toys::WrappableString] The default
|
216
|
+
# description. See {Toys::Tool#desc=} for info on the format.
|
217
|
+
# @return [nil] if this middleware should not set the description.
|
233
218
|
#
|
234
219
|
def generate_arg_desc(arg, data) # rubocop:disable Lint/UnusedMethodArgument
|
235
|
-
acceptable =
|
220
|
+
acceptable = arg.acceptor.type_desc
|
236
221
|
default_clause = arg.default ? " (default is #{arg.default.inspect})" : ""
|
237
222
|
case arg.type
|
238
223
|
when :required
|
@@ -248,26 +233,63 @@ module Toys
|
|
248
233
|
# This method implements logic for generating an arg long description.
|
249
234
|
# Override this method to provide different logic.
|
250
235
|
#
|
251
|
-
# @param [Toys::
|
252
|
-
# @param [Hash]
|
236
|
+
# @param arg [Toys::PositionalArg] The arg to document
|
237
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
253
238
|
# the {Toys::Tool} is passed with key `:tool`. Future versions of
|
254
239
|
# Toys may provide additional information.
|
255
|
-
# @return [Array<Toys::
|
256
|
-
#
|
257
|
-
#
|
240
|
+
# @return [Array<Toys::WrappableString,String,Array<String>>] The default
|
241
|
+
# long description. See {Toys::Tool#long_desc=} for info on the
|
242
|
+
# format.
|
243
|
+
# @return [nil] if this middleware should not set the long description.
|
258
244
|
#
|
259
245
|
def generate_arg_long_desc(arg, data) # rubocop:disable Lint/UnusedMethodArgument
|
260
246
|
nil
|
261
247
|
end
|
262
248
|
|
263
249
|
##
|
264
|
-
#
|
250
|
+
# This method implements the logic for generating a flag group
|
251
|
+
# description. Override this method to provide different logic.
|
265
252
|
#
|
266
|
-
# @param [
|
267
|
-
# @
|
253
|
+
# @param group [Toys::FlagGroup] The flag group to document
|
254
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
255
|
+
# the {Toys::Tool} is passed with key `:tool`. Future
|
256
|
+
# versions of Toys may provide additional information.
|
257
|
+
# @return [String,Array<String>,Toys::WrappableString] The default
|
258
|
+
# description. See {Toys::Tool#desc=} for info on the format.
|
259
|
+
# @return [nil] if this middleware should not set the description.
|
260
|
+
#
|
261
|
+
def generate_flag_group_desc(group, data) # rubocop:disable Lint/UnusedMethodArgument
|
262
|
+
if group.is_a?(FlagGroup::Required)
|
263
|
+
"Required Flags"
|
264
|
+
else
|
265
|
+
"Flags"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
##
|
270
|
+
# This method implements the logic for generating a flag group long
|
271
|
+
# description. Override this method to provide different logic.
|
268
272
|
#
|
269
|
-
|
270
|
-
|
273
|
+
# @param group [Toys::FlagGroup] The flag group to document
|
274
|
+
# @param data [Hash] Additional data that might be useful. Currently,
|
275
|
+
# the {Toys::Tool} is passed with key `:tool`. Future
|
276
|
+
# versions of Toys may provide additional information.
|
277
|
+
# @return [Array<Toys::WrappableString,String,Array<String>>] The default
|
278
|
+
# long description. See {Toys::Tool#long_desc=} for info on the
|
279
|
+
# format.
|
280
|
+
# @return [nil] if this middleware should not set the long description.
|
281
|
+
#
|
282
|
+
def generate_flag_group_long_desc(group, data) # rubocop:disable Lint/UnusedMethodArgument
|
283
|
+
case group
|
284
|
+
when FlagGroup::Required
|
285
|
+
["These flags are required."]
|
286
|
+
when FlagGroup::ExactlyOne
|
287
|
+
["Exactly one of these flags must be set."]
|
288
|
+
when FlagGroup::AtMostOne
|
289
|
+
["At most one of these flags must be set."]
|
290
|
+
when FlagGroup::AtLeastOne
|
291
|
+
["At least one of these flags must be set."]
|
292
|
+
end
|
271
293
|
end
|
272
294
|
|
273
295
|
private
|