toys-core 0.11.5 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +5 -2
  5. data/docs/guide.md +1 -1
  6. data/lib/toys/acceptor.rb +13 -4
  7. data/lib/toys/arg_parser.rb +7 -7
  8. data/lib/toys/cli.rb +170 -120
  9. data/lib/toys/compat.rb +71 -23
  10. data/lib/toys/completion.rb +18 -6
  11. data/lib/toys/context.rb +24 -15
  12. data/lib/toys/core.rb +6 -2
  13. data/lib/toys/dsl/base.rb +87 -0
  14. data/lib/toys/dsl/flag.rb +26 -20
  15. data/lib/toys/dsl/flag_group.rb +18 -14
  16. data/lib/toys/dsl/internal.rb +206 -0
  17. data/lib/toys/dsl/positional_arg.rb +26 -16
  18. data/lib/toys/dsl/tool.rb +180 -218
  19. data/lib/toys/errors.rb +64 -8
  20. data/lib/toys/flag.rb +662 -656
  21. data/lib/toys/flag_group.rb +24 -10
  22. data/lib/toys/input_file.rb +13 -7
  23. data/lib/toys/loader.rb +293 -140
  24. data/lib/toys/middleware.rb +46 -22
  25. data/lib/toys/mixin.rb +10 -8
  26. data/lib/toys/positional_arg.rb +21 -20
  27. data/lib/toys/settings.rb +914 -0
  28. data/lib/toys/source_info.rb +147 -35
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +2 -0
  30. data/lib/toys/standard_middleware/apply_config.rb +6 -4
  31. data/lib/toys/standard_middleware/handle_usage_errors.rb +1 -0
  32. data/lib/toys/standard_middleware/set_default_descriptions.rb +19 -18
  33. data/lib/toys/standard_middleware/show_help.rb +19 -5
  34. data/lib/toys/standard_middleware/show_root_version.rb +2 -0
  35. data/lib/toys/standard_mixins/bundler.rb +24 -15
  36. data/lib/toys/standard_mixins/exec.rb +43 -34
  37. data/lib/toys/standard_mixins/fileutils.rb +3 -1
  38. data/lib/toys/standard_mixins/gems.rb +21 -17
  39. data/lib/toys/standard_mixins/git_cache.rb +46 -0
  40. data/lib/toys/standard_mixins/highline.rb +8 -8
  41. data/lib/toys/standard_mixins/terminal.rb +5 -5
  42. data/lib/toys/standard_mixins/xdg.rb +56 -0
  43. data/lib/toys/template.rb +11 -9
  44. data/lib/toys/{tool.rb → tool_definition.rb} +292 -226
  45. data/lib/toys/utils/completion_engine.rb +7 -2
  46. data/lib/toys/utils/exec.rb +162 -132
  47. data/lib/toys/utils/gems.rb +85 -60
  48. data/lib/toys/utils/git_cache.rb +813 -0
  49. data/lib/toys/utils/help_text.rb +117 -37
  50. data/lib/toys/utils/terminal.rb +11 -3
  51. data/lib/toys/utils/xdg.rb +293 -0
  52. data/lib/toys/wrappable_string.rb +9 -2
  53. data/lib/toys-core.rb +18 -6
  54. metadata +14 -7
data/lib/toys/dsl/tool.rb CHANGED
@@ -10,7 +10,7 @@ module Toys
10
10
  # how to execute the tool, and requesting mixin modules and other services.
11
11
  # It also lets you define subtools, nested arbitrarily deep, using blocks.
12
12
  #
13
- # ## Simple example
13
+ # ### Simple example
14
14
  #
15
15
  # Create a file called `.toys.rb` in the current directory, with the
16
16
  # following contents:
@@ -34,11 +34,6 @@ module Toys
34
34
  # toys greet rubyists
35
35
  #
36
36
  module Tool
37
- ## @private
38
- def method_added(_meth)
39
- DSL::Tool.current_tool(self, true)&.check_definition_state(is_method: true)
40
- end
41
-
42
37
  ##
43
38
  # Create a named acceptor that can be referenced by name from any flag or
44
39
  # positional argument in this tool or its subtools.
@@ -83,7 +78,7 @@ module Toys
83
78
  # an exception (descended from `StandardError`) to indicate that the
84
79
  # string parameter is invalid.
85
80
  #
86
- # ## Example
81
+ # ### Example
87
82
  #
88
83
  # The following example creates an acceptor named "hex" that is defined
89
84
  # via a regular expression. It then uses it to validate values passed to
@@ -105,7 +100,7 @@ module Toys
105
100
  # @return [self]
106
101
  #
107
102
  def acceptor(name, spec = nil, type_desc: nil, &block)
108
- cur_tool = DSL::Tool.current_tool(self, false)
103
+ cur_tool = DSL::Internal.current_tool(self, false)
109
104
  cur_tool&.add_acceptor(name, spec, type_desc: type_desc || name.to_s, &block)
110
105
  self
111
106
  end
@@ -122,7 +117,7 @@ module Toys
122
117
  # block. Alternatively, you can create a module separately and pass it
123
118
  # directly to this directive.
124
119
  #
125
- # ## Example
120
+ # ### Example
126
121
  #
127
122
  # The following example creates a named mixin and uses it in a tool.
128
123
  #
@@ -149,7 +144,7 @@ module Toys
149
144
  # @return [self]
150
145
  #
151
146
  def mixin(name, mixin_module = nil, &block)
152
- cur_tool = DSL::Tool.current_tool(self, false)
147
+ cur_tool = DSL::Internal.current_tool(self, false)
153
148
  cur_tool&.add_mixin(name, mixin_module, &block)
154
149
  self
155
150
  end
@@ -175,7 +170,7 @@ module Toys
175
170
  # directly. See {Toys::Template} for details on creating a template
176
171
  # class.
177
172
  #
178
- # ## Example
173
+ # ### Example
179
174
  #
180
175
  # The following example creates and uses a simple template.
181
176
  #
@@ -204,7 +199,7 @@ module Toys
204
199
  # @return [self]
205
200
  #
206
201
  def template(name, template_class = nil, &block)
207
- cur_tool = DSL::Tool.current_tool(self, false)
202
+ cur_tool = DSL::Internal.current_tool(self, false)
208
203
  return self if cur_tool.nil?
209
204
  cur_tool.add_template(name, template_class, &block)
210
205
  self
@@ -228,7 +223,7 @@ module Toys
228
223
  # * The symbol `:file_system` which indicates that paths in the file
229
224
  # system should serve as completion candidates.
230
225
  #
231
- # ## Example
226
+ # ### Example
232
227
  #
233
228
  # The following example defines a completion that uses only the immediate
234
229
  # files in the current directory as candidates. (This is different from
@@ -252,7 +247,7 @@ module Toys
252
247
  # @return [self]
253
248
  #
254
249
  def completion(name, spec = nil, **options, &block)
255
- cur_tool = DSL::Tool.current_tool(self, false)
250
+ cur_tool = DSL::Internal.current_tool(self, false)
256
251
  return self if cur_tool.nil?
257
252
  cur_tool.add_completion(name, spec, **options, &block)
258
253
  self
@@ -261,7 +256,7 @@ module Toys
261
256
  ##
262
257
  # Create a subtool. You must provide a block defining the subtool.
263
258
  #
264
- # ## Example
259
+ # ### Example
265
260
  #
266
261
  # The following example defines a tool and two subtools within it.
267
262
  #
@@ -280,7 +275,7 @@ module Toys
280
275
  #
281
276
  # The following example defines a tool that runs one of its subtools.
282
277
  #
283
- # tool "test", runs: ["test", "unit"] do
278
+ # tool "test", delegate_to: ["test", "unit"] do
284
279
  # tool "unit" do
285
280
  # def run
286
281
  # puts "Running unit tests"
@@ -316,7 +311,7 @@ module Toys
316
311
  when :ignore
317
312
  return self
318
313
  when :reset
319
- subtool.reset_definition(@__loader)
314
+ subtool.reset_definition
320
315
  end
321
316
  end
322
317
  if delegate_to
@@ -328,7 +323,6 @@ module Toys
328
323
  end
329
324
  self
330
325
  end
331
- alias name tool
332
326
 
333
327
  ##
334
328
  # Create an alias, representing an "alternate name" for a tool.
@@ -337,7 +331,7 @@ module Toys
337
331
  # `delegate_to` option, except that `alias_tool` takes a _relative_ name
338
332
  # for the delegate.
339
333
  #
340
- # ## Example
334
+ # ### Example
341
335
  #
342
336
  # This example defines a tool and an alias pointing to it. Both the tool
343
337
  # name `test` and the alias `t` will then refer to the same tool.
@@ -364,7 +358,7 @@ module Toys
364
358
  # Causes the current tool to delegate to another tool. When run, it
365
359
  # simply invokes the target tool with the same arguments.
366
360
  #
367
- # ## Example
361
+ # ### Example
368
362
  #
369
363
  # This example defines a tool that runs one of its subtools. Running the
370
364
  # `test` tool will have the same effect (and recognize the same args) as
@@ -386,7 +380,7 @@ module Toys
386
380
  # @return [self]
387
381
  #
388
382
  def delegate_to(target)
389
- cur_tool = DSL::Tool.current_tool(self, true)
383
+ cur_tool = DSL::Internal.current_tool(self, true)
390
384
  return self if cur_tool.nil?
391
385
  cur_tool.delegate_to(@__loader.split_path(target))
392
386
  self
@@ -397,20 +391,64 @@ module Toys
397
391
  # at the current location.
398
392
  #
399
393
  # @param path [String] The file or directory to load.
394
+ # @param as [String] Load into the given tool/namespace. If omitted,
395
+ # configuration will be loaded into the current namespace.
396
+ #
400
397
  # @return [self]
401
398
  #
402
- def load(path)
399
+ def load(path, as: nil)
400
+ if as
401
+ tool(as) do
402
+ load(path)
403
+ end
404
+ return self
405
+ end
403
406
  @__loader.load_path(source_info, path, @__words, @__remaining_words, @__priority)
404
407
  self
405
408
  end
406
409
 
410
+ ##
411
+ # Load configuration from a public git repository, as if its contents
412
+ # were inserted at the current location.
413
+ #
414
+ # @param remote [String] The URL of the git repository. Defaults to the
415
+ # current repository if already loading from git.
416
+ # @param path [String] The path within the repo to the file or directory
417
+ # to load. Defaults to the root of the repo.
418
+ # @param commit [String] The commit branch, tag, or sha. Defaults to the
419
+ # current commit if already loading from git, or to `HEAD`.
420
+ # @param as [String] Load into the given tool/namespace. If omitted,
421
+ # configuration will be loaded into the current namespace.
422
+ # @param update [Boolean] Force-fetch from the remote (unless the commit
423
+ # is a SHA). This will ensure that symbolic commits, such as branch
424
+ # names, are up to date. Default is false.
425
+ #
426
+ # @return [self]
427
+ #
428
+ def load_git(remote: nil, path: nil, commit: nil, as: nil, update: false)
429
+ if as
430
+ tool(as) do
431
+ load_git(remote: remote, path: path, commit: commit)
432
+ end
433
+ return self
434
+ end
435
+ remote ||= source_info.git_remote
436
+ raise ToolDefinitionError, "Git remote not specified" unless remote
437
+ path ||= ""
438
+ commit ||= source_info.git_commit || "HEAD"
439
+ @__loader.load_git(source_info, remote, path, commit,
440
+ @__words, @__remaining_words, @__priority,
441
+ update: update)
442
+ self
443
+ end
444
+
407
445
  ##
408
446
  # Expand the given template in the current location.
409
447
  #
410
448
  # The template may be specified as a class or a well-known template name.
411
449
  # You may also provide arguments to pass to the template.
412
450
  #
413
- # ## Example
451
+ # ### Example
414
452
  #
415
453
  # The following example creates and uses a simple template.
416
454
  #
@@ -437,12 +475,13 @@ module Toys
437
475
  # @return [self]
438
476
  #
439
477
  def expand(template_class, *args, **kwargs)
440
- cur_tool = DSL::Tool.current_tool(self, false)
478
+ cur_tool = DSL::Internal.current_tool(self, false)
441
479
  return self if cur_tool.nil?
442
480
  name = template_class.to_s
443
- if template_class.is_a?(::String)
481
+ case template_class
482
+ when ::String
444
483
  template_class = cur_tool.lookup_template(template_class)
445
- elsif template_class.is_a?(::Symbol)
484
+ when ::Symbol
446
485
  template_class = @__loader.resolve_standard_template(name)
447
486
  end
448
487
  if template_class.nil?
@@ -472,7 +511,7 @@ module Toys
472
511
  # across the strings in the array. In this case, whitespace is not
473
512
  # compacted.
474
513
  #
475
- # ## Examples
514
+ # ### Examples
476
515
  #
477
516
  # If you pass in a sentence as a simple string, it may be word wrapped
478
517
  # when displayed:
@@ -488,7 +527,7 @@ module Toys
488
527
  # @return [self]
489
528
  #
490
529
  def desc(str)
491
- cur_tool = DSL::Tool.current_tool(self, true)
530
+ cur_tool = DSL::Internal.current_tool(self, true)
492
531
  return self if cur_tool.nil?
493
532
  cur_tool.desc = str
494
533
  self
@@ -506,7 +545,7 @@ module Toys
506
545
  # word-wrapped when displayed. To insert a blank line, include an empty
507
546
  # string as one of the descriptions.
508
547
  #
509
- # ## Example
548
+ # ### Example
510
549
  #
511
550
  # long_desc "This initial paragraph might get word wrapped.",
512
551
  # "This next paragraph is followed by a blank line.",
@@ -524,7 +563,7 @@ module Toys
524
563
  # @return [self]
525
564
  #
526
565
  def long_desc(*strs, file: nil, data: nil)
527
- cur_tool = DSL::Tool.current_tool(self, true)
566
+ cur_tool = DSL::Internal.current_tool(self, true)
528
567
  return self if cur_tool.nil?
529
568
  if file
530
569
  unless source_info.source_path
@@ -535,7 +574,7 @@ module Toys
535
574
  elsif data
536
575
  file = source_info.find_data(data, type: :file)
537
576
  end
538
- strs += DSL::Tool.load_long_desc_file(file) if file
577
+ strs += DSL::Internal.load_long_desc_file(file) if file
539
578
  cur_tool.append_long_desc(strs)
540
579
  self
541
580
  end
@@ -545,7 +584,7 @@ module Toys
545
584
  # belong to the group. The flags in the group are listed together in
546
585
  # help screens.
547
586
  #
548
- # ## Example
587
+ # ### Example
549
588
  #
550
589
  # The following example creates a flag group in which all flags are
551
590
  # optional.
@@ -562,11 +601,11 @@ module Toys
562
601
  # `:optional`, `:exactly_one`, `:at_most_one`, `:at_least_one`.
563
602
  # Default is `:optional`.
564
603
  # @param desc [String,Array<String>,Toys::WrappableString] Short
565
- # description for the group. See {Toys::Tool#desc=} for a description
566
- # of allowed formats. Defaults to `"Flags"`.
604
+ # description for the group. See {Toys::DSL::Tool#desc} for a
605
+ # description of allowed formats. Defaults to `"Flags"`.
567
606
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
568
607
  # Long description for the flag group. See
569
- # {Toys::Tool#long_desc=} for a description of allowed formats.
608
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
570
609
  # Defaults to the empty array.
571
610
  # @param name [String,Symbol,nil] The name of the group, or nil for no
572
611
  # name.
@@ -581,7 +620,7 @@ module Toys
581
620
  #
582
621
  def flag_group(type: :optional, desc: nil, long_desc: nil, name: nil,
583
622
  report_collisions: true, prepend: false, &block)
584
- cur_tool = DSL::Tool.current_tool(self, true)
623
+ cur_tool = DSL::Internal.current_tool(self, true)
585
624
  return self if cur_tool.nil?
586
625
  cur_tool.add_flag_group(type: type, desc: desc, long_desc: long_desc, name: name,
587
626
  report_collisions: report_collisions, prepend: prepend)
@@ -596,7 +635,7 @@ module Toys
596
635
  # defined in the block belong to the group. All flags in this group are
597
636
  # required.
598
637
  #
599
- # ## Example
638
+ # ### Example
600
639
  #
601
640
  # The following example creates a group of required flags.
602
641
  #
@@ -609,11 +648,11 @@ module Toys
609
648
  # end
610
649
  #
611
650
  # @param desc [String,Array<String>,Toys::WrappableString] Short
612
- # description for the group. See {Toys::Tool#desc=} for a description
613
- # of allowed formats. Defaults to `"Flags"`.
651
+ # description for the group. See {Toys::DSL::Tool#desc} for a
652
+ # description of allowed formats. Defaults to `"Flags"`.
614
653
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
615
654
  # Long description for the flag group. See
616
- # {Toys::Tool#long_desc=} for a description of allowed formats.
655
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
617
656
  # Defaults to the empty array.
618
657
  # @param name [String,Symbol,nil] The name of the group, or nil for no
619
658
  # name.
@@ -637,7 +676,7 @@ module Toys
637
676
  # defined in the block belong to the group. At most one flag in this
638
677
  # group must be provided on the command line.
639
678
  #
640
- # ## Example
679
+ # ### Example
641
680
  #
642
681
  # The following example creates a group of flags in which either one or
643
682
  # none may be set, but not more than one.
@@ -652,11 +691,11 @@ module Toys
652
691
  # end
653
692
  #
654
693
  # @param desc [String,Array<String>,Toys::WrappableString] Short
655
- # description for the group. See {Toys::Tool#desc=} for a description
656
- # of allowed formats. Defaults to `"Flags"`.
694
+ # description for the group. See {Toys::DSL::Tool#desc} for a
695
+ # description of allowed formats. Defaults to `"Flags"`.
657
696
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
658
697
  # Long description for the flag group. See
659
- # {Toys::Tool#long_desc=} for a description of allowed formats.
698
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
660
699
  # Defaults to the empty array.
661
700
  # @param name [String,Symbol,nil] The name of the group, or nil for no
662
701
  # name.
@@ -681,7 +720,7 @@ module Toys
681
720
  # defined in the block belong to the group. At least one flag in this
682
721
  # group must be provided on the command line.
683
722
  #
684
- # ## Example
723
+ # ### Example
685
724
  #
686
725
  # The following example creates a group of flags in which one or more
687
726
  # may be set.
@@ -696,11 +735,11 @@ module Toys
696
735
  # end
697
736
  #
698
737
  # @param desc [String,Array<String>,Toys::WrappableString] Short
699
- # description for the group. See {Toys::Tool#desc=} for a description
700
- # of allowed formats. Defaults to `"Flags"`.
738
+ # description for the group. See {Toys::DSL::Tool#desc} for a
739
+ # description of allowed formats. Defaults to `"Flags"`.
701
740
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
702
741
  # Long description for the flag group. See
703
- # {Toys::Tool#long_desc=} for a description of allowed formats.
742
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
704
743
  # Defaults to the empty array.
705
744
  # @param name [String,Symbol,nil] The name of the group, or nil for no
706
745
  # name.
@@ -725,7 +764,7 @@ module Toys
725
764
  # defined in the block belong to the group. Exactly one flag in this
726
765
  # group must be provided on the command line.
727
766
  #
728
- # ## Example
767
+ # ### Example
729
768
  #
730
769
  # The following example creates a group of flags in which exactly one
731
770
  # must be set.
@@ -740,11 +779,11 @@ module Toys
740
779
  # end
741
780
  #
742
781
  # @param desc [String,Array<String>,Toys::WrappableString] Short
743
- # description for the group. See {Toys::Tool#desc=} for a description
744
- # of allowed formats. Defaults to `"Flags"`.
782
+ # description for the group. See {Toys::DSL::Tool#desc} for a
783
+ # description of allowed formats. Defaults to `"Flags"`.
745
784
  # @param long_desc [Array<String,Array<String>,Toys::WrappableString>]
746
785
  # Long description for the flag group. See
747
- # {Toys::Tool#long_desc=} for a description of allowed formats.
786
+ # {Toys::DSL::Tool#long_desc} for a description of allowed formats.
748
787
  # Defaults to the empty array.
749
788
  # @param name [String,Symbol,nil] The name of the group, or nil for no
750
789
  # name.
@@ -778,7 +817,7 @@ module Toys
778
817
  # set in a block passed to this method. If you provide a block, you can
779
818
  # use directives in {Toys::DSL::Flag} within the block.
780
819
  #
781
- # ## Flag syntax
820
+ # ### Flag syntax
782
821
  #
783
822
  # The flags themselves should be provided in OptionParser form. Following
784
823
  # are examples of valid syntax.
@@ -833,7 +872,7 @@ module Toys
833
872
  # or off. This effectively creates two flags, `--abc` which sets the
834
873
  # value to `true`, and `--no-abc` which sets the falue to `false`.
835
874
  #
836
- # ## Default flag syntax
875
+ # ### Default flag syntax
837
876
  #
838
877
  # If no flag syntax strings are provided, a default syntax will be
839
878
  # inferred based on the key and other options.
@@ -858,7 +897,7 @@ module Toys
858
897
  # flag :number, accept: Integer
859
898
  # flag :number, "--number=VAL", accept: Integer
860
899
  #
861
- # ## More examples
900
+ # ### More examples
862
901
  #
863
902
  # A flag that sets its value to the number of times it appears on the
864
903
  # command line:
@@ -937,7 +976,7 @@ module Toys
937
976
  report_collisions: true, group: nil,
938
977
  desc: nil, long_desc: nil, display_name: nil,
939
978
  &block)
940
- cur_tool = DSL::Tool.current_tool(self, true)
979
+ cur_tool = DSL::Internal.current_tool(self, true)
941
980
  return self if cur_tool.nil?
942
981
  flag_dsl = DSL::Flag.new(
943
982
  flags.flatten, accept, default, handler, complete_flags, complete_values,
@@ -945,14 +984,14 @@ module Toys
945
984
  )
946
985
  flag_dsl.instance_exec(flag_dsl, &block) if block
947
986
  flag_dsl._add_to(cur_tool, key)
948
- DSL::Tool.maybe_add_getter(self, key)
987
+ DSL::Internal.maybe_add_getter(self, key)
949
988
  self
950
989
  end
951
990
 
952
991
  ##
953
- # Add a required positional argument to the current tool. You must specify
954
- # a key which the script may use to obtain the argument value from the
955
- # context.
992
+ # Add a required positional argument to the current tool. You must
993
+ # specify a key which the script may use to obtain the argument value
994
+ # from the context.
956
995
  #
957
996
  # If the given key is a symbol representing a valid method name, then a
958
997
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -963,7 +1002,7 @@ module Toys
963
1002
  # set in a block passed to this method. If you provide a block, you can
964
1003
  # use directives in {Toys::DSL::PositionalArg} within the block.
965
1004
  #
966
- # ## Example
1005
+ # ### Example
967
1006
  #
968
1007
  # This tool "moves" something from a source to destination, and takes two
969
1008
  # required arguments:
@@ -987,8 +1026,8 @@ module Toys
987
1026
  # values of this arg. This is the empty completion by default. To
988
1027
  # customize completion, set this to the name of a previously defined
989
1028
  # completion, or any spec recognized by {Toys::Completion.create}.
990
- # @param display_name [String] A name to use for display (in help text and
991
- # error reports). Defaults to the key in upper case.
1029
+ # @param display_name [String] A name to use for display (in help text
1030
+ # and error reports). Defaults to the key in upper case.
992
1031
  # @param desc [String,Array<String>,Toys::WrappableString] Short
993
1032
  # description for the flag. See {Toys::DSL::Tool#desc} for a
994
1033
  # description of the allowed formats. Defaults to the empty string.
@@ -1006,21 +1045,21 @@ module Toys
1006
1045
  accept: nil, complete: nil, display_name: nil,
1007
1046
  desc: nil, long_desc: nil,
1008
1047
  &block)
1009
- cur_tool = DSL::Tool.current_tool(self, true)
1048
+ cur_tool = DSL::Internal.current_tool(self, true)
1010
1049
  return self if cur_tool.nil?
1011
1050
  arg_dsl = DSL::PositionalArg.new(accept, nil, complete, display_name, desc, long_desc)
1012
1051
  arg_dsl.instance_exec(arg_dsl, &block) if block
1013
1052
  arg_dsl._add_required_to(cur_tool, key)
1014
- DSL::Tool.maybe_add_getter(self, key)
1053
+ DSL::Internal.maybe_add_getter(self, key)
1015
1054
  self
1016
1055
  end
1017
1056
  alias required required_arg
1018
1057
 
1019
1058
  ##
1020
- # Add an optional positional argument to the current tool. You must specify
1021
- # a key which the script may use to obtain the argument value from the
1022
- # context. If an optional argument is not given on the command line, the
1023
- # value is set to the given default.
1059
+ # Add an optional positional argument to the current tool. You must
1060
+ # specify a key which the script may use to obtain the argument value
1061
+ # from the context. If an optional argument is not given on the command
1062
+ # line, the value is set to the given default.
1024
1063
  #
1025
1064
  # If the given key is a symbol representing a valid method name, then a
1026
1065
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -1031,7 +1070,7 @@ module Toys
1031
1070
  # set in a block passed to this method. If you provide a block, you can
1032
1071
  # use directives in {Toys::DSL::PositionalArg} within the block.
1033
1072
  #
1034
- # ## Example
1073
+ # ### Example
1035
1074
  #
1036
1075
  # This tool creates a "link" to a given target. The link location is
1037
1076
  # optional; if it is not given, it is inferred from the target.
@@ -1048,8 +1087,8 @@ module Toys
1048
1087
  # @param key [String,Symbol] The key to use to retrieve the value from
1049
1088
  # the execution context.
1050
1089
  # @param default [Object] The default value. This is the value that will
1051
- # be set in the context if this argument is not provided on the command
1052
- # line. Defaults to `nil`.
1090
+ # be set in the context if this argument is not provided on the
1091
+ # command line. Defaults to `nil`.
1053
1092
  # @param accept [Object] An acceptor that validates and/or converts the
1054
1093
  # value. You may provide either the name of an acceptor you have
1055
1094
  # defined, one of the default acceptors provided by OptionParser, or
@@ -1059,8 +1098,8 @@ module Toys
1059
1098
  # values of this arg. This is the empty completion by default. To
1060
1099
  # customize completion, set this to the name of a previously defined
1061
1100
  # completion, or any spec recognized by {Toys::Completion.create}.
1062
- # @param display_name [String] A name to use for display (in help text and
1063
- # error reports). Defaults to the key in upper case.
1101
+ # @param display_name [String] A name to use for display (in help text
1102
+ # and error reports). Defaults to the key in upper case.
1064
1103
  # @param desc [String,Array<String>,Toys::WrappableString] Short
1065
1104
  # description for the flag. See {Toys::DSL::Tool#desc} for a
1066
1105
  # description of the allowed formats. Defaults to the empty string.
@@ -1078,20 +1117,20 @@ module Toys
1078
1117
  default: nil, accept: nil, complete: nil, display_name: nil,
1079
1118
  desc: nil, long_desc: nil,
1080
1119
  &block)
1081
- cur_tool = DSL::Tool.current_tool(self, true)
1120
+ cur_tool = DSL::Internal.current_tool(self, true)
1082
1121
  return self if cur_tool.nil?
1083
1122
  arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1084
1123
  arg_dsl.instance_exec(arg_dsl, &block) if block
1085
1124
  arg_dsl._add_optional_to(cur_tool, key)
1086
- DSL::Tool.maybe_add_getter(self, key)
1125
+ DSL::Internal.maybe_add_getter(self, key)
1087
1126
  self
1088
1127
  end
1089
1128
  alias optional optional_arg
1090
1129
 
1091
1130
  ##
1092
- # Specify what should be done with unmatched positional arguments. You must
1093
- # specify a key which the script may use to obtain the remaining args from
1094
- # the context.
1131
+ # Specify what should be done with unmatched positional arguments. You
1132
+ # must specify a key which the script may use to obtain the remaining
1133
+ # args from the context.
1095
1134
  #
1096
1135
  # If the given key is a symbol representing a valid method name, then a
1097
1136
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -1102,7 +1141,7 @@ module Toys
1102
1141
  # set in a block passed to this method. If you provide a block, you can
1103
1142
  # use directives in {Toys::DSL::PositionalArg} within the block.
1104
1143
  #
1105
- # ## Example
1144
+ # ### Example
1106
1145
  #
1107
1146
  # This tool displays a "list" of the given directories. If no directories
1108
1147
  # ar given, lists the current directory.
@@ -1131,8 +1170,8 @@ module Toys
1131
1170
  # values of this arg. This is the empty completion by default. To
1132
1171
  # customize completion, set this to the name of a previously defined
1133
1172
  # completion, or any spec recognized by {Toys::Completion.create}.
1134
- # @param display_name [String] A name to use for display (in help text and
1135
- # error reports). Defaults to the key in upper case.
1173
+ # @param display_name [String] A name to use for display (in help text
1174
+ # and error reports). Defaults to the key in upper case.
1136
1175
  # @param desc [String,Array<String>,Toys::WrappableString] Short
1137
1176
  # description for the flag. See {Toys::DSL::Tool#desc} for a
1138
1177
  # description of the allowed formats. Defaults to the empty string.
@@ -1150,12 +1189,12 @@ module Toys
1150
1189
  default: [], accept: nil, complete: nil, display_name: nil,
1151
1190
  desc: nil, long_desc: nil,
1152
1191
  &block)
1153
- cur_tool = DSL::Tool.current_tool(self, true)
1192
+ cur_tool = DSL::Internal.current_tool(self, true)
1154
1193
  return self if cur_tool.nil?
1155
1194
  arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1156
1195
  arg_dsl.instance_exec(arg_dsl, &block) if block
1157
1196
  arg_dsl._set_remaining_on(cur_tool, key)
1158
- DSL::Tool.maybe_add_getter(self, key)
1197
+ DSL::Internal.maybe_add_getter(self, key)
1159
1198
  self
1160
1199
  end
1161
1200
  alias remaining remaining_args
@@ -1168,7 +1207,7 @@ module Toys
1168
1207
  # if the key is a string or does not represent a valid method name, the
1169
1208
  # tool can retrieve the value by calling {Toys::Context#get}.
1170
1209
  #
1171
- # ## Example
1210
+ # ### Example
1172
1211
  #
1173
1212
  # tool "hello" do
1174
1213
  # static :greeting, "Hi there"
@@ -1190,16 +1229,16 @@ module Toys
1190
1229
  # @return [self]
1191
1230
  #
1192
1231
  def static(key, value = nil)
1193
- cur_tool = DSL::Tool.current_tool(self, true)
1232
+ cur_tool = DSL::Internal.current_tool(self, true)
1194
1233
  return self if cur_tool.nil?
1195
1234
  if key.is_a?(::Hash)
1196
1235
  cur_tool.default_data.merge!(key)
1197
1236
  key.each_key do |k|
1198
- DSL::Tool.maybe_add_getter(self, k)
1237
+ DSL::Internal.maybe_add_getter(self, k)
1199
1238
  end
1200
1239
  else
1201
1240
  cur_tool.default_data[key] = value
1202
- DSL::Tool.maybe_add_getter(self, key)
1241
+ DSL::Internal.maybe_add_getter(self, key)
1203
1242
  end
1204
1243
  self
1205
1244
  end
@@ -1207,7 +1246,7 @@ module Toys
1207
1246
  ##
1208
1247
  # Set a option values statically without creating helper methods.
1209
1248
  #
1210
- # ## Example
1249
+ # ### Example
1211
1250
  #
1212
1251
  # tool "hello" do
1213
1252
  # set :greeting, "Hi there"
@@ -1229,7 +1268,7 @@ module Toys
1229
1268
  # @return [self]
1230
1269
  #
1231
1270
  def set(key, value = nil)
1232
- cur_tool = DSL::Tool.current_tool(self, true)
1271
+ cur_tool = DSL::Internal.current_tool(self, true)
1233
1272
  return self if cur_tool.nil?
1234
1273
  if key.is_a?(::Hash)
1235
1274
  cur_tool.default_data.merge!(key)
@@ -1251,7 +1290,7 @@ module Toys
1251
1290
  # @return [self]
1252
1291
  #
1253
1292
  def enforce_flags_before_args(state = true)
1254
- DSL::Tool.current_tool(self, true)&.enforce_flags_before_args(state)
1293
+ DSL::Internal.current_tool(self, true)&.enforce_flags_before_args(state)
1255
1294
  self
1256
1295
  end
1257
1296
 
@@ -1267,7 +1306,7 @@ module Toys
1267
1306
  # @return [self]
1268
1307
  #
1269
1308
  def require_exact_flag_match(state = true)
1270
- DSL::Tool.current_tool(self, true)&.require_exact_flag_match(state)
1309
+ DSL::Internal.current_tool(self, true)&.require_exact_flag_match(state)
1271
1310
  self
1272
1311
  end
1273
1312
 
@@ -1282,7 +1321,7 @@ module Toys
1282
1321
  # @return [self]
1283
1322
  #
1284
1323
  def disable_argument_parsing
1285
- DSL::Tool.current_tool(self, true)&.disable_argument_parsing
1324
+ DSL::Internal.current_tool(self, true)&.disable_argument_parsing
1286
1325
  self
1287
1326
  end
1288
1327
 
@@ -1291,7 +1330,7 @@ module Toys
1291
1330
  # subsequent flag definition. This can be used to prevent middleware from
1292
1331
  # defining a particular flag.
1293
1332
  #
1294
- # ## Example
1333
+ # ### Example
1295
1334
  #
1296
1335
  # This tool does not support the `-v` and `-q` short forms for the two
1297
1336
  # verbosity flags (although it still supports the long forms `--verbose`
@@ -1308,7 +1347,7 @@ module Toys
1308
1347
  # @return [self]
1309
1348
  #
1310
1349
  def disable_flag(*flags)
1311
- DSL::Tool.current_tool(self, true)&.disable_flag(*flags)
1350
+ DSL::Internal.current_tool(self, true)&.disable_flag(*flags)
1312
1351
  self
1313
1352
  end
1314
1353
 
@@ -1319,12 +1358,13 @@ module Toys
1319
1358
  # * The string name of a completion defined in this tool or any of its
1320
1359
  # its ancestors.
1321
1360
  # * A hash of options to pass to the constructor of
1322
- # {Toys::Tool::DefaultCompletion}.
1361
+ # {Toys::ToolDefinition::DefaultCompletion}.
1323
1362
  # * `nil` or `:default` to select the standard completion strategy
1324
- # (which is {Toys::Tool::DefaultCompletion} with no extra options).
1363
+ # (which is {Toys::ToolDefinition::DefaultCompletion} with no extra
1364
+ # options).
1325
1365
  # * Any other specification recognized by {Toys::Completion.create}.
1326
1366
  #
1327
- # ## Example
1367
+ # ### Example
1328
1368
  #
1329
1369
  # The namespace "foo" supports completion only of subtool names. It does
1330
1370
  # not complete the standard flags (like --help).
@@ -1345,7 +1385,7 @@ module Toys
1345
1385
  # @return [self]
1346
1386
  #
1347
1387
  def complete_tool_args(spec = nil, **options, &block)
1348
- cur_tool = DSL::Tool.current_tool(self, true)
1388
+ cur_tool = DSL::Internal.current_tool(self, true)
1349
1389
  return self if cur_tool.nil?
1350
1390
  cur_tool.completion = Completion.scalarize_spec(spec, options, block)
1351
1391
  self
@@ -1360,7 +1400,7 @@ module Toys
1360
1400
  # in the lexical scope. However, it is often more convenient to use
1361
1401
  # {#static} to set the value in the context.)
1362
1402
  #
1363
- # ## Example
1403
+ # ### Example
1364
1404
  #
1365
1405
  # tool "foo" do
1366
1406
  # cur_time = Time.new
@@ -1373,7 +1413,9 @@ module Toys
1373
1413
  # @return [self]
1374
1414
  #
1375
1415
  def to_run(&block)
1376
- define_method(:run, &block)
1416
+ cur_tool = DSL::Internal.current_tool(self, true)
1417
+ return self if cur_tool.nil?
1418
+ cur_tool.run_handler = block
1377
1419
  self
1378
1420
  end
1379
1421
  alias on_run to_run
@@ -1385,7 +1427,7 @@ module Toys
1385
1427
  # either case, the block or method should take one argument, the
1386
1428
  # Interrupt exception that was raised.
1387
1429
  #
1388
- # ## Example
1430
+ # ### Example
1389
1431
  #
1390
1432
  # tool "foo" do
1391
1433
  # def run
@@ -1402,7 +1444,7 @@ module Toys
1402
1444
  # @return [self]
1403
1445
  #
1404
1446
  def on_interrupt(handler = nil, &block)
1405
- cur_tool = DSL::Tool.current_tool(self, true)
1447
+ cur_tool = DSL::Internal.current_tool(self, true)
1406
1448
  return self if cur_tool.nil?
1407
1449
  cur_tool.interrupt_handler = handler || block
1408
1450
  self
@@ -1415,7 +1457,7 @@ module Toys
1415
1457
  # either case, the block or method should take one argument, the array of
1416
1458
  # usage errors reported.
1417
1459
  #
1418
- # ## Example
1460
+ # ### Example
1419
1461
  #
1420
1462
  # This tool runs even if a usage error is encountered. You can find info
1421
1463
  # on the errors from {Toys::Context::Key::USAGE_ERRORS},
@@ -1434,7 +1476,7 @@ module Toys
1434
1476
  # @return [self]
1435
1477
  #
1436
1478
  def on_usage_error(handler = nil, &block)
1437
- cur_tool = DSL::Tool.current_tool(self, true)
1479
+ cur_tool = DSL::Internal.current_tool(self, true)
1438
1480
  return self if cur_tool.nil?
1439
1481
  cur_tool.usage_error_handler = handler || block
1440
1482
  self
@@ -1448,7 +1490,7 @@ module Toys
1448
1490
  # have defined in this tool or one of its ancestors, or the symbol name
1449
1491
  # of a well-known mixin.
1450
1492
  #
1451
- # ## Example
1493
+ # ### Example
1452
1494
  #
1453
1495
  # Include the well-known mixin `:terminal` and perform some terminal
1454
1496
  # magic.
@@ -1463,28 +1505,16 @@ module Toys
1463
1505
  # end
1464
1506
  # end
1465
1507
  #
1466
- # @param mod [Module,Symbol,String] Module or module name.
1508
+ # @param mixin [Module,Symbol,String] Module or module name.
1467
1509
  # @param args [Object...] Arguments to pass to the initializer
1468
1510
  # @param kwargs [keywords] Keyword arguments to pass to the initializer
1469
1511
  # @return [self]
1470
1512
  #
1471
- def include(mod, *args, **kwargs)
1472
- cur_tool = DSL::Tool.current_tool(self, true)
1513
+ def include(mixin, *args, **kwargs)
1514
+ cur_tool = DSL::Internal.current_tool(self, true)
1473
1515
  return self if cur_tool.nil?
1474
- mod = DSL::Tool.resolve_mixin(mod, cur_tool, @__loader)
1475
- if included_modules.include?(mod)
1476
- raise ToolDefinitionError, "Mixin already included: #{mod.name}"
1477
- end
1478
- cur_tool.mark_includes_modules
1479
- super(mod)
1480
- if mod.respond_to?(:initializer)
1481
- callback = mod.initializer
1482
- cur_tool.add_initializer(callback, *args, **kwargs) if callback
1483
- end
1484
- if mod.respond_to?(:inclusion)
1485
- callback = mod.inclusion
1486
- class_exec(*args, **kwargs, &callback) if callback
1487
- end
1516
+ mod = DSL::Internal.resolve_mixin(mixin, cur_tool, @__loader)
1517
+ cur_tool.include_mixin(mod, *args, **kwargs)
1488
1518
  self
1489
1519
  end
1490
1520
 
@@ -1501,9 +1531,9 @@ module Toys
1501
1531
  # @return [nil] if the current tool is not active.
1502
1532
  #
1503
1533
  def include?(mod)
1504
- cur_tool = DSL::Tool.current_tool(self, false)
1534
+ cur_tool = DSL::Internal.current_tool(self, false)
1505
1535
  return if cur_tool.nil?
1506
- super(DSL::Tool.resolve_mixin(mod, cur_tool, @__loader))
1536
+ super(DSL::Internal.resolve_mixin(mod, cur_tool, @__loader))
1507
1537
  end
1508
1538
 
1509
1539
  ##
@@ -1523,7 +1553,7 @@ module Toys
1523
1553
  # in a directory called `.data` inside a Toys directory. This directive
1524
1554
  # locates a data file during tool definition.
1525
1555
  #
1526
- # ## Example
1556
+ # ### Example
1527
1557
  #
1528
1558
  # This tool reads its description from a text file in the `.data`
1529
1559
  # directory.
@@ -1557,17 +1587,17 @@ module Toys
1557
1587
  # @return [nil] if there is no context.
1558
1588
  #
1559
1589
  def context_directory
1560
- DSL::Tool.current_tool(self, false)&.context_directory || source_info.context_directory
1590
+ DSL::Internal.current_tool(self, false)&.context_directory || source_info.context_directory
1561
1591
  end
1562
1592
 
1563
1593
  ##
1564
- # Return the current tool object. This object can be queried to determine
1594
+ # Return the current tool config. This object can be queried to determine
1565
1595
  # such information as the name, but it should not be altered.
1566
1596
  #
1567
- # @return [Toys::Tool]
1597
+ # @return [Toys::ToolDefinition]
1568
1598
  #
1569
1599
  def current_tool
1570
- DSL::Tool.current_tool(self, false)
1600
+ DSL::Internal.current_tool(self, false)
1571
1601
  end
1572
1602
 
1573
1603
  ##
@@ -1577,7 +1607,7 @@ module Toys
1577
1607
  # @return [self]
1578
1608
  #
1579
1609
  def set_context_directory(dir) # rubocop:disable Naming/AccessorMethodName
1580
- cur_tool = DSL::Tool.current_tool(self, false)
1610
+ cur_tool = DSL::Internal.current_tool(self, false)
1581
1611
  return self if cur_tool.nil?
1582
1612
  cur_tool.custom_context_directory = dir
1583
1613
  self
@@ -1591,7 +1621,7 @@ module Toys
1591
1621
  # The block is applied only to subtools defined *after* the block
1592
1622
  # appears. Subtools defined before the block appears are not affected.
1593
1623
  #
1594
- # ## Example
1624
+ # ### Example
1595
1625
  #
1596
1626
  # It is common for tools to use the `:exec` mixin to invoke external
1597
1627
  # programs. This example automatically includes the exec mixin in all
@@ -1616,7 +1646,7 @@ module Toys
1616
1646
  # end
1617
1647
  #
1618
1648
  def subtool_apply(&block)
1619
- cur_tool = DSL::Tool.current_tool(self, false)
1649
+ cur_tool = DSL::Internal.current_tool(self, false)
1620
1650
  return self if cur_tool.nil?
1621
1651
  cur_tool.subtool_middleware_stack.add(:apply_config,
1622
1652
  parent_source: source_info, &block)
@@ -1640,6 +1670,15 @@ module Toys
1640
1670
  end
1641
1671
  end
1642
1672
 
1673
+ ##
1674
+ # Get the settings for this tool.
1675
+ #
1676
+ # @return [Toys::ToolDefinition::Settings] Tool-specific settings.
1677
+ #
1678
+ def settings
1679
+ DSL::Internal.current_tool(self, false)&.settings
1680
+ end
1681
+
1643
1682
  ##
1644
1683
  # Determines whether the current Toys version satisfies the given
1645
1684
  # requirements.
@@ -1673,91 +1712,14 @@ module Toys
1673
1712
  self
1674
1713
  end
1675
1714
 
1676
- ## @private
1677
- def self.new_class(words, priority, loader)
1678
- tool_class = ::Class.new(::Toys::Context)
1679
- tool_class.extend(DSL::Tool)
1680
- tool_class.instance_variable_set(:@__words, words)
1681
- tool_class.instance_variable_set(:@__priority, priority)
1682
- tool_class.instance_variable_set(:@__loader, loader)
1683
- tool_class.instance_variable_set(:@__remaining_words, nil)
1684
- tool_class.instance_variable_set(:@__source, [])
1685
- tool_class
1686
- end
1687
-
1688
- ## @private
1689
- def self.current_tool(tool_class, activate)
1690
- memoize_var = activate ? :@__active_tool : :@__cur_tool
1691
- if tool_class.instance_variable_defined?(memoize_var)
1692
- tool_class.instance_variable_get(memoize_var)
1693
- else
1694
- loader = tool_class.instance_variable_get(:@__loader)
1695
- words = tool_class.instance_variable_get(:@__words)
1696
- priority = tool_class.instance_variable_get(:@__priority)
1697
- cur_tool =
1698
- if activate
1699
- loader.activate_tool(words, priority)
1700
- else
1701
- loader.get_tool(words, priority)
1702
- end
1703
- if cur_tool && activate
1704
- source = tool_class.instance_variable_get(:@__source).last
1705
- cur_tool.lock_source(source)
1706
- end
1707
- tool_class.instance_variable_set(memoize_var, cur_tool)
1708
- end
1709
- end
1710
-
1711
- ## @private
1712
- def self.prepare(tool_class, remaining_words, source)
1713
- tool_class.instance_variable_set(:@__remaining_words, remaining_words)
1714
- tool_class.instance_variable_get(:@__source).push(source)
1715
- yield
1716
- ensure
1717
- tool_class.instance_variable_get(:@__source).pop
1718
- end
1719
-
1720
- ## @private
1721
- def self.maybe_add_getter(tool_class, key)
1722
- if key.is_a?(::Symbol) && key.to_s =~ /^[_a-zA-Z]\w*[!\?]?$/ && key != :run
1723
- unless tool_class.public_method_defined?(key)
1724
- tool_class.class_eval do
1725
- define_method(key) do
1726
- self[key]
1727
- end
1728
- end
1729
- end
1730
- end
1731
- end
1732
-
1733
- ## @private
1734
- def self.resolve_mixin(mod, cur_tool, loader)
1735
- name = mod.to_s
1736
- if mod.is_a?(::String)
1737
- mod = cur_tool.lookup_mixin(mod)
1738
- elsif mod.is_a?(::Symbol)
1739
- mod = loader.resolve_standard_mixin(name)
1740
- end
1741
- unless mod.is_a?(::Module)
1742
- raise ToolDefinitionError, "Module not found: #{name.inspect}"
1743
- end
1744
- mod
1745
- end
1746
-
1747
- ## @private
1748
- def self.load_long_desc_file(path)
1749
- if ::File.extname(path) == ".txt"
1750
- begin
1751
- ::File.readlines(path).map do |line|
1752
- line = line.chomp
1753
- line =~ /^\s/ ? [line] : line
1754
- end
1755
- rescue ::SystemCallError => e
1756
- raise Toys::ToolDefinitionError, e.to_s
1757
- end
1758
- else
1759
- raise Toys::ToolDefinitionError, "Cannot load long desc from file type: #{path}"
1760
- end
1715
+ ##
1716
+ # Notify the tool definition when a method is defined in this tool class.
1717
+ #
1718
+ # @private
1719
+ #
1720
+ def method_added(_meth)
1721
+ super
1722
+ DSL::Internal.current_tool(self, true)&.check_definition_state(is_method: true)
1761
1723
  end
1762
1724
  end
1763
1725
  end