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
@@ -16,8 +16,6 @@ module Toys
16
16
  # This is a frontend for {Toys::Utils::Exec}. More information is
17
17
  # available in that class's documentation.
18
18
  #
19
- # ## Features
20
- #
21
19
  # ### Controlling processes
22
20
  #
23
21
  # A process can be started in the *foreground* or the *background*. If you
@@ -149,7 +147,7 @@ module Toys
149
147
  #
150
148
  # include :exec, exit_on_nonzero_status: true
151
149
  #
152
- # ## Configuration Options
150
+ # ### Configuration Options
153
151
  #
154
152
  # A variety of options can be used to control subprocesses. These can be
155
153
  # provided to any method that starts a subprocess. You can also set
@@ -231,20 +229,6 @@ module Toys
231
229
  #
232
230
  KEY = ::Object.new.freeze
233
231
 
234
- on_initialize do |**opts|
235
- require "toys/utils/exec"
236
- context = self
237
- opts = Exec._setup_exec_opts(opts, context)
238
- context[KEY] = Utils::Exec.new(**opts) do |k|
239
- case k
240
- when :logger
241
- context[Context::Key::LOGGER]
242
- when :cli
243
- context[Context::Key::CLI]
244
- end
245
- end
246
- end
247
-
248
232
  ##
249
233
  # Set default configuration options.
250
234
  #
@@ -267,7 +251,7 @@ module Toys
267
251
  # If the process is not set to run in the background, and a block is
268
252
  # provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
269
253
  #
270
- # ## Examples
254
+ # ### Examples
271
255
  #
272
256
  # Run a command without a shell, and print the exit code (0 for success):
273
257
  #
@@ -303,11 +287,11 @@ module Toys
303
287
  # If the process is not set to run in the background, and a block is
304
288
  # provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
305
289
  #
306
- # ## Example
290
+ # ### Example
307
291
  #
308
292
  # Execute a small script with warnings
309
293
  #
310
- # exec_ruby("-w", "-e", "(1..10).each { |i| puts i }")
294
+ # exec_ruby(["-w", "-e", "(1..10).each { |i| puts i }"])
311
295
  #
312
296
  # @param args [String,Array<String>] The arguments to ruby.
313
297
  # @param opts [keywords] The command options. See the section on
@@ -337,7 +321,7 @@ module Toys
337
321
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
338
322
  # do not support this method because they do not support fork.
339
323
  #
340
- # ## Example
324
+ # ### Example
341
325
  #
342
326
  # Run a proc in a forked process.
343
327
  #
@@ -377,7 +361,7 @@ module Toys
377
361
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
378
362
  # do not support this method because they do not support fork.
379
363
  #
380
- # ## Example
364
+ # ### Example
381
365
  #
382
366
  # Run the "system update" tool and pass it an argument.
383
367
  #
@@ -399,6 +383,7 @@ module Toys
399
383
  def exec_tool(cmd, **opts, &block)
400
384
  func = Exec._make_tool_caller(cmd)
401
385
  opts = Exec._setup_exec_opts(opts, self)
386
+ opts = {log_cmd: "exec tool: #{cmd.inspect}"}.merge(opts)
402
387
  self[KEY].exec_proc(func, **opts, &block)
403
388
  end
404
389
 
@@ -424,7 +409,7 @@ module Toys
424
409
  # run a tool that uses a different bundle. It may also be necessary on
425
410
  # environments without "fork" (such as JRuby or Ruby on Windows).
426
411
  #
427
- # ## Example
412
+ # ### Example
428
413
  #
429
414
  # Run the "system update" tool and pass it an argument.
430
415
  #
@@ -459,7 +444,7 @@ module Toys
459
444
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
460
445
  # yielded to it.
461
446
  #
462
- # ## Example
447
+ # ### Example
463
448
  #
464
449
  # Capture the output of an echo command
465
450
  #
@@ -490,7 +475,7 @@ module Toys
490
475
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
491
476
  # yielded to it.
492
477
  #
493
- # ## Example
478
+ # ### Example
494
479
  #
495
480
  # Capture the output of a ruby script.
496
481
  #
@@ -524,7 +509,7 @@ module Toys
524
509
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
525
510
  # do not support this method because they do not support fork.
526
511
  #
527
- # ## Example
512
+ # ### Example
528
513
  #
529
514
  # Run a proc in a forked process and capture its output:
530
515
  #
@@ -564,7 +549,7 @@ module Toys
564
549
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
565
550
  # do not support this method because they do not support fork.
566
551
  #
567
- # ## Example
552
+ # ### Example
568
553
  #
569
554
  # Run the "system version" tool and capture its output.
570
555
  #
@@ -612,7 +597,7 @@ module Toys
612
597
  # run a tool that uses a different bundle. It may also be necessary on
613
598
  # environments without "fork" (such as JRuby or Ruby on Windows).
614
599
  #
615
- # ## Example
600
+ # ### Example
616
601
  #
617
602
  # Run the "system version" tool and capture its output.
618
603
  #
@@ -642,7 +627,7 @@ module Toys
642
627
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
643
628
  # yielded to it.
644
629
  #
645
- # ## Example
630
+ # ### Example
646
631
  #
647
632
  # Run a shell script
648
633
  #
@@ -677,13 +662,17 @@ module Toys
677
662
  0
678
663
  end
679
664
 
680
- ## @private
665
+ ##
666
+ # @private
667
+ #
681
668
  def self._make_tool_caller(cmd)
682
669
  cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
683
670
  proc { |config| ::Kernel.exit(config[:cli].run(*cmd)) }
684
671
  end
685
672
 
686
- ## @private
673
+ ##
674
+ # @private
675
+ #
687
676
  def self._setup_exec_opts(opts, context)
688
677
  count = 0
689
678
  result_callback = nil
@@ -706,7 +695,9 @@ module Toys
706
695
  opts
707
696
  end
708
697
 
709
- ## @private
698
+ ##
699
+ # @private
700
+ #
710
701
  def self._interpret_e(value, context)
711
702
  return nil unless value
712
703
  proc do |result|
@@ -720,7 +711,9 @@ module Toys
720
711
  end
721
712
  end
722
713
 
723
- ## @private
714
+ ##
715
+ # @private
716
+ #
724
717
  def self._interpret_result_callback(value, context)
725
718
  if value.is_a?(::Symbol)
726
719
  context.method(value)
@@ -733,7 +726,9 @@ module Toys
733
726
  end
734
727
  end
735
728
 
736
- ## @private
729
+ ##
730
+ # @private
731
+ #
737
732
  def self._setup_clean_process(cmd)
738
733
  raise ::ArgumentError, "Toys process is unknown" unless ::Toys.executable_path
739
734
  cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
@@ -748,6 +743,20 @@ module Toys
748
743
  yield(cmd)
749
744
  end
750
745
  end
746
+
747
+ on_initialize do |**opts|
748
+ require "toys/utils/exec"
749
+ context = self
750
+ opts = Exec._setup_exec_opts(opts, context)
751
+ context[KEY] = Utils::Exec.new(**opts) do |k|
752
+ case k
753
+ when :logger
754
+ context[Context::Key::LOGGER]
755
+ when :cli
756
+ context[Context::Key::CLI]
757
+ end
758
+ end
759
+ end
751
760
  end
752
761
  end
753
762
  end
@@ -16,7 +16,9 @@ module Toys
16
16
  module Fileutils
17
17
  include Mixin
18
18
 
19
- ## @private
19
+ ##
20
+ # @private
21
+ #
20
22
  def self.included(mod)
21
23
  mod.include(::FileUtils)
22
24
  end
@@ -25,23 +25,6 @@ module Toys
25
25
  module Gems
26
26
  include Mixin
27
27
 
28
- on_include do |**opts|
29
- @__gems_opts = opts
30
-
31
- ## @private
32
- def self.gems
33
- require "toys/utils/gems"
34
- # rubocop:disable Naming/MemoizedInstanceVariableName
35
- @__gems ||= Utils::Gems.new(**@__gems_opts)
36
- # rubocop:enable Naming/MemoizedInstanceVariableName
37
- end
38
-
39
- ## @private
40
- def self.gem(name, *requirements)
41
- gems.activate(name, *requirements)
42
- end
43
- end
44
-
45
28
  ##
46
29
  # A tool-wide instance of {Toys::Utils::Gems}.
47
30
  # @return [Toys::Utils::Gems]
@@ -60,6 +43,27 @@ module Toys
60
43
  def gem(name, *requirements)
61
44
  self.class.gems.activate(name, *requirements)
62
45
  end
46
+
47
+ on_include do |**opts|
48
+ @__gems_opts = opts
49
+
50
+ ##
51
+ # @private
52
+ #
53
+ def self.gems
54
+ require "toys/utils/gems"
55
+ # rubocop:disable Naming/MemoizedInstanceVariableName
56
+ @__gems ||= Utils::Gems.new(**@__gems_opts)
57
+ # rubocop:enable Naming/MemoizedInstanceVariableName
58
+ end
59
+
60
+ ##
61
+ # @private
62
+ #
63
+ def self.gem(name, *requirements)
64
+ gems.activate(name, *requirements)
65
+ end
66
+ end
63
67
  end
64
68
  end
65
69
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ module StandardMixins
5
+ ##
6
+ # A mixin that provides a git cache.
7
+ #
8
+ # This mixin provides an instance of {Toys::Utils::GitCache}, providing
9
+ # cached access to files from a remote git repo.
10
+ #
11
+ # Example usage:
12
+ #
13
+ # include :git_cache
14
+ #
15
+ # def run
16
+ # # Pull and cache the HEAD commit from the Toys repo.
17
+ # dir = git_cache.find("https://github.com/dazuma/toys.git")
18
+ # # Display the contents of the readme file.
19
+ # puts File.read(File.join(dir, "README.md"))
20
+ # end
21
+ #
22
+ module GitCache
23
+ include Mixin
24
+
25
+ ##
26
+ # Context key for the GitCache object.
27
+ # @return [Object]
28
+ #
29
+ KEY = ::Object.new.freeze
30
+
31
+ ##
32
+ # Access the builtin GitCache.
33
+ #
34
+ # @return [Toys::Utils::GitCache]
35
+ #
36
+ def git_cache
37
+ self[KEY]
38
+ end
39
+
40
+ on_initialize do
41
+ require "toys/utils/git_cache"
42
+ self[KEY] = Utils::GitCache.new
43
+ end
44
+ end
45
+ end
46
+ end
@@ -36,14 +36,6 @@ module Toys
36
36
  #
37
37
  KEY = ::Object.new.freeze
38
38
 
39
- on_initialize do |*args|
40
- require "toys/utils/gems"
41
- Toys::Utils::Gems.activate("highline", "~> 2.0")
42
- require "highline"
43
- self[KEY] = ::HighLine.new(*args)
44
- self[KEY].use_color = $stdout.tty?
45
- end
46
-
47
39
  ##
48
40
  # A tool-wide [HighLine](https://www.rubydoc.info/gems/highline/HighLine)
49
41
  # instance
@@ -136,6 +128,14 @@ module Toys
136
128
  def new_scope
137
129
  highline.new_scope
138
130
  end
131
+
132
+ on_initialize do |*args|
133
+ require "toys/utils/gems"
134
+ Toys::Utils::Gems.activate("highline", "~> 2.0")
135
+ require "highline"
136
+ self[KEY] = ::HighLine.new(*args)
137
+ self[KEY].use_color = $stdout.tty?
138
+ end
139
139
  end
140
140
  end
141
141
  end
@@ -35,11 +35,6 @@ module Toys
35
35
  #
36
36
  KEY = ::Object.new.freeze
37
37
 
38
- on_initialize do |**opts|
39
- require "toys/utils/terminal"
40
- self[KEY] = Utils::Terminal.new(**opts)
41
- end
42
-
43
38
  ##
44
39
  # A tool-wide terminal instance
45
40
  # @return [Toys::Utils::Terminal]
@@ -139,6 +134,11 @@ module Toys
139
134
  frame_length: frame_length, frames: frames, style: style,
140
135
  &block)
141
136
  end
137
+
138
+ on_initialize do |**opts|
139
+ require "toys/utils/terminal"
140
+ self[KEY] = Utils::Terminal.new(**opts)
141
+ end
142
142
  end
143
143
  end
144
144
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Toys
6
+ module StandardMixins
7
+ ##
8
+ # A mixin that provides tools for working with the XDG Base Directory
9
+ # Specification.
10
+ #
11
+ # This mixin provides an instance of {Toys::Utils::XDG}, which includes
12
+ # utility methods that locate base directories and search paths for
13
+ # application state, configuration, caches, and other data, according to
14
+ # the [XDG Base Directory Spec version
15
+ # 0.8](https://specifications.freedesktop.org/basedir-spec/0.8/).
16
+ #
17
+ # Example usage:
18
+ #
19
+ # include :xdg
20
+ #
21
+ # def run
22
+ # # Get config file paths, in order from most to least inportant
23
+ # config_files = xdg.lookup_config("my-config.toml")
24
+ # config_files.each { |path| read_my_config(path) }
25
+ # end
26
+ #
27
+ module XDG
28
+ include Mixin
29
+
30
+ ##
31
+ # Context key for the XDG object.
32
+ # @return [Object]
33
+ #
34
+ KEY = ::Object.new.freeze
35
+
36
+ ##
37
+ # Access XDG utility methods.
38
+ #
39
+ # @return [Toys::Utils::XDG]
40
+ #
41
+ def xdg
42
+ self[KEY]
43
+ end
44
+
45
+ on_initialize do
46
+ require "toys/utils/xdg"
47
+ self[KEY] = Utils::XDG.new
48
+ end
49
+ end
50
+
51
+ ##
52
+ # An alternate name for the {XDG} module
53
+ #
54
+ Xdg = XDG
55
+ end
56
+ end
data/lib/toys/template.rb CHANGED
@@ -10,7 +10,7 @@ module Toys
10
10
  # Templates will often support configuration; for example the minitest
11
11
  # template lets you configure the paths to the test files.
12
12
  #
13
- # ## Usage
13
+ # ### Usage
14
14
  #
15
15
  # To create a template, define a class and include this module.
16
16
  # The class defines the "configuration" of the template. If your template
@@ -25,7 +25,7 @@ module Toys
25
25
  # this block are "inserted" into the user's configuration. The template
26
26
  # object is passed to the block so you have access to the template options.
27
27
  #
28
- # ## Example
28
+ # ### Example
29
29
  #
30
30
  # This is a simple template that generates a "hello" tool. The tool simply
31
31
  # prints a `"Hello, #{name}!"` greeting. The name is set as a template
@@ -84,13 +84,6 @@ module Toys
84
84
  template_class
85
85
  end
86
86
 
87
- ## @private
88
- def self.included(mod)
89
- return if mod.respond_to?(:on_expand)
90
- mod.extend(ClassMethods)
91
- mod.include(Context::Key)
92
- end
93
-
94
87
  ##
95
88
  # Class methods that will be added to a template class.
96
89
  #
@@ -118,5 +111,14 @@ module Toys
118
111
  #
119
112
  attr_accessor :expansion
120
113
  end
114
+
115
+ ##
116
+ # @private
117
+ #
118
+ def self.included(mod)
119
+ return if mod.respond_to?(:on_expand)
120
+ mod.extend(ClassMethods)
121
+ mod.include(Context::Key)
122
+ end
121
123
  end
122
124
  end