toys-core 0.3.6 → 0.3.7

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/lib/toys-core.rb +20 -5
  4. data/lib/toys/cli.rb +39 -32
  5. data/lib/toys/core_version.rb +1 -1
  6. data/lib/toys/{tool → definition}/acceptor.rb +21 -15
  7. data/lib/toys/{utils/line_output.rb → definition/alias.rb} +47 -59
  8. data/lib/toys/{tool/arg_definition.rb → definition/arg.rb} +17 -7
  9. data/lib/toys/{tool/flag_definition.rb → definition/flag.rb} +19 -9
  10. data/lib/toys/definition/tool.rb +574 -0
  11. data/lib/toys/dsl/arg.rb +118 -0
  12. data/lib/toys/dsl/flag.rb +132 -0
  13. data/lib/toys/dsl/tool.rb +521 -0
  14. data/lib/toys/errors.rb +2 -2
  15. data/lib/toys/helpers.rb +3 -3
  16. data/lib/toys/helpers/exec.rb +31 -25
  17. data/lib/toys/helpers/fileutils.rb +8 -2
  18. data/lib/toys/helpers/highline.rb +8 -1
  19. data/lib/toys/{alias.rb → helpers/terminal.rb} +44 -53
  20. data/lib/toys/input_file.rb +61 -0
  21. data/lib/toys/loader.rb +87 -77
  22. data/lib/toys/middleware.rb +3 -3
  23. data/lib/toys/middleware/add_verbosity_flags.rb +22 -20
  24. data/lib/toys/middleware/base.rb +53 -5
  25. data/lib/toys/middleware/handle_usage_errors.rb +9 -12
  26. data/lib/toys/middleware/set_default_descriptions.rb +6 -7
  27. data/lib/toys/middleware/show_help.rb +71 -67
  28. data/lib/toys/middleware/show_root_version.rb +9 -9
  29. data/lib/toys/runner.rb +157 -0
  30. data/lib/toys/template.rb +4 -3
  31. data/lib/toys/templates.rb +2 -2
  32. data/lib/toys/templates/clean.rb +2 -2
  33. data/lib/toys/templates/gem_build.rb +5 -5
  34. data/lib/toys/templates/minitest.rb +2 -2
  35. data/lib/toys/templates/rubocop.rb +2 -2
  36. data/lib/toys/templates/yardoc.rb +2 -2
  37. data/lib/toys/tool.rb +168 -625
  38. data/lib/toys/utils/exec.rb +19 -18
  39. data/lib/toys/utils/gems.rb +140 -0
  40. data/lib/toys/utils/help_text.rb +25 -20
  41. data/lib/toys/utils/terminal.rb +412 -0
  42. data/lib/toys/utils/wrappable_string.rb +3 -1
  43. metadata +15 -24
  44. data/lib/toys/config_dsl.rb +0 -699
  45. data/lib/toys/context.rb +0 -290
  46. data/lib/toys/helpers/spinner.rb +0 -142
@@ -32,13 +32,13 @@ require "optparse"
32
32
  require "toys/utils/wrappable_string"
33
33
 
34
34
  module Toys
35
- class Tool
35
+ module Definition
36
36
  ##
37
37
  # Representation of a formal positional argument
38
38
  #
39
- class ArgDefinition
39
+ class Arg
40
40
  ##
41
- # Create an ArgDefinition
41
+ # Create an Arg definition
42
42
  # @private
43
43
  #
44
44
  def initialize(key, type, accept, default, desc, long_desc, display_name)
@@ -111,16 +111,26 @@ module Toys
111
111
  end
112
112
 
113
113
  ##
114
- # Set description
115
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc
114
+ # Set the short description string.
115
+ #
116
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
117
+ # single string (which will be wrapped), or an array of strings, which will
118
+ # be interpreted as string fragments that will be concatenated and wrapped.
119
+ #
120
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
116
121
  #
117
122
  def desc=(desc)
118
123
  @desc = Utils::WrappableString.make(desc)
119
124
  end
120
125
 
121
126
  ##
122
- # Set long description
123
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
127
+ # Set the long description strings.
128
+ #
129
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a single
130
+ # string (which will be wrapped), or an array of strings, which will be
131
+ # interpreted as string fragments that will be concatenated and wrapped.
132
+ #
133
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
124
134
  #
125
135
  def long_desc=(long_desc)
126
136
  @long_desc = Utils::WrappableString.make_array(long_desc)
@@ -32,7 +32,7 @@ require "optparse"
32
32
  require "toys/utils/wrappable_string"
33
33
 
34
34
  module Toys
35
- class Tool
35
+ module Definition
36
36
  ##
37
37
  # Representation of a single flag.
38
38
  #
@@ -105,9 +105,9 @@ module Toys
105
105
 
106
106
  ##
107
107
  # Representation of a formal set of flags that set a particular context
108
- # key. The flags within a single FlagDefinition are synonyms.
108
+ # key. The flags within a single Flag definition are synonyms.
109
109
  #
110
- class FlagDefinition
110
+ class Flag
111
111
  ##
112
112
  # The default handler replaces the previous value.
113
113
  # @return [Proc]
@@ -115,7 +115,7 @@ module Toys
115
115
  DEFAULT_HANDLER = ->(val, _prev) { val }
116
116
 
117
117
  ##
118
- # Create a FlagDefinition
118
+ # Create a Flag definition
119
119
  # @private
120
120
  #
121
121
  def initialize(key, flags, used_flags, report_collisions, accept, handler, default)
@@ -228,7 +228,7 @@ module Toys
228
228
  end
229
229
 
230
230
  ##
231
- # All optparser flags and acceptor if present
231
+ # Returns a list suitable for passing to OptionParser.
232
232
  # @return [Array]
233
233
  #
234
234
  def optparser_info
@@ -245,16 +245,26 @@ module Toys
245
245
  end
246
246
 
247
247
  ##
248
- # Set description
249
- # @param [String,Array<String>,Toys::Utils::WrappableString] desc
248
+ # Set the short description string.
249
+ #
250
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
251
+ # single string (which will be wrapped), or an array of strings, which will
252
+ # be interpreted as string fragments that will be concatenated and wrapped.
253
+ #
254
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
250
255
  #
251
256
  def desc=(desc)
252
257
  @desc = Utils::WrappableString.make(desc)
253
258
  end
254
259
 
255
260
  ##
256
- # Set long description
257
- # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
261
+ # Set the long description strings.
262
+ #
263
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a single
264
+ # string (which will be wrapped), or an array of strings, which will be
265
+ # interpreted as string fragments that will be concatenated and wrapped.
266
+ #
267
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
258
268
  #
259
269
  def long_desc=(long_desc)
260
270
  @long_desc = Utils::WrappableString.make_array(long_desc)
@@ -0,0 +1,574 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ require "optparse"
31
+
32
+ require "toys/utils/wrappable_string"
33
+
34
+ module Toys
35
+ module Definition
36
+ ##
37
+ # A Tool is a single command that can be invoked using Toys.
38
+ # It has a name, a series of one or more words that you use to identify
39
+ # the tool on the command line. It also has a set of formal flags and
40
+ # command line arguments supported, and a block that gets run when the
41
+ # tool is executed.
42
+ #
43
+ class Tool
44
+ ##
45
+ # Built-in acceptors (i.e. those recognized by OptionParser).
46
+ # You can reference these acceptors directly. Otherwise, you have to add
47
+ # one explicitly to the tool using {Tool#add_acceptor}.
48
+ #
49
+ OPTPARSER_ACCEPTORS = [
50
+ ::Object,
51
+ ::NilClass,
52
+ ::String,
53
+ ::Integer,
54
+ ::Float,
55
+ ::Numeric,
56
+ ::TrueClass,
57
+ ::FalseClass,
58
+ ::Array,
59
+ ::Regexp,
60
+ ::OptionParser::DecimalInteger,
61
+ ::OptionParser::OctalInteger,
62
+ ::OptionParser::DecimalNumeric
63
+ ].freeze
64
+
65
+ ##
66
+ # Create a new tool.
67
+ # @private
68
+ #
69
+ def initialize(loader, parent, full_name, priority, middleware_stack)
70
+ @parent = parent
71
+ @full_name = full_name.dup.freeze
72
+ @tool_class = DSL::Tool.new_class(@full_name, priority, loader)
73
+ @priority = priority
74
+ @middleware_stack = Middleware.resolve_stack(middleware_stack)
75
+
76
+ @source_path = nil
77
+ @definition_finished = false
78
+
79
+ @desc = Toys::Utils::WrappableString.new("")
80
+ @long_desc = []
81
+
82
+ @default_data = {}
83
+ @acceptors = {}
84
+ OPTPARSER_ACCEPTORS.each { |a| @acceptors[a] = a }
85
+ @used_flags = []
86
+
87
+ @helpers = {}
88
+
89
+ @flag_definitions = []
90
+ @required_arg_definitions = []
91
+ @optional_arg_definitions = []
92
+ @remaining_args_definition = nil
93
+ @runnable = false
94
+ end
95
+
96
+ ##
97
+ # Return the name of the tool as an array of strings.
98
+ # This array may not be modified.
99
+ # @return [Array<String>]
100
+ #
101
+ attr_reader :full_name
102
+
103
+ ##
104
+ # Return the priority of this tool definition.
105
+ # @return [Integer]
106
+ #
107
+ attr_reader :priority
108
+
109
+ ##
110
+ # Return the tool class.
111
+ # @return [Class]
112
+ #
113
+ attr_reader :tool_class
114
+
115
+ ##
116
+ # Returns the short description string.
117
+ # @return [Toys::Utils::WrappableString]
118
+ #
119
+ attr_reader :desc
120
+
121
+ ##
122
+ # Returns the long description strings as an array.
123
+ # @return [Array<Toys::Utils::WrappableString>]
124
+ #
125
+ attr_reader :long_desc
126
+
127
+ ##
128
+ # Return a list of all defined flags.
129
+ # @return [Array<Toys::Definition::Flag>]
130
+ #
131
+ attr_reader :flag_definitions
132
+
133
+ ##
134
+ # Return a list of all defined required positional arguments.
135
+ # @return [Array<Toys::Definition::Arg>]
136
+ #
137
+ attr_reader :required_arg_definitions
138
+
139
+ ##
140
+ # Return a list of all defined optional positional arguments.
141
+ # @return [Array<Toys::Definition::Arg>]
142
+ #
143
+ attr_reader :optional_arg_definitions
144
+
145
+ ##
146
+ # Return the remaining arguments specification, or `nil` if remaining
147
+ # arguments are currently not supported by this tool.
148
+ # @return [Toys::Definition::Arg,nil]
149
+ #
150
+ attr_reader :remaining_args_definition
151
+
152
+ ##
153
+ # Return a list of flags that have been used in the flag definitions.
154
+ # @return [Array<String>]
155
+ #
156
+ attr_reader :used_flags
157
+
158
+ ##
159
+ # Return the default argument data.
160
+ # @return [Hash]
161
+ #
162
+ attr_reader :default_data
163
+
164
+ ##
165
+ # Returns the middleware stack
166
+ # @return [Array<Object>]
167
+ #
168
+ attr_reader :middleware_stack
169
+
170
+ ##
171
+ # Returns the path to the file that contains the definition of this tool.
172
+ # @return [String]
173
+ #
174
+ attr_reader :source_path
175
+
176
+ ##
177
+ # Returns the local name of this tool.
178
+ # @return [String]
179
+ #
180
+ def simple_name
181
+ full_name.last
182
+ end
183
+
184
+ ##
185
+ # Returns a displayable name of this tool, generally the full name
186
+ # delimited by spaces.
187
+ # @return [String]
188
+ #
189
+ def display_name
190
+ full_name.join(" ")
191
+ end
192
+
193
+ ##
194
+ # Returns true if this tool is a root tool.
195
+ # @return [Boolean]
196
+ #
197
+ def root?
198
+ full_name.empty?
199
+ end
200
+
201
+ ##
202
+ # Returns true if this tool is marked as runnable.
203
+ # @return [Boolean]
204
+ #
205
+ def runnable?
206
+ @runnable
207
+ end
208
+
209
+ ##
210
+ # Returns true if there is a specific description set for this tool.
211
+ # @return [Boolean]
212
+ #
213
+ def includes_description?
214
+ !long_desc.empty? || !desc.empty?
215
+ end
216
+
217
+ ##
218
+ # Returns true if at least one flag or positional argument is defined
219
+ # for this tool.
220
+ # @return [Boolean]
221
+ #
222
+ def includes_arguments?
223
+ !default_data.empty? || !flag_definitions.empty? ||
224
+ !required_arg_definitions.empty? || !optional_arg_definitions.empty? ||
225
+ !remaining_args_definition.nil?
226
+ end
227
+
228
+ ##
229
+ # Returns true if this tool has any definition information.
230
+ # @return [Boolean]
231
+ #
232
+ def includes_definition?
233
+ includes_arguments? || runnable?
234
+ end
235
+
236
+ ##
237
+ # Returns true if this tool's definition has been finished and is locked.
238
+ # @return [Boolean]
239
+ #
240
+ def definition_finished?
241
+ @definition_finished
242
+ end
243
+
244
+ ##
245
+ # Returns all arg definitions in order: required, optional, remaining.
246
+ # @return [Array<Toys::Definition::Arg>]
247
+ #
248
+ def arg_definitions
249
+ result = required_arg_definitions + optional_arg_definitions
250
+ result << remaining_args_definition if remaining_args_definition
251
+ result
252
+ end
253
+
254
+ ##
255
+ # Returns a list of all custom acceptors used by this tool.
256
+ # @return [Array<Toys::Tool::Acceptor>]
257
+ #
258
+ def custom_acceptors
259
+ result = []
260
+ flag_definitions.each do |f|
261
+ result << f.accept if f.accept.is_a?(Acceptor)
262
+ end
263
+ arg_definitions.each do |a|
264
+ result << a.accept if a.accept.is_a?(Acceptor)
265
+ end
266
+ result.uniq
267
+ end
268
+
269
+ ##
270
+ # Get the named helper from this tool or its ancestors.
271
+ #
272
+ # @param [String] name The helper name
273
+ # @return [Module,nil] The helper module, or `nil` if not found.
274
+ #
275
+ def resolve_helper(name)
276
+ @helpers.fetch(name.to_s) { |k| @parent ? @parent.resolve_helper(k) : nil }
277
+ end
278
+
279
+ ##
280
+ # Include the given helper in the tool class.
281
+ #
282
+ # @param [String,Symbol,Module] name The helper name or module
283
+ #
284
+ def include_helper(name)
285
+ tool_class.include(name)
286
+ self
287
+ end
288
+
289
+ ##
290
+ # Sets the path to the file that defines this tool.
291
+ # A tool may be defined from at most one path. If a different path is
292
+ # already set, raises {Toys::ToolDefinitionError}
293
+ #
294
+ # @param [String] path The path to the file defining this tool
295
+ #
296
+ def lock_source_path(path)
297
+ if source_path && source_path != path
298
+ raise ToolDefinitionError,
299
+ "Cannot redefine tool #{display_name.inspect} in #{path}" \
300
+ " (already defined in #{source_path})"
301
+ end
302
+ @source_path = path
303
+ end
304
+
305
+ ##
306
+ # Set the short description string.
307
+ #
308
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
309
+ # single string (which will be wrapped), or an array of strings, which will
310
+ # be interpreted as string fragments that will be concatenated and wrapped.
311
+ #
312
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
313
+ #
314
+ def desc=(desc)
315
+ check_definition_state
316
+ @desc = Utils::WrappableString.make(desc)
317
+ end
318
+
319
+ ##
320
+ # Set the long description strings.
321
+ #
322
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a single
323
+ # string (which will be wrapped), or an array of strings, which will be
324
+ # interpreted as string fragments that will be concatenated and wrapped.
325
+ #
326
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] long_desc
327
+ #
328
+ def long_desc=(long_desc)
329
+ check_definition_state
330
+ @long_desc = Utils::WrappableString.make_array(long_desc)
331
+ end
332
+
333
+ ##
334
+ # Add an acceptor to the tool. This acceptor may be refereneced by name
335
+ # when adding a flag or an arg.
336
+ #
337
+ # @param [Toys::Tool::Acceptor] acceptor The acceptor to add.
338
+ #
339
+ def add_acceptor(acceptor)
340
+ @acceptors[acceptor.name] = acceptor
341
+ self
342
+ end
343
+
344
+ ##
345
+ # Add a named helper module to this tool.
346
+ #
347
+ # @param [String] name The name of the helper.
348
+ # @param [Module] helper_module The helper module.
349
+ #
350
+ def add_helper(name, helper_module)
351
+ @helpers[name.to_s] = helper_module
352
+ self
353
+ end
354
+
355
+ ##
356
+ # Add a flag to the current tool. Each flag must specify a key which
357
+ # the script may use to obtain the flag value from the context.
358
+ # You may then provide the flags themselves in `OptionParser` form.
359
+ #
360
+ # @param [Symbol] key The key to use to retrieve the value from the
361
+ # execution context.
362
+ # @param [Array<String>] flags The flags in OptionParser format.
363
+ # @param [Object] accept An acceptor that validates and/or converts the
364
+ # value. You may provide either the name of an acceptor you have
365
+ # defined, or one of the default acceptors provided by OptionParser.
366
+ # Optional. If not specified, accepts any value as a string.
367
+ # @param [Object] default The default value. This is the value that will
368
+ # be set in the context if this flag is not provided on the command
369
+ # line. Defaults to `nil`.
370
+ # @param [Proc,nil] handler An optional handler for setting/updating the
371
+ # value. If given, it should take two arguments, the new given value
372
+ # and the previous value, and it should return the new value that
373
+ # should be set. The default handler simply replaces the previous
374
+ # value. i.e. the default is effectively `-> (val, _prev) { val }`.
375
+ # @param [Boolean] report_collisions Raise an exception if a flag is
376
+ # requested that is already in use or marked as disabled. Default is
377
+ # true.
378
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
379
+ # description for the flag. See {Toys::Tool#desc=} for a description of
380
+ # allowed formats. Defaults to the empty string.
381
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
382
+ # Long description for the flag. See {Toys::Tool#long_desc=} for a
383
+ # description of allowed formats. Defaults to the empty array.
384
+ #
385
+ def add_flag(key, flags = [],
386
+ accept: nil, default: nil, handler: nil,
387
+ report_collisions: true,
388
+ desc: nil, long_desc: nil)
389
+ check_definition_state
390
+ accept = resolve_acceptor(accept)
391
+ flag_def = Definition::Flag.new(key, flags, @used_flags, report_collisions,
392
+ accept, handler, default)
393
+ flag_def.desc = desc if desc
394
+ flag_def.long_desc = long_desc if long_desc
395
+ @flag_definitions << flag_def if flag_def.active?
396
+ @default_data[key] = default
397
+ self
398
+ end
399
+
400
+ ##
401
+ # Mark one or more flags as disabled, preventing their use by any
402
+ # subsequent flag definition. This may be used to prevent middleware from
403
+ # defining a particular flag.
404
+ #
405
+ # @param [String...] flags The flags to disable
406
+ #
407
+ def disable_flag(*flags)
408
+ flags = flags.uniq
409
+ intersection = @used_flags & flags
410
+ unless intersection.empty?
411
+ raise ToolDefinitionError, "Cannot disable flags already used: #{intersection.inspect}"
412
+ end
413
+ @used_flags.concat(flags)
414
+ self
415
+ end
416
+
417
+ ##
418
+ # Add a required positional argument to the current tool. You must specify
419
+ # a key which the script may use to obtain the argument value from the
420
+ # context.
421
+ #
422
+ # @param [Symbol] key The key to use to retrieve the value from the
423
+ # execution context.
424
+ # @param [Object] accept An acceptor that validates and/or converts the
425
+ # value. You may provide either the name of an acceptor you have
426
+ # defined, or one of the default acceptors provided by OptionParser.
427
+ # Optional. If not specified, accepts any value as a string.
428
+ # @param [String] display_name A name to use for display (in help text and
429
+ # error reports). Defaults to the key in upper case.
430
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
431
+ # description for the flag. See {Toys::Tool#desc=} for a description of
432
+ # allowed formats. Defaults to the empty string.
433
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
434
+ # Long description for the flag. See {Toys::Tool#long_desc=} for a
435
+ # description of allowed formats. Defaults to the empty array.
436
+ #
437
+ def add_required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil)
438
+ check_definition_state
439
+ accept = resolve_acceptor(accept)
440
+ arg_def = Definition::Arg.new(key, :required, accept, nil, desc, long_desc, display_name)
441
+ @required_arg_definitions << arg_def
442
+ self
443
+ end
444
+
445
+ ##
446
+ # Add an optional positional argument to the current tool. You must specify
447
+ # a key which the script may use to obtain the argument value from the
448
+ # context. If an optional argument is not given on the command line, the
449
+ # value is set to the given default.
450
+ #
451
+ # @param [Symbol] key The key to use to retrieve the value from the
452
+ # execution context.
453
+ # @param [Object] default The default value. This is the value that will
454
+ # be set in the context if this argument is not provided on the command
455
+ # line. Defaults to `nil`.
456
+ # @param [Object] accept An acceptor that validates and/or converts the
457
+ # value. You may provide either the name of an acceptor you have
458
+ # defined, or one of the default acceptors provided by OptionParser.
459
+ # Optional. If not specified, accepts any value as a string.
460
+ # @param [String] display_name A name to use for display (in help text and
461
+ # error reports). Defaults to the key in upper case.
462
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
463
+ # description for the flag. See {Toys::Tool#desc=} for a description of
464
+ # allowed formats. Defaults to the empty string.
465
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
466
+ # Long description for the flag. See {Toys::Tool#long_desc=} for a
467
+ # description of allowed formats. Defaults to the empty array.
468
+ #
469
+ def add_optional_arg(key, default: nil, accept: nil, display_name: nil,
470
+ desc: nil, long_desc: nil)
471
+ check_definition_state
472
+ accept = resolve_acceptor(accept)
473
+ arg_def = Definition::Arg.new(key, :optional, accept, default,
474
+ desc, long_desc, display_name)
475
+ @optional_arg_definitions << arg_def
476
+ @default_data[key] = default
477
+ self
478
+ end
479
+
480
+ ##
481
+ # Specify what should be done with unmatched positional arguments. You must
482
+ # specify a key which the script may use to obtain the remaining args
483
+ # from the context.
484
+ #
485
+ # @param [Symbol] key The key to use to retrieve the value from the
486
+ # execution context.
487
+ # @param [Object] default The default value. This is the value that will
488
+ # be set in the context if no unmatched arguments are provided on the
489
+ # command line. Defaults to the empty array `[]`.
490
+ # @param [Object] accept An acceptor that validates and/or converts the
491
+ # value. You may provide either the name of an acceptor you have
492
+ # defined, or one of the default acceptors provided by OptionParser.
493
+ # Optional. If not specified, accepts any value as a string.
494
+ # @param [String] display_name A name to use for display (in help text and
495
+ # error reports). Defaults to the key in upper case.
496
+ # @param [String,Array<String>,Toys::Utils::WrappableString] desc Short
497
+ # description for the flag. See {Toys::Tool#desc=} for a description of
498
+ # allowed formats. Defaults to the empty string.
499
+ # @param [Array<String,Array<String>,Toys::Utils::WrappableString>] long_desc
500
+ # Long description for the flag. See {Toys::Tool#long_desc=} for a
501
+ # description of allowed formats. Defaults to the empty array.
502
+ #
503
+ def set_remaining_args(key, default: [], accept: nil, display_name: nil,
504
+ desc: nil, long_desc: nil)
505
+ check_definition_state
506
+ accept = resolve_acceptor(accept)
507
+ arg_def = Definition::Arg.new(key, :remaining, accept, default,
508
+ desc, long_desc, display_name)
509
+ @remaining_args_definition = arg_def
510
+ @default_data[key] = default
511
+ self
512
+ end
513
+
514
+ ##
515
+ # Set the runnable block
516
+ #
517
+ # @param [Proc] proc The runnable block
518
+ #
519
+ def runnable=(proc)
520
+ @tool_class.run(&proc)
521
+ end
522
+
523
+ ##
524
+ # Mark this tool as runnable. Should be called from the DSL only.
525
+ # @private
526
+ #
527
+ def mark_runnable
528
+ check_definition_state
529
+ @runnable = true
530
+ self
531
+ end
532
+
533
+ ##
534
+ # Complete definition and run middleware configs. Should be called from
535
+ # the Loader only.
536
+ # @private
537
+ #
538
+ def finish_definition(loader)
539
+ unless @definition_finished
540
+ ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
541
+ config_proc = proc {}
542
+ middleware_stack.reverse.each do |middleware|
543
+ config_proc = make_config_proc(middleware, loader, config_proc)
544
+ end
545
+ config_proc.call
546
+ end
547
+ @definition_finished = true
548
+ end
549
+ self
550
+ end
551
+
552
+ private
553
+
554
+ def make_config_proc(middleware, loader, next_config)
555
+ proc { middleware.config(self, loader, &next_config) }
556
+ end
557
+
558
+ def check_definition_state
559
+ if @definition_finished
560
+ raise ToolDefinitionError,
561
+ "Defintion of tool #{display_name.inspect} is already finished"
562
+ end
563
+ end
564
+
565
+ def resolve_acceptor(accept)
566
+ return accept if accept.nil? || accept.is_a?(Acceptor)
567
+ unless @acceptors.key?(accept)
568
+ raise ToolDefinitionError, "Unknown acceptor: #{accept.inspect}"
569
+ end
570
+ @acceptors[accept]
571
+ end
572
+ end
573
+ end
574
+ end