toys-core 0.3.3 → 0.3.4

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.
@@ -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