toys-core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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