toys-core 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/toys/context.rb
ADDED
@@ -0,0 +1,354 @@
|
|
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
|
+
require "logger"
|
25
|
+
|
26
|
+
module Toys
|
27
|
+
##
|
28
|
+
# This is the base class for tool execution. It represents `self` when your
|
29
|
+
# tool's methods (such as `run`) are called, and it defines the methods that
|
30
|
+
# can be called by your tool (such as {#logger} and {#exit}.)
|
31
|
+
#
|
32
|
+
# This class also manages the "data" available to your tool when it runs.
|
33
|
+
# This data is a hash of key-value pairs. It consists of values set by flags
|
34
|
+
# and arguments defined by the tool, plus some "well-known" values such as
|
35
|
+
# the logger and verbosity level.
|
36
|
+
#
|
37
|
+
# You can obtain a value from the data using the {Toys::Context#get} method.
|
38
|
+
# Additionally, convenience methods are provided for many of the well-known
|
39
|
+
# keys. For instance, you can call {Toys::Context#verbosity} to obtain the
|
40
|
+
# value for the key {Toys::Context::Key::VERBOSITY}. Finally, flags and
|
41
|
+
# positional arguments that store their data here will also typically
|
42
|
+
# generate convenience methods. For example, an argument with key `:abc` will
|
43
|
+
# add a method called `abc` that you can call to get the value.
|
44
|
+
#
|
45
|
+
# By convention, flags and arguments defined by your tool should use strings
|
46
|
+
# or symbols as keys. Keys that are not strings or symbols should either be
|
47
|
+
# well-known keys such as {Toys::Context::Key::VERBOSITY}, or should be used
|
48
|
+
# for internal private information needed by middleware and mixins. The
|
49
|
+
# module {Toys::Context::Key} defines a number of well-known keys as
|
50
|
+
# constants.
|
51
|
+
#
|
52
|
+
class Context
|
53
|
+
##
|
54
|
+
# Well-known context keys.
|
55
|
+
#
|
56
|
+
# This module is mixed into the runtime context. This means you can
|
57
|
+
# reference any of these constants directly from your run method.
|
58
|
+
#
|
59
|
+
# ## Example
|
60
|
+
#
|
61
|
+
# tool "my-name" do
|
62
|
+
# def run
|
63
|
+
# # TOOL_NAME is available here.
|
64
|
+
# puts "My name is #{get(TOOL_NAME)}"
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
module Key
|
69
|
+
##
|
70
|
+
# Context key for the argument list passed to the current tool. Value is
|
71
|
+
# an array of strings.
|
72
|
+
# @return [Object]
|
73
|
+
#
|
74
|
+
ARGS = ::Object.new.freeze
|
75
|
+
|
76
|
+
##
|
77
|
+
# Context key for the currently running {Toys::CLI}. You can use the
|
78
|
+
# value to run other tools from your tool by calling {Toys::CLI#run}.
|
79
|
+
# @return [Object]
|
80
|
+
#
|
81
|
+
CLI = ::Object.new.freeze
|
82
|
+
|
83
|
+
##
|
84
|
+
# Context key for the context directory path. The value is a string
|
85
|
+
# @return [Object]
|
86
|
+
#
|
87
|
+
CONTEXT_DIRECTORY = ::Object.new.freeze
|
88
|
+
|
89
|
+
##
|
90
|
+
# Context key for the active `Logger` object.
|
91
|
+
# @return [Object]
|
92
|
+
#
|
93
|
+
LOGGER = ::Object.new.freeze
|
94
|
+
|
95
|
+
##
|
96
|
+
# Context key for the {Toys::Tool} object being executed.
|
97
|
+
# @return [Object]
|
98
|
+
#
|
99
|
+
TOOL = ::Object.new.freeze
|
100
|
+
|
101
|
+
##
|
102
|
+
# Context key for the full name of the tool being executed. Value is an
|
103
|
+
# array of strings.
|
104
|
+
# @return [Object]
|
105
|
+
#
|
106
|
+
TOOL_NAME = ::Object.new.freeze
|
107
|
+
|
108
|
+
##
|
109
|
+
# Context key for the {Toys::SourceInfo} describing the source of this
|
110
|
+
# tool.
|
111
|
+
# @return [Object]
|
112
|
+
#
|
113
|
+
TOOL_SOURCE = ::Object.new.freeze
|
114
|
+
|
115
|
+
##
|
116
|
+
# Context key for all unmatched args in order. The value is an array of
|
117
|
+
# strings.
|
118
|
+
# @return [Object]
|
119
|
+
#
|
120
|
+
UNMATCHED_ARGS = ::Object.new.freeze
|
121
|
+
|
122
|
+
##
|
123
|
+
# Context key for unmatched flags. The value is an array of strings.
|
124
|
+
# @return [Object]
|
125
|
+
#
|
126
|
+
UNMATCHED_FLAGS = ::Object.new.freeze
|
127
|
+
|
128
|
+
##
|
129
|
+
# Context key for unmatched positional args. The value is an array of
|
130
|
+
# strings.
|
131
|
+
# @return [Object]
|
132
|
+
#
|
133
|
+
UNMATCHED_POSITIONAL = ::Object.new.freeze
|
134
|
+
|
135
|
+
##
|
136
|
+
# Context key for the list of usage errors raised. The value is an array
|
137
|
+
# of {Toys::ArgParser::UsageError}.
|
138
|
+
# @return [Object]
|
139
|
+
#
|
140
|
+
USAGE_ERRORS = ::Object.new.freeze
|
141
|
+
|
142
|
+
##
|
143
|
+
# Context key for the verbosity value. The value is an integer defaulting
|
144
|
+
# to 0, with higher values meaning more verbose and lower meaning more
|
145
|
+
# quiet.
|
146
|
+
# @return [Object]
|
147
|
+
#
|
148
|
+
VERBOSITY = ::Object.new.freeze
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Create a Context object. Applications generally will not need to create
|
153
|
+
# these objects directly; they are created by the tool when it is preparing
|
154
|
+
# for execution.
|
155
|
+
#
|
156
|
+
# @private
|
157
|
+
#
|
158
|
+
# @param data [Hash]
|
159
|
+
#
|
160
|
+
def initialize(data)
|
161
|
+
@__data = data
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# The raw arguments passed to the tool, as an array of strings.
|
166
|
+
# This does not include the tool name itself.
|
167
|
+
#
|
168
|
+
# This is a convenience getter for {Toys::Context::Key::ARGS}.
|
169
|
+
#
|
170
|
+
# @return [Array<String>]
|
171
|
+
#
|
172
|
+
def args
|
173
|
+
@__data[Key::ARGS]
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# The currently running CLI.
|
178
|
+
#
|
179
|
+
# This is a convenience getter for {Toys::Context::Key::CLI}.
|
180
|
+
#
|
181
|
+
# @return [Toys::CLI]
|
182
|
+
#
|
183
|
+
def cli
|
184
|
+
@__data[Key::CLI]
|
185
|
+
end
|
186
|
+
|
187
|
+
##
|
188
|
+
# Return the context directory for this tool. Generally, this defaults
|
189
|
+
# to the directory containing the toys config directory structure being
|
190
|
+
# read, but it may be changed by setting a different context directory
|
191
|
+
# for the tool.
|
192
|
+
#
|
193
|
+
# This is a convenience getter for {Toys::Context::Key::CONTEXT_DIRECTORY}.
|
194
|
+
#
|
195
|
+
# @return [String] Context directory path
|
196
|
+
# @return [nil] if there is no context.
|
197
|
+
#
|
198
|
+
def context_directory
|
199
|
+
@__data[Key::CONTEXT_DIRECTORY]
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# The logger for this execution.
|
204
|
+
#
|
205
|
+
# This is a convenience getter for {Toys::Context::Key::LOGGER}.
|
206
|
+
#
|
207
|
+
# @return [Logger]
|
208
|
+
#
|
209
|
+
def logger
|
210
|
+
@__data[Key::LOGGER]
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# The full name of the tool being executed, as an array of strings.
|
215
|
+
#
|
216
|
+
# This is a convenience getter for {Toys::Context::Key::TOOL_NAME}.
|
217
|
+
#
|
218
|
+
# @return [Array<String>]
|
219
|
+
#
|
220
|
+
def tool_name
|
221
|
+
@__data[Key::TOOL_NAME]
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# The source of the tool being executed.
|
226
|
+
#
|
227
|
+
# This is a convenience getter for {Toys::Context::Key::TOOL_SOURCE}.
|
228
|
+
#
|
229
|
+
# @return [Toys::SourceInfo]
|
230
|
+
#
|
231
|
+
def tool_source
|
232
|
+
@__data[Key::TOOL_SOURCE]
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# The (possibly empty) array of errors detected during argument parsing.
|
237
|
+
#
|
238
|
+
# This is a convenience getter for {Toys::Context::Key::USAGE_ERRORS}.
|
239
|
+
#
|
240
|
+
# @return [Array<Toys::ArgParser::UsageError>]
|
241
|
+
#
|
242
|
+
def usage_errors
|
243
|
+
@__data[Key::USAGE_ERRORS]
|
244
|
+
end
|
245
|
+
|
246
|
+
##
|
247
|
+
# The current verbosity setting as an integer.
|
248
|
+
#
|
249
|
+
# This is a convenience getter for {Toys::Context::Key::VERBOSITY}.
|
250
|
+
#
|
251
|
+
# @return [Integer]
|
252
|
+
#
|
253
|
+
def verbosity
|
254
|
+
@__data[Key::VERBOSITY]
|
255
|
+
end
|
256
|
+
|
257
|
+
##
|
258
|
+
# Fetch an option or other piece of data by key.
|
259
|
+
#
|
260
|
+
# @param key [Symbol]
|
261
|
+
# @return [Object]
|
262
|
+
#
|
263
|
+
def [](key)
|
264
|
+
@__data[key]
|
265
|
+
end
|
266
|
+
alias get []
|
267
|
+
alias __get []
|
268
|
+
|
269
|
+
##
|
270
|
+
# Set an option or other piece of context data by key.
|
271
|
+
#
|
272
|
+
# @param key [Symbol]
|
273
|
+
# @param value [Object]
|
274
|
+
#
|
275
|
+
def []=(key, value)
|
276
|
+
@__data[key] = value
|
277
|
+
end
|
278
|
+
|
279
|
+
##
|
280
|
+
# Set one or more options or other context data by key.
|
281
|
+
#
|
282
|
+
# @return [self]
|
283
|
+
#
|
284
|
+
# @overload set(key, value)
|
285
|
+
# Set an option or other piece of context data by key.
|
286
|
+
# @param key [Symbol]
|
287
|
+
# @param value [Object]
|
288
|
+
# @return [self]
|
289
|
+
#
|
290
|
+
# @overload set(hash)
|
291
|
+
# Set multiple content data keys and values
|
292
|
+
# @param hash [Hash] The keys and values to set
|
293
|
+
# @return [self]
|
294
|
+
#
|
295
|
+
def set(key, value = nil)
|
296
|
+
if key.is_a?(::Hash)
|
297
|
+
@__data.merge!(key)
|
298
|
+
else
|
299
|
+
@__data[key] = value
|
300
|
+
end
|
301
|
+
self
|
302
|
+
end
|
303
|
+
|
304
|
+
##
|
305
|
+
# The subset of the context that uses string or symbol keys. By convention,
|
306
|
+
# this includes keys that are set by tool flags and arguments, but does not
|
307
|
+
# include well-known context values such as verbosity or private context
|
308
|
+
# values used by middleware or mixins.
|
309
|
+
#
|
310
|
+
# @return [Hash]
|
311
|
+
#
|
312
|
+
def options
|
313
|
+
@__data.select do |k, _v|
|
314
|
+
k.is_a?(::Symbol) || k.is_a?(::String)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Find the given data file or directory in this tool's search path.
|
320
|
+
#
|
321
|
+
# @param path [String] The path to find
|
322
|
+
# @param type [nil,:file,:directory] Type of file system object to find,
|
323
|
+
# or nil to return any type.
|
324
|
+
#
|
325
|
+
# @return [String] Absolute path of the result
|
326
|
+
# @return [nil] if the data was not found.
|
327
|
+
#
|
328
|
+
def find_data(path, type: nil)
|
329
|
+
@__data[Key::TOOL_SOURCE].find_data(path, type: type)
|
330
|
+
end
|
331
|
+
|
332
|
+
##
|
333
|
+
# Exit immediately with the given status code
|
334
|
+
#
|
335
|
+
# @param code [Integer] The status code, which should be 0 for no error,
|
336
|
+
# or nonzero for an error condition. Default is 0.
|
337
|
+
# @return [void]
|
338
|
+
#
|
339
|
+
def exit(code = 0)
|
340
|
+
throw :result, code
|
341
|
+
end
|
342
|
+
|
343
|
+
##
|
344
|
+
# Exit immediately with the given status code
|
345
|
+
#
|
346
|
+
# @param code [Integer] The status code, which should be 0 for no error,
|
347
|
+
# or nonzero for an error condition. Default is 0.
|
348
|
+
# @return [void]
|
349
|
+
#
|
350
|
+
def self.exit(code = 0)
|
351
|
+
throw :result, code
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
data/lib/toys/core_version.rb
CHANGED
@@ -1,38 +1,30 @@
|
|
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
|
33
25
|
##
|
34
|
-
# Current version of Toys core
|
26
|
+
# Current version of Toys core.
|
35
27
|
# @return [String]
|
36
28
|
#
|
37
|
-
CORE_VERSION = "0.
|
29
|
+
CORE_VERSION = "0.8.0"
|
38
30
|
end
|
data/lib/toys/dsl/flag.rb
CHANGED
@@ -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
|
@@ -38,12 +30,23 @@ module Toys
|
|
38
30
|
# These directives are available inside a block passed to
|
39
31
|
# {Toys::DSL::Tool#flag}.
|
40
32
|
#
|
33
|
+
# ## Example
|
34
|
+
#
|
35
|
+
# tool "mytool" do
|
36
|
+
# flag :value do
|
37
|
+
# # The directives in here are defined by this class
|
38
|
+
# flags "--value=VAL"
|
39
|
+
# accept Integer
|
40
|
+
# desc "An integer value"
|
41
|
+
# end
|
42
|
+
# # ...
|
43
|
+
# end
|
44
|
+
#
|
41
45
|
class Flag
|
42
46
|
## @private
|
43
|
-
def initialize(flags,
|
44
|
-
group, desc, long_desc, display_name)
|
47
|
+
def initialize(flags, acceptor, default, handler, flag_completion, value_completion,
|
48
|
+
report_collisions, group, desc, long_desc, display_name)
|
45
49
|
@flags = flags
|
46
|
-
@accept = accept
|
47
50
|
@default = default
|
48
51
|
@handler = handler
|
49
52
|
@report_collisions = report_collisions
|
@@ -51,36 +54,98 @@ module Toys
|
|
51
54
|
@desc = desc
|
52
55
|
@long_desc = long_desc || []
|
53
56
|
@display_name = display_name
|
57
|
+
accept(acceptor)
|
58
|
+
complete_flags(flag_completion)
|
59
|
+
complete_values(value_completion)
|
54
60
|
end
|
55
61
|
|
56
62
|
##
|
57
63
|
# Add flags in OptionParser format. This may be called multiple times,
|
58
64
|
# and the results are cumulative.
|
59
65
|
#
|
60
|
-
#
|
61
|
-
#
|
66
|
+
# Following are examples of valid syntax.
|
67
|
+
#
|
68
|
+
# * `-a` : A short boolean switch. When this appears as an argument,
|
69
|
+
# the value is set to `true`.
|
70
|
+
# * `--abc` : A long boolean switch. When this appears as an argument,
|
71
|
+
# the value is set to `true`.
|
72
|
+
# * `-aVAL` or `-a VAL` : A short flag that takes a required value.
|
73
|
+
# These two forms are treated identically. If this argument appears
|
74
|
+
# with a value attached (e.g. `-afoo`), the attached string (e.g.
|
75
|
+
# `"foo"`) is taken as the value. Otherwise, the following argument
|
76
|
+
# is taken as the value (e.g. for `-a foo`, the value is set to
|
77
|
+
# `"foo"`.) The following argument is treated as the value even if it
|
78
|
+
# looks like a flag (e.g. `-a -a` causes the string `"-a"` to be
|
79
|
+
# taken as the value.)
|
80
|
+
# * `-a[VAL]` : A short flag that takes an optional value. If this
|
81
|
+
# argument appears with a value attached (e.g. `-afoo`), the attached
|
82
|
+
# string (e.g. `"foo"`) is taken as the value. Otherwise, the value
|
83
|
+
# is set to `true`. The following argument is never interpreted as
|
84
|
+
# the value. (Compare with `-a [VAL]`.)
|
85
|
+
# * `-a [VAL]` : A short flag that takes an optional value. If this
|
86
|
+
# argument appears with a value attached (e.g. `-afoo`), the attached
|
87
|
+
# string (e.g. `"foo"`) is taken as the value. Otherwise, if the
|
88
|
+
# following argument does not look like a flag (i.e. it does not
|
89
|
+
# begin with a hyphen), it is taken as the value. (e.g. `-a foo`
|
90
|
+
# causes the string `"foo"` to be taken as the value.). If there is
|
91
|
+
# no following argument, or the following argument looks like a flag,
|
92
|
+
# the value is set to `true`. (Compare with `-a[VAL]`.)
|
93
|
+
# * `--abc=VAL` or `--abc VAL` : A long flag that takes a required
|
94
|
+
# value. These two forms are treated identically. If this argument
|
95
|
+
# appears with a value attached (e.g. `--abc=foo`), the attached
|
96
|
+
# string (e.g. `"foo"`) is taken as the value. Otherwise, the
|
97
|
+
# following argument is taken as the value (e.g. for `--abc foo`, the
|
98
|
+
# value is set to `"foo"`.) The following argument is treated as the
|
99
|
+
# value even if it looks like a flag (e.g. `--abc --abc` causes the
|
100
|
+
# string `"--abc"` to be taken as the value.)
|
101
|
+
# * `--abc[=VAL]` : A long flag that takes an optional value. If this
|
102
|
+
# argument appears with a value attached (e.g. `--abc=foo`), the
|
103
|
+
# attached string (e.g. `"foo"`) is taken as the value. Otherwise,
|
104
|
+
# the value is set to `true`. The following argument is never
|
105
|
+
# interpreted as the value. (Compare with `--abc [VAL]`.)
|
106
|
+
# * `--abc [VAL]` : A long flag that takes an optional value. If this
|
107
|
+
# argument appears with a value attached (e.g. `--abc=foo`), the
|
108
|
+
# attached string (e.g. `"foo"`) is taken as the value. Otherwise, if
|
109
|
+
# the following argument does not look like a flag (i.e. it does not
|
110
|
+
# begin with a hyphen), it is taken as the value. (e.g. `--abc foo`
|
111
|
+
# causes the string `"foo"` to be taken as the value.). If there is
|
112
|
+
# no following argument, or the following argument looks like a flag,
|
113
|
+
# the value is set to `true`. (Compare with `--abc=[VAL]`.)
|
114
|
+
# * `--[no-]abc` : A long boolean switch that can be turned either on
|
115
|
+
# or off. This effectively creates two flags, `--abc` which sets the
|
116
|
+
# value to `true`, and `--no-abc` which sets the falue to `false`.
|
117
|
+
#
|
118
|
+
# @param flags [String...]
|
119
|
+
# @return [self]
|
62
120
|
#
|
63
121
|
def flags(*flags)
|
64
|
-
@flags += flags
|
122
|
+
@flags += flags.flatten
|
65
123
|
self
|
66
124
|
end
|
67
125
|
|
68
126
|
##
|
69
|
-
# Set the
|
127
|
+
# Set the acceptor for this flag's values.
|
128
|
+
# You can pass either the string name of an acceptor defined in this tool
|
129
|
+
# or any of its ancestors, or any other specification recognized by
|
130
|
+
# {Toys::Acceptor.create}.
|
70
131
|
#
|
71
|
-
# @param [Object]
|
72
|
-
# @
|
132
|
+
# @param spec [Object]
|
133
|
+
# @param options [Hash]
|
134
|
+
# @param block [Proc]
|
135
|
+
# @return [self]
|
73
136
|
#
|
74
|
-
def accept(
|
75
|
-
@
|
137
|
+
def accept(spec = nil, **options, &block)
|
138
|
+
@acceptor_spec = spec
|
139
|
+
@acceptor_options = options
|
140
|
+
@acceptor_block = block
|
76
141
|
self
|
77
142
|
end
|
78
143
|
|
79
144
|
##
|
80
145
|
# Set the default value.
|
81
146
|
#
|
82
|
-
# @param [Object]
|
83
|
-
# @return [
|
147
|
+
# @param default [Object]
|
148
|
+
# @return [self]
|
84
149
|
#
|
85
150
|
def default(default)
|
86
151
|
@default = default
|
@@ -94,20 +159,63 @@ module Toys
|
|
94
159
|
# should be set. You may pass the handler as a Proc (or an object
|
95
160
|
# responding to the `call` method) or you may pass a block.
|
96
161
|
#
|
97
|
-
# @param [Proc]
|
98
|
-
# @
|
162
|
+
# @param handler [Proc]
|
163
|
+
# @param block [Proc]
|
164
|
+
# @return [self]
|
99
165
|
#
|
100
166
|
def handler(handler = nil, &block)
|
101
167
|
@handler = handler || block
|
102
168
|
self
|
103
169
|
end
|
104
170
|
|
171
|
+
##
|
172
|
+
# Set the shell completion strategy for flag names.
|
173
|
+
# You can pass one of the following:
|
174
|
+
#
|
175
|
+
# * The string name of a completion defined in this tool or any of its
|
176
|
+
# ancestors.
|
177
|
+
# * A hash of options to pass to the constructor of
|
178
|
+
# {Toys::Flag::DefaultCompletion}.
|
179
|
+
# * `nil` or `:default` to select the standard completion strategy
|
180
|
+
# (which is {Toys::Flag::DefaultCompletion} with no extra options).
|
181
|
+
# * Any other specification recognized by {Toys::Completion.create}.
|
182
|
+
#
|
183
|
+
# @param spec [Object]
|
184
|
+
# @param options [Hash]
|
185
|
+
# @param block [Proc]
|
186
|
+
# @return [self]
|
187
|
+
#
|
188
|
+
def complete_flags(spec = nil, **options, &block)
|
189
|
+
@flag_completion_spec = spec
|
190
|
+
@flag_completion_options = options
|
191
|
+
@flag_completion_block = block
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
##
|
196
|
+
# Set the shell completion strategy for flag values.
|
197
|
+
# You can pass either the string name of a completion defined in this
|
198
|
+
# tool or any of its ancestors, or any other specification recognized by
|
199
|
+
# {Toys::Completion.create}.
|
200
|
+
#
|
201
|
+
# @param spec [Object]
|
202
|
+
# @param options [Hash]
|
203
|
+
# @param block [Proc]
|
204
|
+
# @return [self]
|
205
|
+
#
|
206
|
+
def complete_values(spec = nil, **options, &block)
|
207
|
+
@value_completion_spec = spec
|
208
|
+
@value_completion_options = options
|
209
|
+
@value_completion_block = block
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
105
213
|
##
|
106
214
|
# Set whether to raise an exception if a flag is requested that is
|
107
215
|
# already in use or marked as disabled.
|
108
216
|
#
|
109
|
-
# @param [Boolean]
|
110
|
-
# @return [
|
217
|
+
# @param setting [Boolean]
|
218
|
+
# @return [self]
|
111
219
|
#
|
112
220
|
def report_collisions(setting)
|
113
221
|
@report_collisions = setting
|
@@ -115,11 +223,36 @@ module Toys
|
|
115
223
|
end
|
116
224
|
|
117
225
|
##
|
118
|
-
# Set the short description
|
119
|
-
#
|
226
|
+
# Set the short description for the current flag. The short description
|
227
|
+
# is displayed with the flag in online help.
|
228
|
+
#
|
229
|
+
# The description is a {Toys::WrappableString}, which may be word-wrapped
|
230
|
+
# when displayed in a help screen. You may pass a {Toys::WrappableString}
|
231
|
+
# directly to this method, or you may pass any input that can be used to
|
232
|
+
# construct a wrappable string:
|
233
|
+
#
|
234
|
+
# * If you pass a String, its whitespace will be compacted (i.e. tabs,
|
235
|
+
# newlines, and multiple consecutive whitespace will be turned into a
|
236
|
+
# single space), and it will be word-wrapped on whitespace.
|
237
|
+
# * If you pass an Array of Strings, each string will be considered a
|
238
|
+
# literal word that cannot be broken, and wrapping will be done
|
239
|
+
# across the strings in the array. In this case, whitespace is not
|
240
|
+
# compacted.
|
241
|
+
#
|
242
|
+
# ## Examples
|
120
243
|
#
|
121
|
-
#
|
122
|
-
#
|
244
|
+
# If you pass in a sentence as a simple string, it may be word wrapped
|
245
|
+
# when displayed:
|
246
|
+
#
|
247
|
+
# desc "This sentence may be wrapped."
|
248
|
+
#
|
249
|
+
# To specify a sentence that should never be word-wrapped, pass it as the
|
250
|
+
# sole element of a string array:
|
251
|
+
#
|
252
|
+
# desc ["This sentence will not be wrapped."]
|
253
|
+
#
|
254
|
+
# @param desc [String,Array<String>,Toys::WrappableString]
|
255
|
+
# @return [self]
|
123
256
|
#
|
124
257
|
def desc(desc)
|
125
258
|
@desc = desc
|
@@ -127,12 +260,27 @@ module Toys
|
|
127
260
|
end
|
128
261
|
|
129
262
|
##
|
130
|
-
#
|
131
|
-
# the
|
132
|
-
#
|
263
|
+
# Add to the long description for the current flag. The long description
|
264
|
+
# is displayed with the flag in online help. This directive may be given
|
265
|
+
# multiple times, and the results are cumulative.
|
266
|
+
#
|
267
|
+
# A long description is a series of descriptions, which are generally
|
268
|
+
# displayed in a series of lines/paragraphs. Each individual description
|
269
|
+
# uses the form described in the {#desc} documentation, and may be
|
270
|
+
# word-wrapped when displayed. To insert a blank line, include an empty
|
271
|
+
# string as one of the descriptions.
|
272
|
+
#
|
273
|
+
# ## Example
|
274
|
+
#
|
275
|
+
# long_desc "This initial paragraph might get word wrapped.",
|
276
|
+
# "This next paragraph is followed by a blank line.",
|
277
|
+
# "",
|
278
|
+
# ["This line will not be wrapped."],
|
279
|
+
# [" This indent is preserved."]
|
280
|
+
# long_desc "This line is appended to the description."
|
133
281
|
#
|
134
|
-
# @param [String,Array<String>,Toys::
|
135
|
-
# @return [
|
282
|
+
# @param long_desc [String,Array<String>,Toys::WrappableString...]
|
283
|
+
# @return [self]
|
136
284
|
#
|
137
285
|
def long_desc(*long_desc)
|
138
286
|
@long_desc += long_desc
|
@@ -143,8 +291,8 @@ module Toys
|
|
143
291
|
# Set the group. A group may be set by name or group object. Setting
|
144
292
|
# `nil` selects the default group.
|
145
293
|
#
|
146
|
-
# @param [String,Symbol,Toys::
|
147
|
-
# @return [
|
294
|
+
# @param group [String,Symbol,Toys::FlagGroup,nil]
|
295
|
+
# @return [self]
|
148
296
|
#
|
149
297
|
def group(group)
|
150
298
|
@group = group
|
@@ -152,10 +300,11 @@ module Toys
|
|
152
300
|
end
|
153
301
|
|
154
302
|
##
|
155
|
-
# Set the display name. This may be used in help text and
|
303
|
+
# Set the display name for this flag. This may be used in help text and
|
304
|
+
# error messages.
|
156
305
|
#
|
157
|
-
# @param [String]
|
158
|
-
# @return [
|
306
|
+
# @param display_name [String]
|
307
|
+
# @return [self]
|
159
308
|
#
|
160
309
|
def display_name(display_name)
|
161
310
|
@display_name = display_name
|
@@ -164,8 +313,16 @@ module Toys
|
|
164
313
|
|
165
314
|
## @private
|
166
315
|
def _add_to(tool, key)
|
316
|
+
acceptor = tool.scalar_acceptor(@acceptor_spec, @acceptor_options, &@acceptor_block)
|
317
|
+
flag_completion = tool.scalar_completion(
|
318
|
+
@flag_completion_spec, @flag_completion_options, &@flag_completion_block
|
319
|
+
)
|
320
|
+
value_completion = tool.scalar_completion(
|
321
|
+
@value_completion_spec, @value_completion_options, &@value_completion_block
|
322
|
+
)
|
167
323
|
tool.add_flag(key, @flags,
|
168
|
-
accept:
|
324
|
+
accept: acceptor, default: @default, handler: @handler,
|
325
|
+
complete_flags: flag_completion, complete_values: value_completion,
|
169
326
|
report_collisions: @report_collisions, group: @group,
|
170
327
|
desc: @desc, long_desc: @long_desc, display_name: @display_name)
|
171
328
|
end
|