toys-core 0.11.5 → 0.13.0

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