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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE.md +16 -24
  4. data/README.md +307 -59
  5. data/docs/guide.md +44 -4
  6. data/lib/toys-core.rb +58 -49
  7. data/lib/toys/acceptor.rb +672 -0
  8. data/lib/toys/alias.rb +106 -0
  9. data/lib/toys/arg_parser.rb +624 -0
  10. data/lib/toys/cli.rb +422 -181
  11. data/lib/toys/compat.rb +83 -0
  12. data/lib/toys/completion.rb +442 -0
  13. data/lib/toys/context.rb +354 -0
  14. data/lib/toys/core_version.rb +18 -26
  15. data/lib/toys/dsl/flag.rb +213 -56
  16. data/lib/toys/dsl/flag_group.rb +237 -51
  17. data/lib/toys/dsl/positional_arg.rb +210 -0
  18. data/lib/toys/dsl/tool.rb +968 -317
  19. data/lib/toys/errors.rb +46 -28
  20. data/lib/toys/flag.rb +821 -0
  21. data/lib/toys/flag_group.rb +282 -0
  22. data/lib/toys/input_file.rb +18 -26
  23. data/lib/toys/loader.rb +110 -100
  24. data/lib/toys/middleware.rb +24 -31
  25. data/lib/toys/mixin.rb +90 -59
  26. data/lib/toys/module_lookup.rb +125 -0
  27. data/lib/toys/positional_arg.rb +184 -0
  28. data/lib/toys/source_info.rb +192 -0
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
  32. data/lib/toys/standard_middleware/show_help.rb +130 -113
  33. data/lib/toys/standard_middleware/show_root_version.rb +29 -35
  34. data/lib/toys/standard_mixins/exec.rb +116 -78
  35. data/lib/toys/standard_mixins/fileutils.rb +16 -24
  36. data/lib/toys/standard_mixins/gems.rb +29 -30
  37. data/lib/toys/standard_mixins/highline.rb +34 -41
  38. data/lib/toys/standard_mixins/terminal.rb +72 -26
  39. data/lib/toys/template.rb +51 -35
  40. data/lib/toys/tool.rb +1161 -206
  41. data/lib/toys/utils/completion_engine.rb +171 -0
  42. data/lib/toys/utils/exec.rb +279 -182
  43. data/lib/toys/utils/gems.rb +58 -49
  44. data/lib/toys/utils/help_text.rb +117 -111
  45. data/lib/toys/utils/terminal.rb +69 -62
  46. data/lib/toys/wrappable_string.rb +162 -0
  47. metadata +24 -22
  48. data/lib/toys/definition/acceptor.rb +0 -191
  49. data/lib/toys/definition/alias.rb +0 -112
  50. data/lib/toys/definition/arg.rb +0 -140
  51. data/lib/toys/definition/flag.rb +0 -370
  52. data/lib/toys/definition/flag_group.rb +0 -205
  53. data/lib/toys/definition/source_info.rb +0 -190
  54. data/lib/toys/definition/tool.rb +0 -842
  55. data/lib/toys/dsl/arg.rb +0 -132
  56. data/lib/toys/runner.rb +0 -188
  57. data/lib/toys/standard_middleware.rb +0 -47
  58. data/lib/toys/utils/module_lookup.rb +0 -135
  59. data/lib/toys/utils/wrappable_string.rb +0 -165
@@ -1,32 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2018 Daniel Azuma
3
+ # Copyright 2019 Daniel Azuma
4
4
  #
5
- # All rights reserved.
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
- # Redistribution and use in source and binary forms, with or without
8
- # modification, are permitted provided that the following conditions are met:
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
- # * Redistributions of source code must retain the above copyright notice,
11
- # this list of conditions and the following disclaimer.
12
- # * Redistributions in binary form must reproduce the above copyright notice,
13
- # this list of conditions and the following disclaimer in the documentation
14
- # and/or other materials provided with the distribution.
15
- # * Neither the name of the copyright holder, nor the names of any other
16
- # contributors to this software, may be used to endorse or promote products
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,121 +61,251 @@ module Toys
69
61
  end
70
62
 
71
63
  ##
72
- # Create an acceptor that can be passed into a flag or arg. An acceptor
73
- # validates and/or converts a string parameter to a Ruby object. This
74
- # acceptor may, for the current tool, be referenced by the name you provide
75
- # when you create a flag or arg.
64
+ # Create a named acceptor that can be referenced by name from any flag or
65
+ # positional argument in this tool or its subtools.
66
+ #
67
+ # An acceptor validates the string parameter passed to a flag or
68
+ # positional argument. It also optionally converts the string to a
69
+ # different object before storing it in your tool's data.
70
+ #
71
+ # Acceptors can be defined in one of four ways.
72
+ #
73
+ # * You can provide a **regular expression**. This acceptor validates
74
+ # only if the regex matches the *entire string parameter*.
75
+ #
76
+ # You can also provide an optional conversion function as a block. If
77
+ # provided, function must take a variable number of arguments, the
78
+ # first being the matched string and the remainder being the captures
79
+ # from the regular expression. It should return the converted object
80
+ # that will be stored in the context data. If you do not provide a
81
+ # block, the original string will be used.
82
+ #
83
+ # * You can provide an **array** of possible values. The acceptor
84
+ # validates if the string parameter matches the *string form* of one
85
+ # of the array elements (i.e. the results of calling `to_s` on the
86
+ # array elements.)
87
+ #
88
+ # An array acceptor automatically converts the string parameter to
89
+ # the actual array element that it matched. For example, if the
90
+ # symbol `:foo` is in the array, it will match the string `"foo"`,
91
+ # and then store the symbol `:foo` in the tool data.
92
+ #
93
+ # * You can provide a **range** of possible values, along with a
94
+ # conversion function that converts a string parameter to a type
95
+ # comparable by the range. (See the "function" spec below for a
96
+ # detailed description of conversion functions.) If the range has
97
+ # numeric endpoints, the conversion function is optional because a
98
+ # default will be provided.
99
+ #
100
+ # * You can provide a **function** by passing it as a proc or a block.
101
+ # This function performs *both* validation and conversion. It should
102
+ # take the string parameter as its argument, and it must either
103
+ # return the object that should be stored in the tool data, or raise
104
+ # an exception (descended from `StandardError`) to indicate that the
105
+ # string parameter is invalid.
106
+ #
107
+ # ## Example
108
+ #
109
+ # The following example creates an acceptor named "hex" that is defined
110
+ # via a regular expression. It then uses it to validate values passed to
111
+ # a flag.
112
+ #
113
+ # tool "example" do
114
+ # acceptor "hex", /[0-9a-fA-F]+/, type_desc: "hex numbers"
115
+ # flag :number, accept: "hex"
116
+ # def run
117
+ # puts "number was #{number}"
118
+ # end
119
+ # end
120
+ #
121
+ # @param name [String] The acceptor name.
122
+ # @param spec [Object] See the description for recognized values.
123
+ # @param type_desc [String] Type description string, shown in help.
124
+ # Defaults to the acceptor name.
125
+ # @param block [Proc] See the description for recognized forms.
126
+ # @return [self]
127
+ #
128
+ def acceptor(name, spec = nil, type_desc: nil, &block)
129
+ cur_tool = DSL::Tool.current_tool(self, false)
130
+ cur_tool&.add_acceptor(name, spec, type_desc: type_desc || name.to_s, &block)
131
+ self
132
+ end
133
+
134
+ ##
135
+ # Create a named mixin module that can be included by name from this tool
136
+ # or its subtools.
76
137
  #
77
- # An acceptor contains a validator, which parses and validates the string
78
- # syntax of an argument, and a converter, which takes the validation
79
- # results and returns a final value for the context data.
138
+ # A mixin is a module that defines methods that can be called from a
139
+ # tool. It is commonly used to provide "utility" methods, implementing
140
+ # common functionality and allowing tools to share code.
80
141
  #
81
- # The validator may be either a regular expression or a list of valid
82
- # inputs.
142
+ # Normally you provide a block and define the mixin's methods in that
143
+ # block. Alternatively, you can create a module separately and pass it
144
+ # directly to this directive.
83
145
  #
84
- # If the validator is a regular expression, it is matched against the
85
- # argument string and succeeds only if the expression covers the *entire*
86
- # string. The elements of the MatchData (i.e. the string matched, plus any
87
- # captures) are then passed into the conversion function.
146
+ # ## Example
88
147
  #
89
- # If the validator is an array, the *string form* of the array elements
90
- # (i.e. the results of calling to_s on each element) are considered the
91
- # valid values for the argument. This is useful for enums, for example.
92
- # In this case, the input is converted to the original array element, and
93
- # any converter function you provide is ignored.
148
+ # The following example creates a named mixin and uses it in a tool.
94
149
  #
95
- # If you provide no validator, then no validation takes place and all
96
- # argument strings are considered valid. The string itself is passed on to
97
- # the converter.
150
+ # mixin "error-reporter" do
151
+ # def error message
152
+ # logger.error "An error occurred: #{message}"
153
+ # exit 1
154
+ # end
155
+ # end
98
156
  #
99
- # The converter should be a proc that takes as its arguments the results
100
- # of validation. For example, if you use a regular expression validator,
101
- # the converter should take a series of strings arguments, the first of
102
- # which is the full input string, and the rest of which are captures.
103
- # If you provide no converter, no conversion is done and the input string
104
- # is considered the final value. You may also provide the converter as a
105
- # block.
157
+ # tool "build" do
158
+ # include "error-reporter"
159
+ # def run
160
+ # puts "Building..."
161
+ # error "Build failed!"
162
+ # end
163
+ # end
106
164
  #
107
- # @param [String] name The acceptor name.
108
- # @param [Regexp,Array,nil] validator The validator.
109
- # @param [Proc,nil] converter The validator.
110
- # @return [Toys::DSL::Tool] self, for chaining.
165
+ # @param name [String] Name of the mixin
166
+ # @param mixin_module [Module] Module to use as the mixin. Optional.
167
+ # Either pass a module here, *or* provide a block and define the
168
+ # mixin within the block.
169
+ # @param block [Proc] Defines the mixin module.
170
+ # @return [self]
111
171
  #
112
- def acceptor(name, validator = nil, converter = nil, &block)
172
+ def mixin(name, mixin_module = nil, &block)
113
173
  cur_tool = DSL::Tool.current_tool(self, false)
114
- return self if cur_tool.nil?
115
- accept =
116
- case validator
117
- when ::Regexp
118
- Definition::PatternAcceptor.new(name, validator, converter, &block)
119
- when ::Array
120
- Definition::EnumAcceptor.new(name, validator)
121
- when nil
122
- Definition::Acceptor.new(name, converter, &block)
123
- else
124
- raise ToolDefinitionError, "Illegal validator: #{validator.inspect}"
125
- end
126
- cur_tool.add_acceptor(accept)
174
+ cur_tool&.add_mixin(name, mixin_module, &block)
127
175
  self
128
176
  end
129
177
 
130
178
  ##
131
- # Create a named mixin module.
132
- # This module may be included by name in this tool or any subtool.
179
+ # Create a named template that can be expanded by name from this tool
180
+ # or its subtools.
181
+ #
182
+ # A template is an object that generates DSL directives. You can use it
183
+ # to build "prefabricated" tools, and then instantiate them in your Toys
184
+ # files. Generally, a template is a class with an associated `expansion`
185
+ # procedure. The class defines parameters for the template expansion,
186
+ # and `expansion` includes DSL directives that should be run based on
187
+ # those parameters.
188
+ #
189
+ # Normally, you provide a block and define the template class in that
190
+ # block. Most templates will define an `initialize` method that takes any
191
+ # arguments passed into the template expansion. The template must also
192
+ # provide an `expansion` block showing how to use the template object to
193
+ # produce DSL directives.
194
+ #
195
+ # Alternately, you can create a template class separately and pass it
196
+ # directly. See {Toys::Template} for details on creating a template
197
+ # class.
198
+ #
199
+ # ## Example
200
+ #
201
+ # The following example creates and uses a simple template.
202
+ #
203
+ # template "hello-generator" do
204
+ # def initialize(name, message)
205
+ # @name = name
206
+ # @message = message
207
+ # end
208
+ # attr_reader :name, :message
209
+ # expansion do |template|
210
+ # tool template.name do
211
+ # to_run do
212
+ # puts template.message
213
+ # end
214
+ # end
215
+ # end
216
+ # end
133
217
  #
134
- # You should pass a block and define methods in that block.
218
+ # expand "hello-generator", "mytool", "mytool is running!"
135
219
  #
136
- # @param [String] name Name of the mixin
137
- # @return [Toys::DSL::Tool] self, for chaining.
220
+ # @param name [String] Name of the template
221
+ # @param template_class [Class] Module to use as the mixin. Optional.
222
+ # Either pass a module here, *or* provide a block and define the
223
+ # mixin within the block.
224
+ # @param block [Proc] Defines the template class.
225
+ # @return [self]
138
226
  #
139
- def mixin(name, &block)
227
+ def template(name, template_class = nil, &block)
140
228
  cur_tool = DSL::Tool.current_tool(self, false)
141
- if cur_tool
142
- mixin_mod = ::Module.new do
143
- include ::Toys::Mixin
144
- end
145
- mixin_mod.module_eval(&block)
146
- cur_tool.add_mixin(name, mixin_mod)
147
- end
229
+ cur_tool&.add_template(name, template_class, &block)
148
230
  self
149
231
  end
150
232
 
151
233
  ##
152
- # Create a named template class.
153
- # This template may be expanded by name in this tool or any subtool.
234
+ # Create a named completion procedure that may be used by name by any
235
+ # flag or positional arg in this tool or any subtool.
236
+ #
237
+ # A completion controls tab completion for the value of a flag or
238
+ # positional argument. In general, it is a Ruby `Proc` that takes a
239
+ # context object (of type {Toys::Completion::Context}) and returns an
240
+ # array of completion candidate strings.
241
+ #
242
+ # Completions can be specified in one of three ways.
243
+ #
244
+ # * A Proc object itself, either passed directly to this directive or
245
+ # provided as a block.
246
+ # * A static array of strings, indicating the completion candidates
247
+ # independent of context.
248
+ # * The symbol `:file_system` which indicates that paths in the file
249
+ # system should serve as completion candidates.
250
+ #
251
+ # ## Example
154
252
  #
155
- # You should pass a block and define the template in that block. You do
156
- # not need to include `Toys::Template` in the block. Otherwise, see
157
- # {Toys::Template} for information on defining a template. In general,
158
- # the block should define an initialize method, and call `to_expand` to
159
- # define how to expand the template.
253
+ # The following example defines a completion that uses only the immediate
254
+ # files in the current directory as candidates. (This is different from
255
+ # the `:file_system` completion which will descend into subdirectories
256
+ # similar to how bash completes most of its file system commands.)
160
257
  #
161
- # @param [String] name Name of the template
162
- # @return [Toys::DSL::Tool] self, for chaining.
258
+ # completion "local-files" do |_context|
259
+ # `/bin/ls`.split("\n")
260
+ # end
261
+ # tool "example" do
262
+ # flag :file, complete_values: "local-files"
263
+ # def run
264
+ # puts "selected file #{file}"
265
+ # end
266
+ # end
267
+ #
268
+ # @param name [String] Name of the completion
269
+ # @param spec [Object] See the description for recognized values.
270
+ # @param options [Hash] Additional options to pass to the completion.
271
+ # @param block [Proc] See the description for recognized forms.
272
+ # @return [self]
163
273
  #
164
- def template(name, &block)
274
+ def completion(name, spec = nil, **options, &block)
165
275
  cur_tool = DSL::Tool.current_tool(self, false)
166
- if cur_tool
167
- template_class = ::Class.new do
168
- include ::Toys::Template
169
- end
170
- template_class.class_eval(&block)
171
- cur_tool.add_template(name, template_class)
172
- end
276
+ cur_tool&.add_completion(name, spec, options, &block)
173
277
  self
174
278
  end
175
279
 
176
280
  ##
177
281
  # Create a subtool. You must provide a block defining the subtool.
178
282
  #
179
- # @param [String,Array<String>] words The name of the subtool
180
- # @param [:combine,:reset,:ignore] if_defined What to do if a definition
283
+ # ## Example
284
+ #
285
+ # The following example defines a tool and two subtools within it.
286
+ #
287
+ # tool "build" do
288
+ # tool "staging" do
289
+ # def run
290
+ # puts "Building staging"
291
+ # end
292
+ # end
293
+ # tool "staging" do
294
+ # def run
295
+ # puts "Building staging"
296
+ # end
297
+ # end
298
+ # end
299
+ #
300
+ # @param words [String,Array<String>] The name of the subtool
301
+ # @param if_defined [:combine,:reset,:ignore] What to do if a definition
181
302
  # already exists for this tool. Possible values are `:combine` (the
182
303
  # default) indicating the definition should be combined with the
183
304
  # existing definition, `:reset` indicating the earlier definition
184
305
  # should be reset and the new definition applied instead, or
185
306
  # `:ignore` indicating the new definition should be ignored.
186
- # @return [Toys::DSL::Tool] self, for chaining.
307
+ # @param block [Proc] Defines the subtool.
308
+ # @return [self]
187
309
  #
188
310
  def tool(words, if_defined: :combine, &block)
189
311
  subtool_words = @__words
@@ -193,7 +315,7 @@ module Toys
193
315
  subtool_words += [word]
194
316
  next_remaining = Loader.next_remaining_words(next_remaining, word)
195
317
  end
196
- subtool = @__loader.get_tool_definition(subtool_words, @__priority)
318
+ subtool = @__loader.get_tool(subtool_words, @__priority)
197
319
  if subtool.includes_definition?
198
320
  case if_defined
199
321
  when :ignore
@@ -213,9 +335,24 @@ module Toys
213
335
  ##
214
336
  # Create an alias in the current namespace.
215
337
  #
216
- # @param [String] word The name of the alias
217
- # @param [String] target The target of the alias
218
- # @return [Toys::DSL::Tool] self, for chaining.
338
+ # An alias is an alternate name for a tool. The referenced tool must be
339
+ # in the same namespace.
340
+ #
341
+ # ## Example
342
+ #
343
+ # This example defines a tool and an alias pointing to it. Both the tool
344
+ # name `test` and the alias `t` will then refer to the same tool.
345
+ #
346
+ # tool "test" do
347
+ # def run
348
+ # puts "Running tests..."
349
+ # end
350
+ # end
351
+ # alias_tool "t", "test"
352
+ #
353
+ # @param word [String] The name of the alias
354
+ # @param target [String] The target of the alias
355
+ # @return [self]
219
356
  #
220
357
  def alias_tool(word, target)
221
358
  @__loader.make_alias(@__words + [word.to_s], @__words + [target.to_s], @__priority)
@@ -226,8 +363,8 @@ module Toys
226
363
  # Load another config file or directory, as if its contents were inserted
227
364
  # at the current location.
228
365
  #
229
- # @param [String] path The file or directory to load.
230
- # @return [Toys::DSL::Tool] self, for chaining.
366
+ # @param path [String] The file or directory to load.
367
+ # @return [self]
231
368
  #
232
369
  def load(path)
233
370
  @__loader.load_path(source_info, path, @__words, @__remaining_words, @__priority)
@@ -240,16 +377,37 @@ module Toys
240
377
  # The template may be specified as a class or a well-known template name.
241
378
  # You may also provide arguments to pass to the template.
242
379
  #
243
- # @param [Class,String,Symbol] template_class The template, either as a
380
+ # ## Example
381
+ #
382
+ # The following example creates and uses a simple template.
383
+ #
384
+ # template "hello-generator" do
385
+ # def initialize(name, message)
386
+ # @name = name
387
+ # @message = message
388
+ # end
389
+ # attr_reader :name, :message
390
+ # expansion do |template|
391
+ # tool template.name do
392
+ # to_run do
393
+ # puts template.message
394
+ # end
395
+ # end
396
+ # end
397
+ # end
398
+ #
399
+ # expand "hello-generator", "mytool", "mytool is running!"
400
+ #
401
+ # @param template_class [Class,String,Symbol] The template, either as a
244
402
  # class or a well-known name.
245
- # @param [Object...] args Template arguments
246
- # @return [Toys::DSL::Tool] self, for chaining.
403
+ # @param args [Object...] Template arguments
404
+ # @return [self]
247
405
  #
248
406
  def expand(template_class, *args)
249
407
  cur_tool = DSL::Tool.current_tool(self, false)
250
408
  name = template_class.to_s
251
409
  if template_class.is_a?(::String)
252
- template_class = cur_tool.resolve_template(template_class)
410
+ template_class = cur_tool.lookup_template(template_class)
253
411
  elsif template_class.is_a?(::Symbol)
254
412
  template_class = @__loader.resolve_standard_template(name)
255
413
  end
@@ -258,29 +416,32 @@ module Toys
258
416
  end
259
417
  template = template_class.new(*args)
260
418
  yield template if block_given?
261
- class_exec(template, &template_class.expander)
419
+ class_exec(template, &template_class.expansion)
262
420
  self
263
421
  end
264
422
 
265
423
  ##
266
- # Set the short description for the current tool. The short description is
267
- # displayed with the tool in a subtool list. You may also use the
424
+ # Set the short description for the current tool. The short description
425
+ # is displayed with the tool in a subtool list. You may also use the
268
426
  # equivalent method `short_desc`.
269
427
  #
270
- # The description is a {Toys::Utils::WrappableString}, which may be word-
271
- # wrapped when displayed in a help screen. You may pass a
272
- # {Toys::Utils::WrappableString} directly to this method, or you may pass
273
- # any input that can be used to construct a wrappable string:
428
+ # The description is a {Toys::WrappableString}, which may be word-wrapped
429
+ # when displayed in a help screen. You may pass a {Toys::WrappableString}
430
+ # directly to this method, or you may pass any input that can be used to
431
+ # construct a wrappable string:
274
432
  #
275
- # * If you pass a String, its whitespace will be compacted (i.e. tabs,
433
+ # * If you pass a String, its whitespace will be compacted (i.e. tabs,
276
434
  # newlines, and multiple consecutive whitespace will be turned into a
277
435
  # single space), and it will be word-wrapped on whitespace.
278
- # * If you pass an Array of Strings, each string will be considered a
279
- # literal word that cannot be broken, and wrapping will be done across
280
- # the strings in the array. In this case, whitespace is not compacted.
436
+ # * If you pass an Array of Strings, each string will be considered a
437
+ # literal word that cannot be broken, and wrapping will be done
438
+ # across the strings in the array. In this case, whitespace is not
439
+ # compacted.
281
440
  #
282
- # For example, if you pass in a sentence as a simple string, it may be
283
- # word wrapped when displayed:
441
+ # ## Examples
442
+ #
443
+ # If you pass in a sentence as a simple string, it may be word wrapped
444
+ # when displayed:
284
445
  #
285
446
  # desc "This sentence may be wrapped."
286
447
  #
@@ -289,8 +450,8 @@ module Toys
289
450
  #
290
451
  # desc ["This sentence will not be wrapped."]
291
452
  #
292
- # @param [Toys::Utils::WrappableString,String,Array<String>] str
293
- # @return [Toys::DSL::Tool] self, for chaining.
453
+ # @param str [Toys::WrappableString,String,Array<String>]
454
+ # @return [self]
294
455
  #
295
456
  def desc(str)
296
457
  cur_tool = DSL::Tool.current_tool(self, true)
@@ -300,24 +461,27 @@ module Toys
300
461
  alias short_desc desc
301
462
 
302
463
  ##
303
- # Set the long description for the current tool. The long description is
304
- # displayed in the usage documentation for the tool itself.
464
+ # Add to the long description for the current tool. The long description
465
+ # is displayed in the usage documentation for the tool itself. This
466
+ # directive may be given multiple times, and the results are cumulative.
305
467
  #
306
468
  # A long description is a series of descriptions, which are generally
307
469
  # displayed in a series of lines/paragraphs. Each individual description
308
- # uses the form described in the {Toys::DSL::Tool#desc} documentation, and
309
- # may be word-wrapped when displayed. To insert a blank line, include an
310
- # empty string as one of the descriptions.
470
+ # uses the form described in the {#desc} documentation, and may be
471
+ # word-wrapped when displayed. To insert a blank line, include an empty
472
+ # string as one of the descriptions.
311
473
  #
312
- # Example:
474
+ # ## Example
313
475
  #
314
- # long_desc "This is an initial paragraph that might be word wrapped.",
476
+ # long_desc "This initial paragraph might get word wrapped.",
315
477
  # "This next paragraph is followed by a blank line.",
316
478
  # "",
317
- # ["This line will not be wrapped."]
479
+ # ["This line will not be wrapped."],
480
+ # [" This indent is preserved."]
481
+ # long_desc "This line is appended to the description."
318
482
  #
319
- # @param [Toys::Utils::WrappableString,String,Array<String>...] strs
320
- # @return [Toys::DSL::Tool] self, for chaining.
483
+ # @param strs [Toys::WrappableString,String,Array<String>...]
484
+ # @return [self]
321
485
  #
322
486
  def long_desc(*strs)
323
487
  DSL::Tool.current_tool(self, true)&.append_long_desc(strs)
@@ -329,33 +493,39 @@ module Toys
329
493
  # belong to the group. The flags in the group are listed together in
330
494
  # help screens.
331
495
  #
332
- # Example:
496
+ # ## Example
497
+ #
498
+ # The following example creates a flag group in which all flags are
499
+ # optional.
333
500
  #
334
- # flag_group desc: "Debug Flags" do
335
- # flag :debug, "-D", desc: "Enable debugger"
336
- # flag :warnings, "-W[VAL]", desc: "Enable warnings"
501
+ # tool "execute" do
502
+ # flag_group desc: "Debug Flags" do
503
+ # flag :debug, "-D", desc: "Enable debugger"
504
+ # flag :warnings, "-W[VAL]", desc: "Enable warnings"
505
+ # end
506
+ # # ...
337
507
  # end
338
508
  #
339
- # @param [Symbol] type The type of group. Allowed values: `:required`,
509
+ # @param type [Symbol] The type of group. Allowed values: `:required`,
340
510
  # `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`.
341
511
  # Default is `:optional`.
342
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
343
- # description for the group. See {Toys::Definition::Tool#desc=} for a
344
- # description of allowed formats. Defaults to `"Flags"`.
345
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
512
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
513
+ # description for the group. See {Toys::Tool#desc=} for a description
514
+ # of allowed formats. Defaults to `"Flags"`.
515
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
346
516
  # Long description for the flag group. See
347
- # {Toys::Definition::Tool#long_desc=} for a description of allowed
348
- # formats. Defaults to the empty array.
349
- # @param [String,Symbol,nil] name The name of the group, or nil for no
517
+ # {Toys::Tool#long_desc=} for a description of allowed formats.
518
+ # Defaults to the empty array.
519
+ # @param name [String,Symbol,nil] The name of the group, or nil for no
350
520
  # name.
351
- # @param [Boolean] report_collisions If `true`, raise an exception if a
521
+ # @param report_collisions [Boolean] If `true`, raise an exception if a
352
522
  # the given name is already taken. If `false`, ignore. Default is
353
523
  # `true`.
354
- # @param [Boolean] prepend If `true`, prepend rather than append the
524
+ # @param prepend [Boolean] If `true`, prepend rather than append the
355
525
  # group to the list. Default is `false`.
356
- # @yieldparam flag_group_dsl [Toys::DSL::FlagGroup] An object that lets
357
- # add flags to this group in a block.
358
- # @return [Toys::DSL::Tool] self, for chaining.
526
+ # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup}
527
+ # for the directives that can be called in this block.
528
+ # @return [self]
359
529
  #
360
530
  def flag_group(type: :optional, desc: nil, long_desc: nil, name: nil,
361
531
  report_collisions: true, prepend: false, &block)
@@ -374,30 +544,35 @@ module Toys
374
544
  # defined in the block belong to the group. All flags in this group are
375
545
  # required.
376
546
  #
377
- # Example:
547
+ # ## Example
548
+ #
549
+ # The following example creates a group of required flags.
378
550
  #
379
- # all_required do
380
- # flag :username, "--username=VAL", desc: "Set the username (required)"
381
- # flag :password, "--password=VAL", desc: "Set the password (required)"
551
+ # tool "login" do
552
+ # all_required do
553
+ # flag :username, "--username=VAL", desc: "Set username (required)"
554
+ # flag :password, "--password=VAL", desc: "Set password (required)"
555
+ # end
556
+ # # ...
382
557
  # end
383
558
  #
384
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
385
- # description for the group. See {Toys::Definition::Tool#desc=} for a
386
- # description of allowed formats. Defaults to `"Flags"`.
387
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
559
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
560
+ # description for the group. See {Toys::Tool#desc=} for a description
561
+ # of allowed formats. Defaults to `"Flags"`.
562
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
388
563
  # Long description for the flag group. See
389
- # {Toys::Definition::Tool#long_desc=} for a description of allowed
390
- # formats. Defaults to the empty array.
391
- # @param [String,Symbol,nil] name The name of the group, or nil for no
564
+ # {Toys::Tool#long_desc=} for a description of allowed formats.
565
+ # Defaults to the empty array.
566
+ # @param name [String,Symbol,nil] The name of the group, or nil for no
392
567
  # name.
393
- # @param [Boolean] report_collisions If `true`, raise an exception if a
568
+ # @param report_collisions [Boolean] If `true`, raise an exception if a
394
569
  # the given name is already taken. If `false`, ignore. Default is
395
570
  # `true`.
396
- # @param [Boolean] prepend If `true`, prepend rather than append the
571
+ # @param prepend [Boolean] If `true`, prepend rather than append the
397
572
  # group to the list. Default is `false`.
398
- # @yieldparam flag_group_dsl [Toys::DSL::FlagGroup] An object that lets
399
- # add flags to this group in a block.
400
- # @return [Toys::DSL::Tool] self, for chaining.
573
+ # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup}
574
+ # for the directives that can be called in this block.
575
+ # @return [self]
401
576
  #
402
577
  def all_required(desc: nil, long_desc: nil, name: nil, report_collisions: true,
403
578
  prepend: false, &block)
@@ -410,87 +585,132 @@ module Toys
410
585
  # defined in the block belong to the group. At most one flag in this
411
586
  # group must be provided on the command line.
412
587
  #
413
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
414
- # description for the group. See {Toys::Definition::Tool#desc=} for a
415
- # description of allowed formats. Defaults to `"Flags"`.
416
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
588
+ # ## Example
589
+ #
590
+ # The following example creates a group of flags in which either one or
591
+ # none may be set, but not more than one.
592
+ #
593
+ # tool "provision-server" do
594
+ # at_most_one do
595
+ # flag :restore_from_backup, "--restore-from-backup=VAL"
596
+ # flag :restore_from_image, "--restore-from-image=VAL"
597
+ # flag :clone_existing, "--clone-existing=VAL"
598
+ # end
599
+ # # ...
600
+ # end
601
+ #
602
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
603
+ # description for the group. See {Toys::Tool#desc=} for a description
604
+ # of allowed formats. Defaults to `"Flags"`.
605
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
417
606
  # Long description for the flag group. See
418
- # {Toys::Definition::Tool#long_desc=} for a description of allowed
419
- # formats. Defaults to the empty array.
420
- # @param [String,Symbol,nil] name The name of the group, or nil for no
607
+ # {Toys::Tool#long_desc=} for a description of allowed formats.
608
+ # Defaults to the empty array.
609
+ # @param name [String,Symbol,nil] The name of the group, or nil for no
421
610
  # name.
422
- # @param [Boolean] report_collisions If `true`, raise an exception if a
611
+ # @param report_collisions [Boolean] If `true`, raise an exception if a
423
612
  # the given name is already taken. If `false`, ignore. Default is
424
613
  # `true`.
425
- # @param [Boolean] prepend If `true`, prepend rather than append the
614
+ # @param prepend [Boolean] If `true`, prepend rather than append the
426
615
  # group to the list. Default is `false`.
427
- # @yieldparam flag_group_dsl [Toys::DSL::FlagGroup] An object that lets
428
- # add flags to this group in a block.
429
- # @return [Toys::DSL::Tool] self, for chaining.
616
+ # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup}
617
+ # for the directives that can be called in this block.
618
+ # @return [self]
430
619
  #
431
- def at_most_one_required(desc: nil, long_desc: nil, name: nil, report_collisions: true,
432
- prepend: false, &block)
620
+ def at_most_one(desc: nil, long_desc: nil, name: nil, report_collisions: true,
621
+ prepend: false, &block)
433
622
  flag_group(type: :at_most_one, desc: desc, long_desc: long_desc,
434
623
  name: name, report_collisions: report_collisions, prepend: prepend, &block)
435
624
  end
625
+ alias at_most_one_required at_most_one
436
626
 
437
627
  ##
438
628
  # Create a flag group of type `:at_least_one`. If a block is given, flags
439
629
  # defined in the block belong to the group. At least one flag in this
440
630
  # group must be provided on the command line.
441
631
  #
442
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
443
- # description for the group. See {Toys::Definition::Tool#desc=} for a
444
- # description of allowed formats. Defaults to `"Flags"`.
445
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
632
+ # ## Example
633
+ #
634
+ # The following example creates a group of flags in which one or more
635
+ # may be set.
636
+ #
637
+ # tool "run-tests" do
638
+ # at_least_one do
639
+ # flag :unit, desc: "Run unit tests"
640
+ # flag :integration, desc: "Run integration tests"
641
+ # flag :performance, desc: "Run performance tests"
642
+ # end
643
+ # # ...
644
+ # end
645
+ #
646
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
647
+ # description for the group. See {Toys::Tool#desc=} for a description
648
+ # of allowed formats. Defaults to `"Flags"`.
649
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
446
650
  # Long description for the flag group. See
447
- # {Toys::Definition::Tool#long_desc=} for a description of allowed
448
- # formats. Defaults to the empty array.
449
- # @param [String,Symbol,nil] name The name of the group, or nil for no
651
+ # {Toys::Tool#long_desc=} for a description of allowed formats.
652
+ # Defaults to the empty array.
653
+ # @param name [String,Symbol,nil] The name of the group, or nil for no
450
654
  # name.
451
- # @param [Boolean] report_collisions If `true`, raise an exception if a
655
+ # @param report_collisions [Boolean] If `true`, raise an exception if a
452
656
  # the given name is already taken. If `false`, ignore. Default is
453
657
  # `true`.
454
- # @param [Boolean] prepend If `true`, prepend rather than append the
658
+ # @param prepend [Boolean] If `true`, prepend rather than append the
455
659
  # group to the list. Default is `false`.
456
- # @yieldparam flag_group_dsl [Toys::DSL::FlagGroup] An object that lets
457
- # add flags to this group in a block.
458
- # @return [Toys::DSL::Tool] self, for chaining.
660
+ # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup}
661
+ # for the directives that can be called in this block.
662
+ # @return [self]
459
663
  #
460
- def at_least_one_required(desc: nil, long_desc: nil, name: nil, report_collisions: true,
461
- prepend: false, &block)
664
+ def at_least_one(desc: nil, long_desc: nil, name: nil, report_collisions: true,
665
+ prepend: false, &block)
462
666
  flag_group(type: :at_least_one, desc: desc, long_desc: long_desc,
463
667
  name: name, report_collisions: report_collisions, prepend: prepend, &block)
464
668
  end
669
+ alias at_least_one_required at_least_one
465
670
 
466
671
  ##
467
672
  # Create a flag group of type `:exactly_one`. If a block is given, flags
468
673
  # defined in the block belong to the group. Exactly one flag in this
469
674
  # group must be provided on the command line.
470
675
  #
471
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
472
- # description for the group. See {Toys::Definition::Tool#desc=} for a
473
- # description of allowed formats. Defaults to `"Flags"`.
474
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
676
+ # ## Example
677
+ #
678
+ # The following example creates a group of flags in which exactly one
679
+ # must be set.
680
+ #
681
+ # tool "deploy" do
682
+ # exactly_one do
683
+ # flag :server, "--server=IP_ADDR", desc: "Deploy to server"
684
+ # flag :vm, "--vm=ID", desc: "Deploy to a VM"
685
+ # flag :container, "--container=ID", desc: "Deploy to a container"
686
+ # end
687
+ # # ...
688
+ # end
689
+ #
690
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
691
+ # description for the group. See {Toys::Tool#desc=} for a description
692
+ # of allowed formats. Defaults to `"Flags"`.
693
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
475
694
  # Long description for the flag group. See
476
- # {Toys::Definition::Tool#long_desc=} for a description of allowed
477
- # formats. Defaults to the empty array.
478
- # @param [String,Symbol,nil] name The name of the group, or nil for no
695
+ # {Toys::Tool#long_desc=} for a description of allowed formats.
696
+ # Defaults to the empty array.
697
+ # @param name [String,Symbol,nil] The name of the group, or nil for no
479
698
  # name.
480
- # @param [Boolean] report_collisions If `true`, raise an exception if a
699
+ # @param report_collisions [Boolean] If `true`, raise an exception if a
481
700
  # the given name is already taken. If `false`, ignore. Default is
482
701
  # `true`.
483
- # @param [Boolean] prepend If `true`, prepend rather than append the
702
+ # @param prepend [Boolean] If `true`, prepend rather than append the
484
703
  # group to the list. Default is `false`.
485
- # @yieldparam flag_group_dsl [Toys::DSL::FlagGroup] An object that lets
486
- # add flags to this group in a block.
487
- # @return [Toys::DSL::Tool] self, for chaining.
704
+ # @param block [Proc] Adds flags to the group. See {Toys::DSL::FlagGroup}
705
+ # for the directives that can be called in this block.
706
+ # @return [self]
488
707
  #
489
- def exactly_one_required(desc: nil, long_desc: nil, name: nil, report_collisions: true,
490
- prepend: false, &block)
708
+ def exactly_one(desc: nil, long_desc: nil, name: nil, report_collisions: true,
709
+ prepend: false, &block)
491
710
  flag_group(type: :exactly_one, desc: desc, long_desc: long_desc,
492
711
  name: name, report_collisions: report_collisions, prepend: prepend, &block)
493
712
  end
713
+ alias exactly_one_required exactly_one
494
714
 
495
715
  ##
496
716
  # Add a flag to the current tool. Each flag must specify a key which
@@ -500,56 +720,176 @@ module Toys
500
720
  # If the given key is a symbol representing a valid method name, then a
501
721
  # helper method is automatically added to retrieve the value. Otherwise,
502
722
  # if the key is a string or does not represent a valid method name, the
503
- # tool can retrieve the value by calling {Toys::Tool#get}.
723
+ # tool can retrieve the value by calling {Toys::Context#get}.
504
724
  #
505
725
  # Attributes of the flag may be passed in as arguments to this method, or
506
726
  # set in a block passed to this method. If you provide a block, you can
507
727
  # use directives in {Toys::DSL::Flag} within the block.
508
728
  #
509
- # @param [String,Symbol] key The key to use to retrieve the value from
729
+ # ## Flag syntax
730
+ #
731
+ # The flags themselves should be provided in OptionParser form. Following
732
+ # are examples of valid syntax.
733
+ #
734
+ # * `-a` : A short boolean switch. When this appears as an argument,
735
+ # the value is set to `true`.
736
+ # * `--abc` : A long boolean switch. When this appears as an argument,
737
+ # the value is set to `true`.
738
+ # * `-aVAL` or `-a VAL` : A short flag that takes a required value.
739
+ # These two forms are treated identically. If this argument appears
740
+ # with a value attached (e.g. `-afoo`), the attached string (e.g.
741
+ # `"foo"`) is taken as the value. Otherwise, the following argument
742
+ # is taken as the value (e.g. for `-a foo`, the value is set to
743
+ # `"foo"`.) The following argument is treated as the value even if it
744
+ # looks like a flag (e.g. `-a -a` causes the string `"-a"` to be
745
+ # taken as the value.)
746
+ # * `-a[VAL]` : A short flag that takes an optional value. If this
747
+ # argument appears with a value attached (e.g. `-afoo`), the attached
748
+ # string (e.g. `"foo"`) is taken as the value. Otherwise, the value
749
+ # is set to `true`. The following argument is never interpreted as
750
+ # the value. (Compare with `-a [VAL]`.)
751
+ # * `-a [VAL]` : A short flag that takes an optional value. If this
752
+ # argument appears with a value attached (e.g. `-afoo`), the attached
753
+ # string (e.g. `"foo"`) is taken as the value. Otherwise, if the
754
+ # following argument does not look like a flag (i.e. it does not
755
+ # begin with a hyphen), it is taken as the value. (e.g. `-a foo`
756
+ # causes the string `"foo"` to be taken as the value.). If there is
757
+ # no following argument, or the following argument looks like a flag,
758
+ # the value is set to `true`. (Compare with `-a[VAL]`.)
759
+ # * `--abc=VAL` or `--abc VAL` : A long flag that takes a required
760
+ # value. These two forms are treated identically. If this argument
761
+ # appears with a value attached (e.g. `--abc=foo`), the attached
762
+ # string (e.g. `"foo"`) is taken as the value. Otherwise, the
763
+ # following argument is taken as the value (e.g. for `--abc foo`, the
764
+ # value is set to `"foo"`.) The following argument is treated as the
765
+ # value even if it looks like a flag (e.g. `--abc --abc` causes the
766
+ # string `"--abc"` to be taken as the value.)
767
+ # * `--abc[=VAL]` : A long flag that takes an optional value. If this
768
+ # argument appears with a value attached (e.g. `--abc=foo`), the
769
+ # attached string (e.g. `"foo"`) is taken as the value. Otherwise,
770
+ # the value is set to `true`. The following argument is never
771
+ # interpreted as the value. (Compare with `--abc [VAL]`.)
772
+ # * `--abc [VAL]` : A long flag that takes an optional value. If this
773
+ # argument appears with a value attached (e.g. `--abc=foo`), the
774
+ # attached string (e.g. `"foo"`) is taken as the value. Otherwise, if
775
+ # the following argument does not look like a flag (i.e. it does not
776
+ # begin with a hyphen), it is taken as the value. (e.g. `--abc foo`
777
+ # causes the string `"foo"` to be taken as the value.). If there is
778
+ # no following argument, or the following argument looks like a flag,
779
+ # the value is set to `true`. (Compare with `--abc=[VAL]`.)
780
+ # * `--[no-]abc` : A long boolean switch that can be turned either on
781
+ # or off. This effectively creates two flags, `--abc` which sets the
782
+ # value to `true`, and `--no-abc` which sets the falue to `false`.
783
+ #
784
+ # ## Default flag syntax
785
+ #
786
+ # If no flag syntax strings are provided, a default syntax will be
787
+ # inferred based on the key and other options.
788
+ #
789
+ # Specifically, if the key has one character, then that character will be
790
+ # chosen as a short flag. If the key has multiple characters, a long flag
791
+ # will be generated.
792
+ #
793
+ # Furthermore, if a custom completion, a non-boolean acceptor, or a
794
+ # non-boolean default value is provided in the options, then the flag
795
+ # will be considered to take a value. Otherwise, it will be considered to
796
+ # be a boolean switch.
797
+ #
798
+ # For example, the following pairs of flags are identical:
799
+ #
800
+ # flag :a
801
+ # flag :a, "-a"
802
+ #
803
+ # flag :abc_def
804
+ # flag :abc_def, "--abc-def"
805
+ #
806
+ # flag :number, accept: Integer
807
+ # flag :number, "--number=VAL", accept: Integer
808
+ #
809
+ # ## More examples
810
+ #
811
+ # A flag that sets its value to the number of times it appears on the
812
+ # command line:
813
+ #
814
+ # flag :verbose, "-v", "--verbose",
815
+ # default: 0, handler: ->(_val, count) { count + 1 }
816
+ #
817
+ # An example using block form:
818
+ #
819
+ # flag :shout do
820
+ # flags "-s", "--shout"
821
+ # default false
822
+ # desc "Say it louder"
823
+ # long_desc "This flag says it lowder.",
824
+ # "You might use this when people can't hear you.",
825
+ # "",
826
+ # "Example:",
827
+ # [" toys say --shout hello"]
828
+ # end
829
+ #
830
+ # @param key [String,Symbol] The key to use to retrieve the value from
510
831
  # the execution context.
511
- # @param [String...] flags The flags in OptionParser format.
512
- # @param [Object] accept An acceptor that validates and/or converts the
832
+ # @param flags [String...] The flags in OptionParser format.
833
+ # @param accept [Object] An acceptor that validates and/or converts the
513
834
  # value. You may provide either the name of an acceptor you have
514
835
  # defined, or one of the default acceptors provided by OptionParser.
515
836
  # Optional. If not specified, accepts any value as a string.
516
- # @param [Object] default The default value. This is the value that will
837
+ # @param default [Object] The default value. This is the value that will
517
838
  # be set in the context if this flag is not provided on the command
518
839
  # line. Defaults to `nil`.
519
- # @param [Proc,nil] handler An optional handler for setting/updating the
520
- # value. If given, it should take two arguments, the new given value
521
- # and the previous value, and it should return the new value that
522
- # should be set. The default handler simply replaces the previous
523
- # value. i.e. the default is effectively `-> (val, _prev) { val }`.
524
- # @param [Boolean] report_collisions Raise an exception if a flag is
840
+ # @param handler [Proc,nil,:set,:push] An optional handler for
841
+ # setting/updating the value. A handler is a proc taking two
842
+ # arguments, the given value and the previous value, returning the
843
+ # new value that should be set. You may also specify a predefined
844
+ # named handler. The `:set` handler (the default) replaces the
845
+ # previous value (effectively `-> (val, _prev) { val }`). The
846
+ # `:push` handler expects the previous value to be an array and
847
+ # pushes the given value onto it; it should be combined with setting
848
+ # `default: []` and is intended for "multi-valued" flags.
849
+ # @param complete_flags [Object] A specifier for shell tab completion
850
+ # for flag names associated with this flag. By default, a
851
+ # {Toys::Flag::DefaultCompletion} is used, which provides the flag's
852
+ # names as completion candidates. To customize completion, set this
853
+ # to the name of a previously defined completion, a hash of options
854
+ # to pass to the constructor for {Toys::Flag::DefaultCompletion}, or
855
+ # any other spec recognized by {Toys::Completion.create}.
856
+ # @param complete_values [Object] A specifier for shell tab completion
857
+ # for flag values associated with this flag. This is the empty
858
+ # completion by default. To customize completion, set this to the
859
+ # name of a previously defined completion, or any spec recognized by
860
+ # {Toys::Completion.create}.
861
+ # @param report_collisions [Boolean] Raise an exception if a flag is
525
862
  # requested that is already in use or marked as unusable. Default is
526
863
  # true.
527
- # @param [Toys::Definition::FlagGroup,String,Symbol,nil] group Group for
528
- # this flag. You may provide a group name, a FlagGroup object, or
529
- # `nil` which denotes the default group.
530
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
864
+ # @param group [Toys::FlagGroup,String,Symbol,nil] Group for this flag.
865
+ # You may provide a group name, a FlagGroup object, or `nil` which
866
+ # denotes the default group.
867
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
531
868
  # description for the flag. See {Toys::DSL::Tool#desc} for a
532
869
  # description of the allowed formats. Defaults to the empty string.
533
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
870
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
534
871
  # Long description for the flag. See {Toys::DSL::Tool#long_desc} for
535
872
  # a description of the allowed formats. (But note that this param
536
873
  # takes an Array of description lines, rather than a series of
537
874
  # arguments.) Defaults to the empty array.
538
- # @param [String] display_name A display name for this flag, used in help
875
+ # @param display_name [String] A display name for this flag, used in help
539
876
  # text and error messages.
540
- # @yieldparam flag_dsl [Toys::DSL::Flag] An object that lets you
541
- # configure this flag in a block.
542
- # @return [Toys::DSL::Tool] self, for chaining.
877
+ # @param block [Proc] Configures the flag. See {Toys::DSL::Flag} for the
878
+ # directives that can be called in this block.
879
+ # @return [self]
543
880
  #
544
881
  def flag(key, *flags,
545
882
  accept: nil, default: nil, handler: nil,
883
+ complete_flags: nil, complete_values: nil,
546
884
  report_collisions: true, group: nil,
547
885
  desc: nil, long_desc: nil, display_name: nil,
548
886
  &block)
549
887
  cur_tool = DSL::Tool.current_tool(self, true)
550
888
  return self if cur_tool.nil?
551
- flag_dsl = DSL::Flag.new(flags, accept, default, handler, report_collisions,
552
- group, desc, long_desc, display_name)
889
+ flag_dsl = DSL::Flag.new(
890
+ flags.flatten, accept, default, handler, complete_flags, complete_values,
891
+ report_collisions, group, desc, long_desc, display_name
892
+ )
553
893
  flag_dsl.instance_exec(flag_dsl, &block) if block
554
894
  flag_dsl._add_to(cur_tool, key)
555
895
  DSL::Tool.maybe_add_getter(self, key)
@@ -564,39 +904,57 @@ module Toys
564
904
  # If the given key is a symbol representing a valid method name, then a
565
905
  # helper method is automatically added to retrieve the value. Otherwise,
566
906
  # if the key is a string or does not represent a valid method name, the
567
- # tool can retrieve the value by calling {Toys::Tool#get}.
907
+ # tool can retrieve the value by calling {Toys::Context#get}.
568
908
  #
569
909
  # Attributes of the arg may be passed in as arguments to this method, or
570
910
  # set in a block passed to this method. If you provide a block, you can
571
- # use directives in {Toys::DSL::Arg} within the block.
911
+ # use directives in {Toys::DSL::PositionalArg} within the block.
912
+ #
913
+ # ## Example
914
+ #
915
+ # This tool "moves" something from a source to destination, and takes two
916
+ # required arguments:
572
917
  #
573
- # @param [String,Symbol] key The key to use to retrieve the value from
918
+ # tool "mv" do
919
+ # required_arg :source
920
+ # required_arg :dest
921
+ # def run
922
+ # puts "moving from #{source} to #{dest}..."
923
+ # end
924
+ # end
925
+ #
926
+ # @param key [String,Symbol] The key to use to retrieve the value from
574
927
  # the execution context.
575
- # @param [Object] accept An acceptor that validates and/or converts the
928
+ # @param accept [Object] An acceptor that validates and/or converts the
576
929
  # value. You may provide either the name of an acceptor you have
577
930
  # defined, or one of the default acceptors provided by OptionParser.
578
931
  # Optional. If not specified, accepts any value as a string.
579
- # @param [String] display_name A name to use for display (in help text and
932
+ # @param complete [Object] A specifier for shell tab completion for
933
+ # values of this arg. This is the empty completion by default. To
934
+ # customize completion, set this to the name of a previously defined
935
+ # completion, or any spec recognized by {Toys::Completion.create}.
936
+ # @param display_name [String] A name to use for display (in help text and
580
937
  # error reports). Defaults to the key in upper case.
581
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
938
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
582
939
  # description for the flag. See {Toys::DSL::Tool#desc} for a
583
940
  # description of the allowed formats. Defaults to the empty string.
584
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
941
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
585
942
  # Long description for the flag. See {Toys::DSL::Tool#long_desc} for
586
943
  # a description of the allowed formats. (But note that this param
587
944
  # takes an Array of description lines, rather than a series of
588
945
  # arguments.) Defaults to the empty array.
589
- # @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
590
- # this argument in a block.
591
- # @return [Toys::DSL::Tool] self, for chaining.
946
+ # @param block [Proc] Configures the positional argument. See
947
+ # {Toys::DSL::PositionalArg} for the directives that can be called in
948
+ # this block.
949
+ # @return [self]
592
950
  #
593
951
  def required_arg(key,
594
- accept: nil, display_name: nil,
952
+ accept: nil, complete: nil, display_name: nil,
595
953
  desc: nil, long_desc: nil,
596
954
  &block)
597
955
  cur_tool = DSL::Tool.current_tool(self, true)
598
956
  return self if cur_tool.nil?
599
- arg_dsl = DSL::Arg.new(accept, nil, display_name, desc, long_desc)
957
+ arg_dsl = DSL::PositionalArg.new(accept, nil, complete, display_name, desc, long_desc)
600
958
  arg_dsl.instance_exec(arg_dsl, &block) if block
601
959
  arg_dsl._add_required_to(cur_tool, key)
602
960
  DSL::Tool.maybe_add_getter(self, key)
@@ -613,42 +971,61 @@ module Toys
613
971
  # If the given key is a symbol representing a valid method name, then a
614
972
  # helper method is automatically added to retrieve the value. Otherwise,
615
973
  # if the key is a string or does not represent a valid method name, the
616
- # tool can retrieve the value by calling {Toys::Tool#get}.
974
+ # tool can retrieve the value by calling {Toys::Context#get}.
617
975
  #
618
976
  # Attributes of the arg may be passed in as arguments to this method, or
619
977
  # set in a block passed to this method. If you provide a block, you can
620
- # use directives in {Toys::DSL::Arg} within the block.
978
+ # use directives in {Toys::DSL::PositionalArg} within the block.
979
+ #
980
+ # ## Example
981
+ #
982
+ # This tool creates a "link" to a given target. The link location is
983
+ # optional; if it is not given, it is inferred from the target.
984
+ #
985
+ # tool "ln" do
986
+ # required_arg :target
987
+ # optional_arg :location
988
+ # def run
989
+ # loc = location || File.basename(target)
990
+ # puts "linking to #{target} from #{loc}..."
991
+ # end
992
+ # end
621
993
  #
622
- # @param [String,Symbol] key The key to use to retrieve the value from
994
+ # @param key [String,Symbol] The key to use to retrieve the value from
623
995
  # the execution context.
624
- # @param [Object] default The default value. This is the value that will
996
+ # @param default [Object] The default value. This is the value that will
625
997
  # be set in the context if this argument is not provided on the command
626
998
  # line. Defaults to `nil`.
627
- # @param [Object] accept An acceptor that validates and/or converts the
999
+ # @param accept [Object] An acceptor that validates and/or converts the
628
1000
  # value. You may provide either the name of an acceptor you have
629
1001
  # defined, or one of the default acceptors provided by OptionParser.
630
1002
  # Optional. If not specified, accepts any value as a string.
631
- # @param [String] display_name A name to use for display (in help text and
1003
+ # @param complete [Object] A specifier for shell tab completion for
1004
+ # values of this arg. This is the empty completion by default. To
1005
+ # customize completion, set this to the name of a previously defined
1006
+ # completion, or any spec recognized by {Toys::Completion.create}.
1007
+ # @param display_name [String] A name to use for display (in help text and
632
1008
  # error reports). Defaults to the key in upper case.
633
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
1009
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
634
1010
  # description for the flag. See {Toys::DSL::Tool#desc} for a
635
1011
  # description of the allowed formats. Defaults to the empty string.
636
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
1012
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
637
1013
  # Long description for the flag. See {Toys::DSL::Tool#long_desc} for
638
1014
  # a description of the allowed formats. (But note that this param
639
1015
  # takes an Array of description lines, rather than a series of
640
1016
  # arguments.) Defaults to the empty array.
641
- # @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
642
- # this argument in a block.
643
- # @return [Toys::DSL::Tool] self, for chaining.
1017
+ # @param block [Proc] Configures the positional argument. See
1018
+ # {Toys::DSL::PositionalArg} for the directives that can be called in
1019
+ # this block.
1020
+ # @return [self]
644
1021
  #
645
1022
  def optional_arg(key,
646
- default: nil, accept: nil, display_name: nil,
1023
+ default: nil, accept: nil, complete: nil, display_name: nil,
647
1024
  desc: nil, long_desc: nil,
648
1025
  &block)
649
1026
  cur_tool = DSL::Tool.current_tool(self, true)
650
1027
  return self if cur_tool.nil?
651
- arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
1028
+ arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
652
1029
  arg_dsl.instance_exec(arg_dsl, &block) if block
653
1030
  arg_dsl._add_optional_to(cur_tool, key)
654
1031
  DSL::Tool.maybe_add_getter(self, key)
@@ -664,42 +1041,62 @@ module Toys
664
1041
  # If the given key is a symbol representing a valid method name, then a
665
1042
  # helper method is automatically added to retrieve the value. Otherwise,
666
1043
  # if the key is a string or does not represent a valid method name, the
667
- # tool can retrieve the value by calling {Toys::Tool#get}.
1044
+ # tool can retrieve the value by calling {Toys::Context#get}.
668
1045
  #
669
1046
  # Attributes of the arg may be passed in as arguments to this method, or
670
1047
  # set in a block passed to this method. If you provide a block, you can
671
- # use directives in {Toys::DSL::Arg} within the block.
1048
+ # use directives in {Toys::DSL::PositionalArg} within the block.
1049
+ #
1050
+ # ## Example
1051
+ #
1052
+ # This tool displays a "list" of the given directories. If no directories
1053
+ # ar given, lists the current directory.
1054
+ #
1055
+ # tool "ln" do
1056
+ # remaining_args :directories
1057
+ # def run
1058
+ # dirs = directories.empty? ? [Dir.pwd] : directories
1059
+ # dirs.each do |dir|
1060
+ # puts "Listing directory #{dir}..."
1061
+ # end
1062
+ # end
1063
+ # end
672
1064
  #
673
- # @param [String,Symbol] key The key to use to retrieve the value from
1065
+ # @param key [String,Symbol] The key to use to retrieve the value from
674
1066
  # the execution context.
675
- # @param [Object] default The default value. This is the value that will
1067
+ # @param default [Object] The default value. This is the value that will
676
1068
  # be set in the context if no unmatched arguments are provided on the
677
1069
  # command line. Defaults to the empty array `[]`.
678
- # @param [Object] accept An acceptor that validates and/or converts the
1070
+ # @param accept [Object] An acceptor that validates and/or converts the
679
1071
  # value. You may provide either the name of an acceptor you have
680
1072
  # defined, or one of the default acceptors provided by OptionParser.
681
1073
  # Optional. If not specified, accepts any value as a string.
682
- # @param [String] display_name A name to use for display (in help text and
1074
+ # @param complete [Object] A specifier for shell tab completion for
1075
+ # values of this arg. This is the empty completion by default. To
1076
+ # customize completion, set this to the name of a previously defined
1077
+ # completion, or any spec recognized by {Toys::Completion.create}.
1078
+ # @param display_name [String] A name to use for display (in help text and
683
1079
  # error reports). Defaults to the key in upper case.
684
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
1080
+ # @param desc [String,Array<String>,Toys::WrappableString] Short
685
1081
  # description for the flag. See {Toys::DSL::Tool#desc} for a
686
1082
  # description of the allowed formats. Defaults to the empty string.
687
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
1083
+ # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
688
1084
  # Long description for the flag. See {Toys::DSL::Tool#long_desc} for
689
1085
  # a description of the allowed formats. (But note that this param
690
1086
  # takes an Array of description lines, rather than a series of
691
1087
  # arguments.) Defaults to the empty array.
692
- # @yieldparam arg_dsl [Toys::DSL::Arg] An object that lets you configure
693
- # this argument in a block.
694
- # @return [Toys::DSL::Tool] self, for chaining.
1088
+ # @param block [Proc] Configures the positional argument. See
1089
+ # {Toys::DSL::PositionalArg} for the directives that can be called in
1090
+ # this block.
1091
+ # @return [self]
695
1092
  #
696
1093
  def remaining_args(key,
697
- default: [], accept: nil, display_name: nil,
1094
+ default: [], accept: nil, complete: nil, display_name: nil,
698
1095
  desc: nil, long_desc: nil,
699
1096
  &block)
700
1097
  cur_tool = DSL::Tool.current_tool(self, true)
701
1098
  return self if cur_tool.nil?
702
- arg_dsl = DSL::Arg.new(accept, default, display_name, desc, long_desc)
1099
+ arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
703
1100
  arg_dsl.instance_exec(arg_dsl, &block) if block
704
1101
  arg_dsl._set_remaining_on(cur_tool, key)
705
1102
  DSL::Tool.maybe_add_getter(self, key)
@@ -708,17 +1105,35 @@ module Toys
708
1105
  alias remaining remaining_args
709
1106
 
710
1107
  ##
711
- # Set an option value statically.
1108
+ # Set a option values statically and create a helper method.
712
1109
  #
713
- # If the given key is a symbol representing a valid method name, then a
1110
+ # If any given key is a symbol representing a valid method name, then a
714
1111
  # helper method is automatically added to retrieve the value. Otherwise,
715
1112
  # if the key is a string or does not represent a valid method name, the
716
- # tool can retrieve the value by calling {Toys::Tool#get}.
1113
+ # tool can retrieve the value by calling {Toys::Context#get}.
717
1114
  #
718
- # @param [String,Symbol] key The key to use to retrieve the value from
719
- # the execution context.
720
- # @param [Object] value The value to set.
721
- # @return [Toys::DSL::Tool] self, for chaining.
1115
+ # ## Example
1116
+ #
1117
+ # tool "hello" do
1118
+ # static :greeting, "Hi there"
1119
+ # def run
1120
+ # puts "#{greeting}, world!"
1121
+ # end
1122
+ # end
1123
+ #
1124
+ # @return [self]
1125
+ #
1126
+ # @overload static(key, value)
1127
+ # Set a single value by key.
1128
+ # @param key [String,Symbol] The key to use to retrieve the value from
1129
+ # the execution context.
1130
+ # @param value [Object] The value to set.
1131
+ # @return [self]
1132
+ #
1133
+ # @overload static(hash)
1134
+ # Set multiple keys and values
1135
+ # @param hash [Hash] The keys and values to set
1136
+ # @return [self]
722
1137
  #
723
1138
  def static(key, value = nil)
724
1139
  cur_tool = DSL::Tool.current_tool(self, true)
@@ -735,15 +1150,84 @@ module Toys
735
1150
  self
736
1151
  end
737
1152
 
1153
+ ##
1154
+ # Set a option values statically without creating helper methods.
1155
+ #
1156
+ # ## Example
1157
+ #
1158
+ # tool "hello" do
1159
+ # set :greeting, "Hi there"
1160
+ # def run
1161
+ # puts "#{get(:greeting)}, world!"
1162
+ # end
1163
+ # end
1164
+ #
1165
+ # @return [self]
1166
+ #
1167
+ # @overload set(key, value)
1168
+ # Set a single value by key.
1169
+ # @param key [String,Symbol] The key to use to retrieve the value from
1170
+ # the execution context.
1171
+ # @param value [Object] The value to set.
1172
+ # @return [self]
1173
+ #
1174
+ # @overload set(hash)
1175
+ # Set multiple keys and values
1176
+ # @param hash [Hash] The keys and values to set
1177
+ # @return [self]
1178
+ #
1179
+ def set(key, value = nil)
1180
+ cur_tool = DSL::Tool.current_tool(self, true)
1181
+ return self if cur_tool.nil?
1182
+ if key.is_a?(::Hash)
1183
+ cur_tool.default_data.merge!(key)
1184
+ else
1185
+ cur_tool.default_data[key] = value
1186
+ end
1187
+ self
1188
+ end
1189
+
1190
+ ##
1191
+ # Enforce that all flags must be provided before any positional args.
1192
+ # That is, as soon as the first positional arg appears in the command
1193
+ # line arguments, flag parsing is disabled as if `--` had appeared.
1194
+ #
1195
+ # Issuing this directive by itself turns on enforcement. You may turn it
1196
+ # off by passsing `false` as the parameter.
1197
+ #
1198
+ # @param state [Boolean]
1199
+ # @return [self]
1200
+ #
1201
+ def enforce_flags_before_args(state = true)
1202
+ DSL::Tool.current_tool(self, true)&.enforce_flags_before_args(state)
1203
+ self
1204
+ end
1205
+
1206
+ ##
1207
+ # Require that flags must match exactly. That is, flags must appear in
1208
+ # their entirety on the command line. (If false, substrings of flags are
1209
+ # accepted as long as they are unambiguous.)
1210
+ #
1211
+ # Issuing this directive by itself turns on exact match. You may turn it
1212
+ # off by passsing `false` as the parameter.
1213
+ #
1214
+ # @param state [Boolean]
1215
+ # @return [self]
1216
+ #
1217
+ def require_exact_flag_match(state = true)
1218
+ DSL::Tool.current_tool(self, true)&.require_exact_flag_match(state)
1219
+ self
1220
+ end
1221
+
738
1222
  ##
739
1223
  # Disable argument parsing for this tool. Arguments will not be parsed
740
1224
  # and the options will not be populated. Instead, tools can retrieve the
741
- # full unparsed argument list by calling {Toys::Tool#args}.
1225
+ # full unparsed argument list by calling {Toys::Context#args}.
742
1226
  #
743
1227
  # This directive is mutually exclusive with any of the directives that
744
1228
  # declare arguments or flags.
745
1229
  #
746
- # @return [Toys::DSL::Tool] self, for chaining.
1230
+ # @return [self]
747
1231
  #
748
1232
  def disable_argument_parsing
749
1233
  DSL::Tool.current_tool(self, true)&.disable_argument_parsing
@@ -755,26 +1239,152 @@ module Toys
755
1239
  # subsequent flag definition. This can be used to prevent middleware from
756
1240
  # defining a particular flag.
757
1241
  #
758
- # @param [String...] flags The flags to disable
759
- # @return [Toys::DSL::Tool] self, for chaining.
1242
+ # ## Example
1243
+ #
1244
+ # This tool does not support the `-v` and `-q` short forms for the two
1245
+ # verbosity flags (although it still supports the long forms `--verbose`
1246
+ # and `--quiet`.)
1247
+ #
1248
+ # tool "mytool" do
1249
+ # disable_flag "-v", "-q"
1250
+ # def run
1251
+ # # ...
1252
+ # end
1253
+ # end
1254
+ #
1255
+ # @param flags [String...] The flags to disable
1256
+ # @return [self]
760
1257
  #
761
1258
  def disable_flag(*flags)
762
1259
  DSL::Tool.current_tool(self, true)&.disable_flag(*flags)
763
1260
  self
764
1261
  end
765
1262
 
1263
+ ##
1264
+ # Set the shell completion strategy for this tool's arguments.
1265
+ # You can pass one of the following:
1266
+ #
1267
+ # * The string name of a completion defined in this tool or any of its
1268
+ # its ancestors.
1269
+ # * A hash of options to pass to the constructor of
1270
+ # {Toys::Tool::DefaultCompletion}.
1271
+ # * `nil` or `:default` to select the standard completion strategy
1272
+ # (which is {Toys::Tool::DefaultCompletion} with no extra options).
1273
+ # * Any other specification recognized by {Toys::Completion.create}.
1274
+ #
1275
+ # ## Example
1276
+ #
1277
+ # The namespace "foo" supports completion only of subtool names. It does
1278
+ # not complete the standard flags (like --help).
1279
+ #
1280
+ # tool "foo" do
1281
+ # complete_tool_args complete_args: false, complete_flags: false,
1282
+ # complete_flag_values: false
1283
+ # tool "bar" do
1284
+ # def run
1285
+ # puts "in foo bar"
1286
+ # end
1287
+ # end
1288
+ # end
1289
+ #
1290
+ # @param spec [Object]
1291
+ # @param options [Hash]
1292
+ # @param block [Proc]
1293
+ # @return [self]
1294
+ #
1295
+ def complete_tool_args(spec = nil, **options, &block)
1296
+ cur_tool = DSL::Tool.current_tool(self, true)
1297
+ return self if cur_tool.nil?
1298
+ cur_tool.completion = cur_tool.scalar_completion(spec, options, &block)
1299
+ self
1300
+ end
1301
+
766
1302
  ##
767
1303
  # Specify how to run this tool. Typically you do this by defining a
768
- # method namd `run`. Alternatively, you can pass a block to this method.
1304
+ # method namd `run`. Alternatively, however, you can pass a block to the
1305
+ # `to_run` method.
1306
+ #
769
1307
  # You may want to do this if your method needs access to local variables
770
- # in the lexical scope.
1308
+ # in the lexical scope. However, it is often more convenient to use
1309
+ # {#static} to set the value in the context.)
1310
+ #
1311
+ # ## Example
1312
+ #
1313
+ # tool "foo" do
1314
+ # cur_time = Time.new
1315
+ # to_run do
1316
+ # puts "The time at tool definition was #{cur_time}"
1317
+ # end
1318
+ # end
771
1319
  #
772
- # @return [Toys::DSL::Tool] self, for chaining.
1320
+ # @param block [Proc] The run method.
1321
+ # @return [self]
773
1322
  #
774
1323
  def to_run(&block)
775
1324
  define_method(:run, &block)
776
1325
  self
777
1326
  end
1327
+ alias on_run to_run
1328
+
1329
+ ##
1330
+ # Specify how to handle interrupts.
1331
+ #
1332
+ # You may pass a block to be called, or the name of a method to call. In
1333
+ # either case, the block or method should take one argument, the
1334
+ # Interrupt exception that was raised.
1335
+ #
1336
+ # ## Example
1337
+ #
1338
+ # tool "foo" do
1339
+ # def run
1340
+ # sleep 10
1341
+ # end
1342
+ # on_interrupt do |e|
1343
+ # puts "I was interrupted."
1344
+ # end
1345
+ # end
1346
+ #
1347
+ # @param handler [Proc,Symbol,nil] The interrupt callback proc or method
1348
+ # name. Pass nil to disable interrupt handling.
1349
+ # @param block [Proc] The interrupt callback as a block.
1350
+ # @return [self]
1351
+ #
1352
+ def on_interrupt(handler = nil, &block)
1353
+ cur_tool = DSL::Tool.current_tool(self, true)
1354
+ cur_tool.interrupt_handler = handler || block unless cur_tool.nil?
1355
+ self
1356
+ end
1357
+
1358
+ ##
1359
+ # Specify how to handle usage errors.
1360
+ #
1361
+ # You may pass a block to be called, or the name of a method to call. In
1362
+ # either case, the block or method should take one argument, the array of
1363
+ # usage errors reported.
1364
+ #
1365
+ # ## Example
1366
+ #
1367
+ # This tool runs even if a usage error is encountered. You can find info
1368
+ # on the errors from {Toys::Context::Key::USAGE_ERRORS},
1369
+ # {Toys::Context::Key::UNMATCHED_ARGS}, and similar keys.
1370
+ #
1371
+ # tool "foo" do
1372
+ # def run
1373
+ # puts "Errors: #{usage_errors.join("\n")}"
1374
+ # end
1375
+ # on_usage_error :run
1376
+ # end
1377
+ #
1378
+ # @param handler [Proc,Symbol,nil] The interrupt callback proc or method
1379
+ # name. Pass nil to disable interrupt handling.
1380
+ # @param block [Proc] The interrupt callback as a block.
1381
+ # @return [self]
1382
+ #
1383
+ def on_usage_error(handler = nil, &block)
1384
+ cur_tool = DSL::Tool.current_tool(self, true)
1385
+ cur_tool.usage_error_handler = handler || block unless cur_tool.nil?
1386
+ self
1387
+ end
778
1388
 
779
1389
  ##
780
1390
  # Specify that the given module should be mixed into this tool, and its
@@ -784,26 +1394,43 @@ module Toys
784
1394
  # have defined in this tool or one of its ancestors, or the symbol name
785
1395
  # of a well-known mixin.
786
1396
  #
787
- # @param [Module,Symbol,String] mod Module or module name.
788
- # @param [Object...] args Arguments to pass to the initializer
1397
+ # ## Example
1398
+ #
1399
+ # Include the well-known mixin `:terminal` and perform some terminal
1400
+ # magic.
1401
+ #
1402
+ # tool "spin" do
1403
+ # include :terminal
1404
+ # def run
1405
+ # # The spinner method is defined by the :terminal mixin.
1406
+ # spinner(leading_text: "Waiting...", final_text: "\n") do
1407
+ # sleep 5
1408
+ # end
1409
+ # end
1410
+ # end
1411
+ #
1412
+ # @param mod [Module,Symbol,String] Module or module name.
1413
+ # @param args [Object...] Arguments to pass to the initializer
1414
+ # @return [self]
789
1415
  #
790
1416
  def include(mod, *args)
791
1417
  cur_tool = DSL::Tool.current_tool(self, true)
792
- return if cur_tool.nil?
1418
+ return self if cur_tool.nil?
793
1419
  mod = DSL::Tool.resolve_mixin(mod, cur_tool, @__loader)
794
1420
  if included_modules.include?(mod)
795
1421
  raise ToolDefinitionError, "Mixin already included: #{mod.name}"
796
1422
  end
797
1423
  cur_tool.mark_includes_modules
798
- if mod.respond_to?(:initialization_callback)
799
- callback = mod.initialization_callback
1424
+ super(mod)
1425
+ if mod.respond_to?(:initializer)
1426
+ callback = mod.initializer
800
1427
  cur_tool.add_initializer(callback, *args) if callback
801
1428
  end
802
- if mod.respond_to?(:inclusion_callback)
803
- callback = mod.inclusion_callback
1429
+ if mod.respond_to?(:inclusion)
1430
+ callback = mod.inclusion
804
1431
  class_exec(*args, &callback) if callback
805
1432
  end
806
- super(mod)
1433
+ self
807
1434
  end
808
1435
 
809
1436
  ##
@@ -813,9 +1440,10 @@ module Toys
813
1440
  # have defined in this tool or one of its ancestors, or the symbol name
814
1441
  # of a well-known mixin.
815
1442
  #
816
- # @param [Module,Symbol,String] mod Module or module name.
817
- # @return [Boolean,nil] A boolean value, or `nil` if the current tool
818
- # is not active.
1443
+ # @param mod [Module,Symbol,String] Module or module name.
1444
+ #
1445
+ # @return [Boolean] Whether the mixin is included
1446
+ # @return [nil] if the current tool is not active.
819
1447
  #
820
1448
  def include?(mod)
821
1449
  cur_tool = DSL::Tool.current_tool(self, false)
@@ -826,19 +1454,39 @@ module Toys
826
1454
  ##
827
1455
  # Return the current source info object.
828
1456
  #
829
- # @return [Toys::Definition::SourceInfo] Source info.
1457
+ # @return [Toys::SourceInfo] Source info.
830
1458
  #
831
1459
  def source_info
832
1460
  @__source.last
833
1461
  end
834
1462
 
835
1463
  ##
836
- # Find the given data path (file or directory)
1464
+ # Find the given data path (file or directory).
1465
+ #
1466
+ # Data directories are a convenient place to put images, archives, keys,
1467
+ # or other such static data needed by your tools. Data files are located
1468
+ # in a directory called `.data` inside a Toys directory. This directive
1469
+ # locates a data file during tool definition.
1470
+ #
1471
+ # ## Example
1472
+ #
1473
+ # This tool reads its description from a text file in the `.data`
1474
+ # directory.
1475
+ #
1476
+ # tool "mytool" do
1477
+ # path = find_data("mytool-desc.txt", type: :file)
1478
+ # desc IO.read(path) if path
1479
+ # def run
1480
+ # # ...
1481
+ # end
1482
+ # end
1483
+ #
1484
+ # @param path [String] The path to find
1485
+ # @param type [nil,:file,:directory] Type of file system object to find.
1486
+ # Default is `nil`, indicating any type.
837
1487
  #
838
- # @param [String] path The path to find
839
- # @param [nil,:file,:directory] type Type of file system object to find,
840
- # or nil to return any type.
841
- # @return [String,nil] Absolute path of the result, or nil if not found.
1488
+ # @return [String] Absolute path of the data.
1489
+ # @return [nil] if the given data path is not found.
842
1490
  #
843
1491
  def find_data(path, type: nil)
844
1492
  source_info.find_data(path, type: type)
@@ -849,9 +1497,9 @@ module Toys
849
1497
  # to the directory containing the toys config directory structure being
850
1498
  # read, but it may be changed by setting a different context directory
851
1499
  # for the tool.
852
- # May return nil if there is no context.
853
1500
  #
854
- # @return [String,nil] Context directory
1501
+ # @return [String] Context directory path
1502
+ # @return [nil] if there is no context.
855
1503
  #
856
1504
  def context_directory
857
1505
  DSL::Tool.current_tool(self, false)&.context_directory || source_info.context_directory
@@ -860,9 +1508,10 @@ module Toys
860
1508
  ##
861
1509
  # Set a custom context directory for this tool.
862
1510
  #
863
- # @param [String] dir Context directory
1511
+ # @param dir [String] Context directory
1512
+ # @return [self]
864
1513
  #
865
- def set_context_directory(dir)
1514
+ def set_context_directory(dir) # rubocop:disable Naming/AccessorMethodName
866
1515
  cur_tool = DSL::Tool.current_tool(self, false)
867
1516
  return if cur_tool.nil?
868
1517
  cur_tool.custom_context_directory = dir
@@ -871,7 +1520,7 @@ module Toys
871
1520
 
872
1521
  ## @private
873
1522
  def self.new_class(words, priority, loader)
874
- tool_class = ::Class.new(::Toys::Tool)
1523
+ tool_class = ::Class.new(::Toys::Context)
875
1524
  tool_class.extend(DSL::Tool)
876
1525
  tool_class.instance_variable_set(:@__words, words)
877
1526
  tool_class.instance_variable_set(:@__priority, priority)
@@ -892,11 +1541,11 @@ module Toys
892
1541
  priority = tool_class.instance_variable_get(:@__priority)
893
1542
  cur_tool =
894
1543
  if activate
895
- loader.activate_tool_definition(words, priority)
1544
+ loader.activate_tool(words, priority)
896
1545
  else
897
- loader.get_tool_definition(words, priority)
1546
+ loader.get_tool(words, priority)
898
1547
  end
899
- if cur_tool.is_a?(Definition::Alias)
1548
+ if cur_tool.is_a?(Alias)
900
1549
  raise ToolDefinitionError,
901
1550
  "Cannot configure #{words.join(' ').inspect} because it is an alias"
902
1551
  end
@@ -920,10 +1569,12 @@ module Toys
920
1569
 
921
1570
  ## @private
922
1571
  def self.maybe_add_getter(tool_class, key)
923
- if key.is_a?(::Symbol) && key.to_s =~ /^[_a-zA-Z]\w*[!\?]?$/
924
- tool_class.class_eval do
925
- define_method(key) do
926
- self[key]
1572
+ if key.is_a?(::Symbol) && key.to_s =~ /^[_a-zA-Z]\w*[!\?]?$/ && key != :run
1573
+ unless tool_class.public_method_defined?(key)
1574
+ tool_class.class_eval do
1575
+ define_method(key) do
1576
+ self[key]
1577
+ end
927
1578
  end
928
1579
  end
929
1580
  end
@@ -933,7 +1584,7 @@ module Toys
933
1584
  def self.resolve_mixin(mod, cur_tool, loader)
934
1585
  name = mod.to_s
935
1586
  if mod.is_a?(::String)
936
- mod = cur_tool.resolve_mixin(mod)
1587
+ mod = cur_tool.lookup_mixin(mod)
937
1588
  elsif mod.is_a?(::Symbol)
938
1589
  mod = loader.resolve_standard_mixin(name)
939
1590
  end