toys-core 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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