toys-core 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,20 +28,21 @@
28
28
  ;
29
29
 
30
30
  require "toys/middleware/base"
31
+ require "toys/utils/line_output"
31
32
 
32
33
  module Toys
33
34
  module Middleware
34
35
  ##
35
36
  # A middleware that displays a version string for certain tools if the
36
- # `--version` switch is given. You can specify which tools respond to
37
- # this switch, and the string that will be displayed.
37
+ # `--version` flag is given. You can specify which tools respond to
38
+ # this flag, and the string that will be displayed.
38
39
  #
39
40
  class ShowVersion < Base
40
41
  ##
41
- # Default version switches
42
+ # Default version flags
42
43
  # @return [Array<String>]
43
44
  #
44
- DEFAULT_VERSION_SWITCHES = ["--version"].freeze
45
+ DEFAULT_VERSION_FLAGS = ["--version"].freeze
45
46
 
46
47
  ##
47
48
  # Return a simple version displayer that returns the given string for
@@ -58,28 +59,31 @@ module Toys
58
59
  #
59
60
  # @param [Proc] version_displayer A proc that takes a tool and returns
60
61
  # either the version string that should be displayed, or a falsy
61
- # value to indicate the tool should not have a `--version` switch.
62
+ # value to indicate the tool should not have a `--version` flag.
62
63
  # Defaults to a "null" displayer that returns false for all tools.
63
- # @param [Array<String>] version_switches A list of switches that should
64
+ # @param [Array<String>] version_flags A list of flags that should
64
65
  # trigger displaying the version. Default is
65
- # {DEFAULT_VERSION_SWITCHES}.
66
+ # {DEFAULT_VERSION_FLAGS}.
67
+ # @param [IO] stream Output stream to write to. Default is stdout.
66
68
  #
67
69
  def initialize(version_displayer: nil,
68
- version_switches: DEFAULT_VERSION_SWITCHES)
70
+ version_flags: DEFAULT_VERSION_FLAGS,
71
+ stream: $stdout)
69
72
  @version_displayer = version_displayer || proc { |_| false }
70
- @version_switches = version_switches
73
+ @version_flags = version_flags
74
+ @output = Utils::LineOutput.new(stream)
71
75
  end
72
76
 
73
77
  ##
74
- # Adds the version switch if requested.
78
+ # Adds the version flag if requested.
75
79
  #
76
- def config(tool)
80
+ def config(tool, _loader)
77
81
  version = @version_displayer.call(tool)
78
82
  if version
79
- tool.add_switch(:_show_version, *@version_switches,
80
- docs: "Show version",
81
- handler: ->(_val, _prev) { version },
82
- only_unique: true)
83
+ tool.add_flag(:_show_version, *@version_flags,
84
+ desc: "Display the version",
85
+ handler: ->(_val, _prev) { version },
86
+ only_unique: true)
83
87
  end
84
88
  yield
85
89
  end
@@ -89,7 +93,7 @@ module Toys
89
93
  #
90
94
  def execute(context)
91
95
  if context[:_show_version]
92
- puts context[:_show_version]
96
+ @output.puts context[:_show_version]
93
97
  else
94
98
  yield
95
99
  end
@@ -72,7 +72,10 @@ module Toys
72
72
  files.uniq!
73
73
 
74
74
  files.each do |file|
75
- rm_rf file
75
+ if ::File.exist?(file)
76
+ rm_rf file
77
+ puts "Cleaned: #{file}"
78
+ end
76
79
  end
77
80
  end
78
81
  end
@@ -88,6 +88,8 @@ module Toys
88
88
  tool(template.name) do
89
89
  desc "#{task_type} the gem: #{template.gem_name}"
90
90
 
91
+ flag :yes, "-y", "--yes", desc: "Do not ask for interactive confirmation"
92
+
91
93
  use :exec
92
94
  use :fileutils
93
95
  use :highline
@@ -105,7 +107,7 @@ module Toys
105
107
  logger.error "Cannot push the gem when there are uncommited changes"
106
108
  exit(1)
107
109
  end
108
- exit(1) unless agree("Release #{gemfile}? (y/n) ")
110
+ exit(1) unless options[:yes] || agree("Release #{gemfile}? (y/n) ")
109
111
  sh "gem push pkg/#{gemfile}"
110
112
  if template.tag
111
113
  sh "git tag v#{version}"
@@ -86,12 +86,11 @@ module Toys
86
86
 
87
87
  use :exec
88
88
 
89
- switch(
90
- :warnings, "-w", "--[no-]warnings",
91
- default: template.warnings,
92
- docs: "Turn on Ruby warnings (defaults to #{template.warnings})"
93
- )
94
- remaining_args(:tests, docs: "Paths to the tests to run (defaults to all tests)")
89
+ flag :warnings, "-w", "--[no-]warnings",
90
+ default: template.warnings,
91
+ desc: "Turn on Ruby warnings (defaults to #{template.warnings})"
92
+
93
+ remaining_args :tests, desc: "Paths to the tests to run (defaults to all tests)"
95
94
 
96
95
  execute do
97
96
  ruby_args = []
data/lib/toys/tool.rb CHANGED
@@ -35,7 +35,7 @@ module Toys
35
35
  ##
36
36
  # A Tool is a single command that can be invoked using Toys.
37
37
  # It has a name, a series of one or more words that you use to identify
38
- # the tool on the command line. It also has a set of formal switches and
38
+ # the tool on the command line. It also has a set of formal flags and
39
39
  # command line arguments supported, and a block that gets run when the
40
40
  # tool is executed.
41
41
  #
@@ -52,11 +52,11 @@ module Toys
52
52
  @definition_path = nil
53
53
  @definition_finished = false
54
54
 
55
- @desc = []
55
+ @desc = Toys::Utils::WrappableString.new("")
56
56
  @long_desc = []
57
57
 
58
58
  @default_data = {}
59
- @switch_definitions = []
59
+ @flag_definitions = []
60
60
  @required_arg_definitions = []
61
61
  @optional_arg_definitions = []
62
62
  @remaining_args_definition = nil
@@ -74,10 +74,22 @@ module Toys
74
74
  attr_reader :full_name
75
75
 
76
76
  ##
77
- # Return a list of all defined switches.
78
- # @return [Array<Toys::Tool::SwitchDefinition>]
77
+ # Returns the short description string.
78
+ # @return [Toys::Utils::WrappableString]
79
79
  #
80
- attr_reader :switch_definitions
80
+ attr_reader :desc
81
+
82
+ ##
83
+ # Returns the long description strings as an array.
84
+ # @return [Array<Toys::Utils::WrappableString>]
85
+ #
86
+ attr_reader :long_desc
87
+
88
+ ##
89
+ # Return a list of all defined flags.
90
+ # @return [Array<Toys::Tool::FlagDefinition>]
91
+ #
92
+ attr_reader :flag_definitions
81
93
 
82
94
  ##
83
95
  # Return a list of all defined required positional arguments.
@@ -167,45 +179,21 @@ module Toys
167
179
  executor.is_a?(::Proc)
168
180
  end
169
181
 
170
- ##
171
- # Returns the effective short description for this tool. This will be
172
- # displayed when this tool is listed in a command list.
173
- #
174
- # @param [Integer,nil] wrap_width Wrap wrappable strings to the given
175
- # width, or `nil` for no wrapping.
176
- # @return [Array<String>]
177
- #
178
- def effective_desc(wrap_width: nil)
179
- Tool.resolve_wrapping(@desc, wrap_width)
180
- end
181
-
182
- ##
183
- # Returns the effective long description for this tool. This will be
184
- # displayed as part of the usage for this particular tool.
185
- #
186
- # @param [Integer,nil] wrap_width Wrap wrappable strings to the given
187
- # width, or `nil` for no wrapping.
188
- # @return [Array<String>]
189
- #
190
- def effective_long_desc(wrap_width: nil)
191
- Tool.resolve_wrapping(@long_desc.empty? ? @desc : @long_desc, wrap_width)
192
- end
193
-
194
182
  ##
195
183
  # Returns true if there is a specific description set for this tool.
196
184
  # @return [Boolean]
197
185
  #
198
186
  def includes_description?
199
- !@long_desc.empty? || !@desc.empty?
187
+ !long_desc.empty? || !desc.empty?
200
188
  end
201
189
 
202
190
  ##
203
- # Returns true if at least one switch or positional argument is defined
191
+ # Returns true if at least one flag or positional argument is defined
204
192
  # for this tool.
205
193
  # @return [Boolean]
206
194
  #
207
195
  def includes_arguments?
208
- !default_data.empty? || !switch_definitions.empty? ||
196
+ !default_data.empty? || !flag_definitions.empty? ||
209
197
  !required_arg_definitions.empty? || !optional_arg_definitions.empty? ||
210
198
  !remaining_args_definition.nil?
211
199
  end
@@ -228,11 +216,29 @@ module Toys
228
216
  end
229
217
 
230
218
  ##
231
- # Returns a list of switch flags used by this tool.
219
+ # Returns true if this tool's definition has been finished and is locked.
220
+ # @return [Boolean]
221
+ #
222
+ def definition_finished?
223
+ @definition_finished
224
+ end
225
+
226
+ ##
227
+ # Returns all arg definitions in order: required, optional, remaining.
228
+ # @return [Array<Toys::Tool::ArgDefinition>]
229
+ #
230
+ def arg_definitions
231
+ result = required_arg_definitions + optional_arg_definitions
232
+ result << remaining_args_definition if remaining_args_definition
233
+ result
234
+ end
235
+
236
+ ##
237
+ # Returns a list of flags used by this tool.
232
238
  # @return [Array<String>]
233
239
  #
234
- def used_switches
235
- @switch_definitions.reduce([]) { |used, sdef| used + sdef.effective_switches }.uniq
240
+ def used_flags
241
+ flag_definitions.reduce([]) { |used, fdef| used + fdef.effective_flags }.uniq
236
242
  end
237
243
 
238
244
  ##
@@ -242,33 +248,56 @@ module Toys
242
248
  #
243
249
  # @param [String] path The path to the file defining this tool
244
250
  #
245
- def definition_path=(path)
246
- if @definition_path && @definition_path != path
251
+ def lock_definition_path(path)
252
+ if definition_path && definition_path != path
247
253
  raise ToolDefinitionError,
248
254
  "Cannot redefine tool #{display_name.inspect} in #{path}" \
249
- " (already defined in #{@definition_path})"
255
+ " (already defined in #{definition_path})"
250
256
  end
251
257
  @definition_path = path
252
258
  end
253
259
 
254
260
  ##
255
- # Set the short description.
261
+ # Set the short description string.
262
+ #
263
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
264
+ # single string (which will be wrapped), or an array of strings, which will
265
+ # be interpreted as string fragments that will be concatenated and wrapped.
256
266
  #
257
- # @param [String,Array<String>] strs The short description
267
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
258
268
  #
259
- def desc=(strs)
269
+ def desc=(desc)
260
270
  check_definition_state
261
- @desc = Tool.canonicalize_desc(strs)
271
+ @desc = Tool.canonicalize_desc(desc)
262
272
  end
263
273
 
264
274
  ##
265
- # Set the long description.
275
+ # Set the long description strings.
266
276
  #
267
- # @param [String,Array<String>] strs The long description
277
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a single
278
+ # string (which will be wrapped), or an array of strings, which will be
279
+ # interpreted as string fragments that will be concatenated and wrapped.
268
280
  #
269
- def long_desc=(strs)
281
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] descs
282
+ #
283
+ def long_desc=(descs)
270
284
  check_definition_state
271
- @long_desc = Tool.canonicalize_desc(strs)
285
+ @long_desc = Tool.canonicalize_long_desc(descs)
286
+ end
287
+
288
+ ##
289
+ # Set the long description strings.
290
+ #
291
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a single
292
+ # string (which will be wrapped), or an array of strings, which will be
293
+ # interpreted as string fragments that will be concatenated and wrapped.
294
+ #
295
+ # @param [Toys::Utils::WrappableString,String,Array<String>...] descs
296
+ #
297
+ def populate_long_desc(*descs)
298
+ check_definition_state
299
+ @long_desc = Tool.canonicalize_long_desc(descs)
300
+ self
272
301
  end
273
302
 
274
303
  ##
@@ -312,24 +341,27 @@ module Toys
312
341
  end
313
342
 
314
343
  ##
315
- # Add a switch to the current tool. Each switch must specify a key which
316
- # the executor may use to obtain the switch value from the context.
317
- # You may then provide the switches themselves in `OptionParser` form.
344
+ # Add a flag to the current tool. Each flag must specify a key which
345
+ # the executor may use to obtain the flag value from the context.
346
+ # You may then provide the flags themselves in `OptionParser` form.
318
347
  #
319
348
  # @param [Symbol] key The key to use to retrieve the value from the
320
349
  # execution context.
321
- # @param [String...] switches The switches in OptionParser format.
350
+ # @param [String...] flags The flags in OptionParser format.
322
351
  # @param [Object,nil] accept An OptionParser acceptor. Optional.
323
352
  # @param [Object] default The default value. This is the value that will
324
- # be set in the context if this switch is not provided on the command
353
+ # be set in the context if this flag is not provided on the command
325
354
  # line. Defaults to `nil`.
326
355
  # @param [String,Toys::Utils::WrappableString,
327
- # Array<String,Toys::Utils::WrappableString>] docs Documentation for
328
- # the switch. Defaults to empty array.
329
- # @param [Boolean] only_unique If true, any switches that are already
330
- # defined in this tool are removed from this switch. For example, if
331
- # an earlier switch uses `-a`, and this switch wants to use both
332
- # `-a` and `-b`, then only `-b` will be assigned to this switch.
356
+ # Array<String,Toys::Utils::WrappableString>] desc Short description
357
+ # for the flag. Defaults to empty array.
358
+ # @param [String,Toys::Utils::WrappableString,
359
+ # Array<String,Toys::Utils::WrappableString>] long_desc Long
360
+ # description for the flag. Defaults to empty array.
361
+ # @param [Boolean] only_unique If true, any flags that are already
362
+ # defined in this tool are removed from this flag. For example, if
363
+ # an earlier flag uses `-a`, and this flag wants to use both
364
+ # `-a` and `-b`, then only `-b` will be assigned to this flag.
333
365
  # Defaults to false.
334
366
  # @param [Proc,nil] handler An optional handler for setting/updating the
335
367
  # value. If given, it should take two arguments, the new given value
@@ -337,17 +369,21 @@ module Toys
337
369
  # should be set. The default handler simply replaces the previous
338
370
  # value. i.e. the default is effectively `-> (val, _prev) { val }`.
339
371
  #
340
- def add_switch(key, *switches,
341
- accept: nil, default: nil, docs: nil, only_unique: false, handler: nil)
372
+ def add_flag(key, *flags,
373
+ accept: nil, default: nil, desc: nil, long_desc: nil,
374
+ only_unique: false, handler: nil)
342
375
  check_definition_state
343
- switch_info = SwitchDefinition.new(key, switches, accept, docs, handler)
344
- if only_unique
345
- switch_info.remove_switches(used_switches)
346
- end
347
- if switch_info.active?
348
- @default_data[key] = default
349
- @switch_definitions << switch_info
350
- end
376
+ flag_def = FlagDefinition.new(self, key)
377
+ flag_def.add_flags(flags)
378
+ flag_def.accept = accept
379
+ flag_def.handler = handler
380
+ flag_def.default = default
381
+ flag_def.desc = desc unless desc.nil?
382
+ flag_def.long_desc = long_desc unless long_desc.nil?
383
+ yield flag_def if block_given?
384
+ flag_def.create_default_flag_if_needed
385
+ flag_def.remove_flags(used_flags) if only_unique
386
+ @flag_definitions << flag_def if flag_def.active?
351
387
  self
352
388
  end
353
389
 
@@ -359,14 +395,25 @@ module Toys
359
395
  # @param [Symbol] key The key to use to retrieve the value from the
360
396
  # execution context.
361
397
  # @param [Object,nil] accept An OptionParser acceptor. Optional.
398
+ # @param [String] display_name A name to use for display (in help text and
399
+ # error reports). Defaults to the key in upper case.
400
+ # @param [String,Toys::Utils::WrappableString,
401
+ # Array<String,Toys::Utils::WrappableString>] desc Short description
402
+ # for the arg. Defaults to empty array.
362
403
  # @param [String,Toys::Utils::WrappableString,
363
- # Array<String,Toys::Utils::WrappableString>] docs Documentation for
364
- # the arg. Defaults to empty array.
404
+ # Array<String,Toys::Utils::WrappableString>] long_desc Long
405
+ # description for the arg. Defaults to empty array.
365
406
  #
366
- def add_required_arg(key, accept: nil, docs: nil)
407
+ def add_required_arg(key, accept: nil, display_name: nil, desc: nil, long_desc: nil)
367
408
  check_definition_state
368
- @default_data[key] = nil
369
- @required_arg_definitions << ArgDefinition.new(key, accept, docs)
409
+ arg_def = ArgDefinition.new(self, key, :required)
410
+ arg_def.accept = accept
411
+ arg_def.default = nil
412
+ arg_def.display_name = display_name unless display_name.nil?
413
+ arg_def.desc = desc unless desc.nil?
414
+ arg_def.long_desc = long_desc unless long_desc.nil?
415
+ yield arg_def if block_given?
416
+ @required_arg_definitions << arg_def
370
417
  self
371
418
  end
372
419
 
@@ -378,18 +425,30 @@ module Toys
378
425
  #
379
426
  # @param [Symbol] key The key to use to retrieve the value from the
380
427
  # execution context.
381
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
382
428
  # @param [Object] default The default value. This is the value that will
383
429
  # be set in the context if this argument is not provided on the command
384
430
  # line. Defaults to `nil`.
431
+ # @param [Object,nil] accept An OptionParser acceptor. Optional.
432
+ # @param [String] display_name A name to use for display (in help text and
433
+ # error reports). Defaults to the key in upper case.
385
434
  # @param [String,Toys::Utils::WrappableString,
386
- # Array<String,Toys::Utils::WrappableString>] docs Documentation for
387
- # the arg. Defaults to empty array.
435
+ # Array<String,Toys::Utils::WrappableString>] desc Short description
436
+ # for the arg. Defaults to empty array.
437
+ # @param [String,Toys::Utils::WrappableString,
438
+ # Array<String,Toys::Utils::WrappableString>] long_desc Long
439
+ # description for the arg. Defaults to empty array.
388
440
  #
389
- def add_optional_arg(key, accept: nil, default: nil, docs: nil)
441
+ def add_optional_arg(key, default: nil, accept: nil, display_name: nil,
442
+ desc: nil, long_desc: nil)
390
443
  check_definition_state
391
- @default_data[key] = default
392
- @optional_arg_definitions << ArgDefinition.new(key, accept, docs)
444
+ arg_def = ArgDefinition.new(self, key, :optional)
445
+ arg_def.accept = accept
446
+ arg_def.default = default
447
+ arg_def.display_name = display_name unless display_name.nil?
448
+ arg_def.desc = desc unless desc.nil?
449
+ arg_def.long_desc = long_desc unless long_desc.nil?
450
+ yield arg_def if block_given?
451
+ @optional_arg_definitions << arg_def
393
452
  self
394
453
  end
395
454
 
@@ -400,18 +459,30 @@ module Toys
400
459
  #
401
460
  # @param [Symbol] key The key to use to retrieve the value from the
402
461
  # execution context.
403
- # @param [Object,nil] accept An OptionParser acceptor. Optional.
404
462
  # @param [Object] default The default value. This is the value that will
405
463
  # be set in the context if no unmatched arguments are provided on the
406
464
  # command line. Defaults to the empty array `[]`.
465
+ # @param [Object,nil] accept An OptionParser acceptor. Optional.
466
+ # @param [String] display_name A name to use for display (in help text and
467
+ # error reports). Defaults to the key in upper case.
468
+ # @param [String,Toys::Utils::WrappableString,
469
+ # Array<String,Toys::Utils::WrappableString>] desc Short description
470
+ # for the arg. Defaults to empty array.
407
471
  # @param [String,Toys::Utils::WrappableString,
408
- # Array<String,Toys::Utils::WrappableString>] docs Documentation for
409
- # the args. Defaults to empty array.
472
+ # Array<String,Toys::Utils::WrappableString>] long_desc Long
473
+ # description for the arg. Defaults to empty array.
410
474
  #
411
- def set_remaining_args(key, accept: nil, default: [], docs: nil)
475
+ def set_remaining_args(key, default: [], accept: nil, display_name: nil,
476
+ desc: nil, long_desc: nil)
412
477
  check_definition_state
413
- @default_data[key] = default
414
- @remaining_args_definition = ArgDefinition.new(key, accept, docs)
478
+ arg_def = ArgDefinition.new(self, key, :remaining)
479
+ arg_def.accept = accept
480
+ arg_def.default = default
481
+ arg_def.display_name = display_name unless display_name.nil?
482
+ arg_def.desc = desc unless desc.nil?
483
+ arg_def.long_desc = long_desc unless long_desc.nil?
484
+ yield arg_def if block_given?
485
+ @remaining_args_definition = arg_def
415
486
  self
416
487
  end
417
488
 
@@ -437,33 +508,40 @@ module Toys
437
508
  # @return [Integer] The result code.
438
509
  #
439
510
  def execute(cli, args, verbosity: 0)
440
- finish_definition unless @definition_finished
441
- Execution.new(self).execute(cli, args, verbosity: verbosity)
511
+ ContextualError.capture_path(
512
+ "Error during tool execution!", definition_path,
513
+ tool_name: full_name, tool_args: args
514
+ ) do
515
+ Execution.new(self).execute(cli, args, verbosity: verbosity)
516
+ end
442
517
  end
443
518
 
444
519
  ##
445
520
  # Complete definition and run middleware configs
521
+ # @param [Toys::Loader] loader
446
522
  #
447
523
  # @private
448
524
  #
449
- def finish_definition
525
+ def finish_definition(loader)
450
526
  unless @definition_finished
451
- config_proc = proc {}
452
- middleware_stack.reverse.each do |middleware|
453
- config_proc = make_config_proc(middleware, config_proc)
527
+ ContextualError.capture("Error installing tool middleware!", tool_name: full_name) do
528
+ config_proc = proc {}
529
+ middleware_stack.reverse.each do |middleware|
530
+ config_proc = make_config_proc(middleware, loader, config_proc)
531
+ end
532
+ config_proc.call
454
533
  end
455
- config_proc.call
456
534
  @definition_finished = true
457
535
  end
458
536
  self
459
537
  end
460
538
 
461
539
  ##
462
- # Representation of a single switch
540
+ # Representation of a single flag
463
541
  #
464
- class SwitchSyntax
542
+ class FlagSyntax
465
543
  ##
466
- # Parse switch syntax
544
+ # Parse flag syntax
467
545
  # @param [String] str syntax.
468
546
  #
469
547
  def initialize(str)
@@ -474,58 +552,54 @@ module Toys
474
552
  elsif str =~ /^(--\w[\?\w-]*)(([=\s])(\w+))?$/
475
553
  setup(str, [$1], $1, "--", $3, $4)
476
554
  else
477
- raise ToolDefinitionError, "Illegal switch: #{str.inspect}"
555
+ raise ToolDefinitionError, "Illegal flag: #{str.inspect}"
478
556
  end
479
557
  end
480
558
 
481
- attr_reader :str
559
+ attr_reader :original_str
482
560
  attr_reader :str_without_value
483
- attr_reader :switches
484
- attr_reader :switch_style
561
+ attr_reader :flags
562
+ attr_reader :flag_style
485
563
  attr_reader :value_delim
486
564
  attr_reader :value_label
487
565
 
488
566
  private
489
567
 
490
- def setup(str, switches, str_without_value, switch_style, value_delim, value_label)
491
- @str = str
492
- @switches = switches
568
+ def setup(original_str, flags, str_without_value, flag_style, value_delim, value_label)
569
+ @original_str = original_str
570
+ @flags = flags
493
571
  @str_without_value = str_without_value
494
- @switch_style = switch_style
572
+ @flag_style = flag_style
495
573
  @value_delim = value_delim
496
- @value_label = value_label
574
+ @value_label = value_label ? value_label.upcase : value_label
497
575
  end
498
576
  end
499
577
 
500
578
  ##
501
- # Representation of a formal set of switches.
579
+ # Representation of a formal set of flags.
502
580
  #
503
- class SwitchDefinition
581
+ class FlagDefinition
582
+ ##
583
+ # The default handler replaces the previous value.
584
+ # @return [Proc]
585
+ #
586
+ DEFAULT_HANDLER = ->(val, _prev) { val }
587
+
504
588
  ##
505
- # Create a SwitchDefinition
589
+ # Create a FlagDefinition
506
590
  # @private
507
591
  #
508
- # @param [Symbol] key This switch will set the given context key.
509
- # @param [Array<String>] switches Switches in OptionParser format
510
- # @param [Object] accept An OptionParser acceptor, or `nil` for none.
511
- # @param [String,Toys::Utils::WrappableString,
512
- # Array<String,Toys::Utils::WrappableString>] docs Documentation
513
- # @param [Proc,nil] handler An optional handler for setting/updating the
514
- # value. If given, it should take two arguments, the new given value
515
- # and the previous value, and it should return the new value that
516
- # should be set. If `nil`, uses a default handler that just replaces
517
- # the previous value. i.e. the default is effectively
518
- # `-> (val, _prev) { val }`.
519
- #
520
- def initialize(key, switches, accept, docs, handler = nil)
592
+ # @param [Symbol] key This flag will set the given context key.
593
+ #
594
+ def initialize(tool, key)
595
+ @tool = tool
521
596
  @key = key
522
- switches = ["--#{Tool.canonical_switch(key)}=VALUE"] if switches.empty?
523
- @switch_syntax = switches.map { |s| SwitchSyntax.new(s) }
524
- @accept = accept
525
- @docs = Tool.canonicalize_desc(docs)
526
- @handler = handler || ->(val, _prev) { val }
597
+ @flag_syntax = []
598
+ @accept = nil
599
+ @handler = DEFAULT_HANDLER
600
+ @desc = ""
601
+ @long_desc = []
527
602
  reset_data
528
- @effective_switches = nil
529
603
  end
530
604
 
531
605
  ##
@@ -535,10 +609,10 @@ module Toys
535
609
  attr_reader :key
536
610
 
537
611
  ##
538
- # Returns an array of SwitchSyntax for the switches.
539
- # @return [Array<SwitchSyntax>]
612
+ # Returns an array of FlagSyntax for the flags.
613
+ # @return [Array<FlagSyntax>]
540
614
  #
541
- attr_reader :switch_syntax
615
+ attr_reader :flag_syntax
542
616
 
543
617
  ##
544
618
  # Returns the acceptor, which may be `nil`.
@@ -547,66 +621,150 @@ module Toys
547
621
  attr_reader :accept
548
622
 
549
623
  ##
550
- # Returns the documentation strings, which may be the empty array.
551
- # @return [Array<String>]
624
+ # Returns the short description string.
625
+ # @return [Toys::Utils::WrappableString]
626
+ #
627
+ attr_reader :desc
628
+
629
+ ##
630
+ # Returns the long description strings as an array.
631
+ # @return [Array<Toys::Utils::WrappableString>]
552
632
  #
553
- attr_reader :docs
633
+ attr_reader :long_desc
554
634
 
555
635
  ##
556
- # Returns the handler.
636
+ # Returns the handler for setting/updating the value.
557
637
  # @return [Proc]
558
638
  #
559
639
  attr_reader :handler
560
640
 
561
641
  ##
562
- # Returns an array of SwitchSyntax including only single-dash switches
563
- # @return [Array<SwitchSyntax>]
642
+ # Adds the given flags.
643
+ # @param [Array<String>] flags Flags in OptionParser format
564
644
  #
565
- def single_switch_syntax
566
- @single_switch_syntax ||= switch_syntax.find_all { |ss| ss.switch_style == "-" }
645
+ def add_flags(flags)
646
+ @flag_syntax.concat(flags.map { |s| FlagSyntax.new(s) })
647
+ reset_data
648
+ self
567
649
  end
568
650
 
569
651
  ##
570
- # Returns an array of SwitchSyntax including only double-dash switches
571
- # @return [Array<SwitchSyntax>]
652
+ # Set the acceptor.
653
+ # @param [Object] accept Acceptor. May be `nil` for the default.
572
654
  #
573
- def double_switch_syntax
574
- @double_switch_syntax ||= switch_syntax.find_all { |ss| ss.switch_style == "--" }
655
+ def accept=(accept)
656
+ @accept = accept
657
+ reset_data
575
658
  end
576
659
 
577
660
  ##
578
- # Returns the list of effective switches used.
579
- # @return [Array<String>]
661
+ # Set the default.
662
+ # @param [Object] default The default value.
663
+ #
664
+ def default=(default)
665
+ @tool.default_data[@key] = default
666
+ end
667
+
668
+ ##
669
+ # Set the handler for setting/updating the value.
670
+ # @param [Proc,nil] handler The handler for setting/updating the value.
671
+ # The handler should take two arguments, the new given value and the
672
+ # previous value, and it should return the new value that should be
673
+ # set. If `nil`, uses {DEFAULT_HANDLER}.
674
+ #
675
+ def handler=(handler)
676
+ @handler = handler || DEFAULT_HANDLER
677
+ end
678
+
679
+ ##
680
+ # Set the short description string.
681
+ #
682
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
683
+ # single string (which will be wrapped), or an array of strings, which
684
+ # will be interpreted as string fragments that will be concatenated and
685
+ # wrapped.
686
+ #
687
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
688
+ #
689
+ def desc=(desc)
690
+ @desc = Tool.canonicalize_desc(desc)
691
+ end
692
+
693
+ ##
694
+ # Set the long description strings.
695
+ #
696
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a
697
+ # single string (which will be wrapped), or an array of strings, which
698
+ # will be interpreted as string fragments that will be concatenated and
699
+ # wrapped.
580
700
  #
581
- def effective_switches
582
- @effective_switches ||= switch_syntax.map(&:switches).flatten
701
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] descs
702
+ #
703
+ def long_desc=(descs)
704
+ @long_desc = Tool.canonicalize_long_desc(descs)
583
705
  end
584
706
 
585
707
  ##
586
- # Returns the documentation strings with wrapping resolved.
708
+ # Set the long description strings.
709
+ #
710
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a
711
+ # single string (which will be wrapped), or an array of strings, which
712
+ # will be interpreted as string fragments that will be concatenated and
713
+ # wrapped.
587
714
  #
588
- # @param [Integer,nil] width Wrapping width, or `nil` to use default.
715
+ # @param [Toys::Utils::WrappableString,String,Array<String>...] descs
716
+ #
717
+ def populate_long_desc(*descs)
718
+ @long_desc = Tool.canonicalize_long_desc(descs)
719
+ self
720
+ end
721
+
722
+ ##
723
+ # Returns an array of FlagSyntax including only single-dash flags
724
+ # @return [Array<FlagSyntax>]
725
+ #
726
+ def single_flag_syntax
727
+ @single_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "-" }
728
+ end
729
+
730
+ ##
731
+ # Returns an array of FlagSyntax including only double-dash flags
732
+ # @return [Array<FlagSyntax>]
733
+ #
734
+ def double_flag_syntax
735
+ @double_flag_syntax ||= flag_syntax.find_all { |ss| ss.flag_style == "--" }
736
+ end
737
+
738
+ ##
739
+ # Returns the list of effective flags used.
589
740
  # @return [Array<String>]
590
741
  #
591
- def wrapped_docs(width)
592
- Tool.resolve_wrapping(docs, width)
742
+ def effective_flags
743
+ @effective_flags ||= flag_syntax.map(&:flags).flatten
593
744
  end
594
745
 
595
746
  ##
596
- # All optparser switches and acceptor if present
747
+ # All optparser flags and acceptor if present
597
748
  # @return [Array]
598
749
  #
599
750
  def optparser_info
600
- @optparser_info ||= switch_syntax.map(&:str) + Array(accept)
751
+ @optparser_info ||= begin
752
+ flags = flag_syntax.map do |fs|
753
+ f = fs.str_without_value
754
+ f = "#{f} #{value_label}" if value_label
755
+ f
756
+ end
757
+ flags + Array(accept)
758
+ end
601
759
  end
602
760
 
603
761
  ##
604
- # Returns true if this switch is active. That is, it has a nonempty
605
- # switches list.
762
+ # Returns true if this flag is active. That is, it has a nonempty
763
+ # flags list.
606
764
  # @return [Boolean]
607
765
  #
608
766
  def active?
609
- !effective_switches.empty?
767
+ !effective_flags.empty?
610
768
  end
611
769
 
612
770
  ##
@@ -627,47 +785,52 @@ module Toys
627
785
  @value_delim
628
786
  end
629
787
 
630
- ##
631
- # Removes the given switches.
632
- # @param [Array<String>] switches
633
- #
634
- def remove_switches(switches)
635
- @switch_syntax.select! do |ss|
636
- ss.switches.all? { |s| !switches.include?(s) }
788
+ ## @private
789
+ def remove_flags(flags)
790
+ flags = flags.map { |f| FlagSyntax.new(f).flags }.flatten
791
+ @flag_syntax.select! do |ss|
792
+ ss.flags.all? { |s| !flags.include?(s) }
637
793
  end
638
794
  reset_data
639
795
  self
640
796
  end
641
797
 
798
+ ## @private
799
+ def create_default_flag_if_needed
800
+ return unless @flag_syntax.empty?
801
+ canonical_flag = key.to_s.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "").sub(/^-+/, "")
802
+ unless canonical_flag.empty?
803
+ flag = @accept ? "--#{canonical_flag}=VALUE" : "--#{canonical_flag}"
804
+ @flag_syntax << FlagSyntax.new(flag)
805
+ end
806
+ reset_data
807
+ end
808
+
642
809
  private
643
810
 
644
811
  def reset_data
645
- @effective_switches = nil
812
+ @effective_flags = nil
646
813
  @optparser_info = nil
647
- @single_switch_syntax = nil
648
- @double_switch_syntax = nil
814
+ @single_flag_syntax = nil
815
+ @double_flag_syntax = nil
649
816
  @value_label = nil
650
817
  @value_delim = nil
651
818
  end
652
819
 
653
820
  def find_canonical_value_label
654
821
  return if @value_delim
655
- double_switch_syntax.reverse_each do |ss|
822
+ @value_label = @accept ? "VALUE" : nil
823
+ @value_delim = @accept ? " " : ""
824
+ single_flag_syntax.each do |ss|
656
825
  next unless ss.value_label
657
826
  @value_label = ss.value_label
658
827
  @value_delim = ss.value_delim
659
- break
660
828
  end
661
- return if @value_delim
662
- single_switch_syntax.reverse_each do |ss|
829
+ double_flag_syntax.each do |ss|
663
830
  next unless ss.value_label
664
831
  @value_label = ss.value_label
665
832
  @value_delim = ss.value_delim
666
- break
667
833
  end
668
- return if @value_delim
669
- @value_label = nil
670
- @value_delim = ""
671
834
  end
672
835
  end
673
836
 
@@ -680,14 +843,16 @@ module Toys
680
843
  # @private
681
844
  #
682
845
  # @param [Symbol] key This argument will set the given context key.
683
- # @param [Object] accept An OptionParser acceptor, or `nil` for none.
684
- # @param [String,Toys::Utils::WrappableString,
685
- # Array<String,Toys::Utils::WrappableString>] docs Documentation
846
+ # @param [:required,:optional,:remaining] type Type of this argument
686
847
  #
687
- def initialize(key, accept, docs)
848
+ def initialize(tool, key, type)
849
+ @tool = tool
688
850
  @key = key
689
- @accept = accept
690
- @docs = Tool.canonicalize_desc(docs)
851
+ @type = type
852
+ @accept = nil
853
+ @desc = ""
854
+ @long_desc = []
855
+ @display_name = key.to_s.tr("-", "_").gsub(/\W/, "").upcase
691
856
  end
692
857
 
693
858
  ##
@@ -696,35 +861,85 @@ module Toys
696
861
  #
697
862
  attr_reader :key
698
863
 
864
+ ##
865
+ # Type of this argument.
866
+ # @return [:required,:optional,:remaining]
867
+ #
868
+ attr_reader :type
869
+
699
870
  ##
700
871
  # Returns the acceptor, which may be `nil`.
701
872
  # @return [Object]
702
873
  #
703
- attr_reader :accept
874
+ attr_accessor :accept
704
875
 
705
876
  ##
706
- # Returns the documentation strings, which may be the empty array.
707
- # @return [Array<String,Toys::Utils::WrappableString>]
877
+ # Returns the short description string.
878
+ # @return [Toys::Utils::WrappableString]
708
879
  #
709
- attr_reader :docs
880
+ attr_reader :desc
710
881
 
711
882
  ##
712
- # Return a canonical name for this arg. Used in usage documentation.
883
+ # Returns the long description strings as an array.
884
+ # @return [Array<Toys::Utils::WrappableString>]
713
885
  #
886
+ attr_reader :long_desc
887
+
888
+ ##
889
+ # Returns the displayable name.
714
890
  # @return [String]
715
891
  #
716
- def canonical_name
717
- Tool.canonical_switch(key)
892
+ attr_accessor :display_name
893
+
894
+ ##
895
+ # Set the default.
896
+ # @param [Object] default The default value.
897
+ #
898
+ def default=(default)
899
+ @tool.default_data[@key] = default
718
900
  end
719
901
 
720
902
  ##
721
- # Returns the documentation strings with wrapping resolved.
903
+ # Set the short description string.
722
904
  #
723
- # @param [Integer,nil] width Wrapping width, or `nil` to use default.
724
- # @return [Array<String>]
905
+ # The description may be provided as a {Toys::Utils::WrappableString}, a
906
+ # single string (which will be wrapped), or an array of strings, which
907
+ # will be interpreted as string fragments that will be concatenated and
908
+ # wrapped.
725
909
  #
726
- def wrapped_docs(width)
727
- Tool.resolve_wrapping(docs, width)
910
+ # @param [Toys::Utils::WrappableString,String,Array<String>] desc
911
+ #
912
+ def desc=(desc)
913
+ @desc = Tool.canonicalize_desc(desc)
914
+ end
915
+
916
+ ##
917
+ # Set the long description strings.
918
+ #
919
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a
920
+ # single string (which will be wrapped), or an array of strings, which
921
+ # will be interpreted as string fragments that will be concatenated and
922
+ # wrapped.
923
+ #
924
+ # @param [Array<Toys::Utils::WrappableString,String,Array<String>>] descs
925
+ #
926
+ def long_desc=(descs)
927
+ @long_desc = Tool.canonicalize_long_desc(descs)
928
+ end
929
+
930
+ ##
931
+ # Set the long description strings.
932
+ #
933
+ # Each string may be provided as a {Toys::Utils::WrappableString}, a
934
+ # single string (which will be wrapped), or an array of strings, which
935
+ # will be interpreted as string fragments that will be concatenated and
936
+ # wrapped.
937
+ #
938
+ # @param [Toys::Utils::WrappableString,String,Array<String>...] descs
939
+ #
940
+ def populate_long_desc(*descs)
941
+ @long_desc = Tool.canonicalize_long_desc(descs)
942
+ self
728
943
  end
729
944
 
730
945
  ##
@@ -736,19 +951,18 @@ module Toys
736
951
  #
737
952
  def process_value(input)
738
953
  return input unless accept
739
- n = canonical_name
740
954
  result = input
741
955
  optparse = ::OptionParser.new
742
- optparse.on("--#{n}=VALUE", accept) { |v| result = v }
743
- optparse.parse(["--#{n}", input])
956
+ optparse.on("--abc=VALUE", accept) { |v| result = v }
957
+ optparse.parse(["--abc", input])
744
958
  result
745
959
  end
746
960
  end
747
961
 
748
962
  private
749
963
 
750
- def make_config_proc(middleware, next_config)
751
- proc { middleware.config(self, &next_config) }
964
+ def make_config_proc(middleware, loader, next_config)
965
+ proc { middleware.config(self, loader, &next_config) }
752
966
  end
753
967
 
754
968
  def check_definition_state
@@ -759,23 +973,14 @@ module Toys
759
973
  end
760
974
 
761
975
  class << self
762
- ## @private
763
- def canonical_switch(name)
764
- name.to_s.downcase.tr("_", "-").gsub(/[^a-z0-9-]/, "")
765
- end
766
-
767
976
  ## @private
768
977
  def canonicalize_desc(desc)
769
- Array(desc).map do |d|
770
- d.is_a?(Utils::WrappableString) ? d : d.split("\n")
771
- end.flatten.freeze
978
+ desc.is_a?(Utils::WrappableString) ? desc : Utils::WrappableString.new(desc)
772
979
  end
773
980
 
774
981
  ## @private
775
- def resolve_wrapping(strs, wrap_width)
776
- strs.map do |s|
777
- s.is_a?(Utils::WrappableString) && !wrap_width.nil? ? s.wrap(wrap_width) : s.to_s
778
- end.flatten
982
+ def canonicalize_long_desc(descs)
983
+ Array(descs).map { |desc| canonicalize_desc(desc) }.freeze
779
984
  end
780
985
  end
781
986
 
@@ -821,14 +1026,14 @@ module Toys
821
1026
 
822
1027
  def create_option_parser
823
1028
  optparse = ::OptionParser.new
824
- # The following clears out the Officious (hidden default switches).
1029
+ # The following clears out the Officious (hidden default flags).
825
1030
  optparse.remove
826
1031
  optparse.remove
827
1032
  optparse.new
828
1033
  optparse.new
829
- @tool.switch_definitions.each do |switch|
830
- optparse.on(*switch.optparser_info) do |val|
831
- @data[switch.key] = switch.handler.call(val, @data[switch.key])
1034
+ @tool.flag_definitions.each do |flag|
1035
+ optparse.on(*flag.optparser_info) do |val|
1036
+ @data[flag.key] = flag.handler.call(val, @data[flag.key])
832
1037
  end
833
1038
  end
834
1039
  optparse
@@ -837,7 +1042,7 @@ module Toys
837
1042
  def parse_required_args(remaining, args)
838
1043
  @tool.required_arg_definitions.each do |arg_info|
839
1044
  if remaining.empty?
840
- reason = "No value given for required argument named <#{arg_info.canonical_name}>"
1045
+ reason = "No value given for required argument #{arg_info.display_name}"
841
1046
  raise create_parse_error(args, reason)
842
1047
  end
843
1048
  @data[arg_info.key] = arg_info.process_value(remaining.shift)
@@ -887,7 +1092,7 @@ module Toys
887
1092
  if @tool.includes_executor?
888
1093
  context.instance_eval(&@tool.executor)
889
1094
  else
890
- context.logger.fatal("No implementation for #{@tool.display_name.inspect}")
1095
+ context.logger.fatal("No implementation for tool #{@tool.display_name.inspect}")
891
1096
  context.exit(-1)
892
1097
  end
893
1098
  end