toys-core 0.13.1 → 0.14.1

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.
@@ -140,7 +140,7 @@ module Toys
140
140
  ##
141
141
  # Create a SourceInfo.
142
142
  #
143
- # @private
143
+ # @private This interface is internal and subject to change without warning.
144
144
  #
145
145
  def initialize(parent, priority, context_directory, source_type, source_path, source_proc,
146
146
  git_remote, git_path, git_commit, source_name, data_dir_name, lib_dir_name)
@@ -165,7 +165,7 @@ module Toys
165
165
  ##
166
166
  # Create a child SourceInfo relative to the parent path.
167
167
  #
168
- # @private
168
+ # @private This interface is internal and subject to change without warning.
169
169
  #
170
170
  def relative_child(filename, source_name: nil)
171
171
  unless source_type == :directory
@@ -189,7 +189,7 @@ module Toys
189
189
  ##
190
190
  # Create a child SourceInfo with an absolute path.
191
191
  #
192
- # @private
192
+ # @private This interface is internal and subject to change without warning.
193
193
  #
194
194
  def absolute_child(child_path, source_name: nil)
195
195
  child_path, type = SourceInfo.check_path(child_path, false)
@@ -201,7 +201,7 @@ module Toys
201
201
  ##
202
202
  # Create a child SourceInfo with a git source.
203
203
  #
204
- # @private
204
+ # @private This interface is internal and subject to change without warning.
205
205
  #
206
206
  def git_child(child_git_remote, child_git_path, child_git_commit, child_path,
207
207
  source_name: nil)
@@ -216,7 +216,7 @@ module Toys
216
216
  ##
217
217
  # Create a proc child SourceInfo
218
218
  #
219
- # @private
219
+ # @private This interface is internal and subject to change without warning.
220
220
  #
221
221
  def proc_child(child_proc, source_name: nil)
222
222
  source_name ||= self.source_name
@@ -228,7 +228,7 @@ module Toys
228
228
  ##
229
229
  # Create a root source info for a file path.
230
230
  #
231
- # @private
231
+ # @private This interface is internal and subject to change without warning.
232
232
  #
233
233
  def self.create_path_root(source_path, priority,
234
234
  context_directory: nil,
@@ -250,7 +250,7 @@ module Toys
250
250
  ##
251
251
  # Create a root source info for a cached git repo.
252
252
  #
253
- # @private
253
+ # @private This interface is internal and subject to change without warning.
254
254
  #
255
255
  def self.create_git_root(git_remote, git_path, git_commit, source_path, priority,
256
256
  context_directory: nil,
@@ -266,7 +266,7 @@ module Toys
266
266
  ##
267
267
  # Create a root source info for a proc.
268
268
  #
269
- # @private
269
+ # @private This interface is internal and subject to change without warning.
270
270
  #
271
271
  def self.create_proc_root(source_proc, priority,
272
272
  context_directory: nil,
@@ -281,7 +281,7 @@ module Toys
281
281
  ##
282
282
  # Check a path and determine the canonical path and type.
283
283
  #
284
- # @private
284
+ # @private This interface is internal and subject to change without warning.
285
285
  #
286
286
  def self.check_path(path, lenient)
287
287
  path = ::File.expand_path(path)
@@ -285,23 +285,11 @@ module Toys
285
285
  separate_sources: @separate_sources,
286
286
  wrap_width: terminal.width
287
287
  )
288
- if less_path
289
- require "toys/utils/exec"
290
- Utils::Exec.new.exec([less_path, "-R"], in: [:string, str])
291
- else
292
- terminal.puts(str)
293
- end
294
- end
295
-
296
- def less_path
297
- unless defined? @less_path
298
- @less_path =
299
- if @use_less && @stream.tty?
300
- path = `which less`.strip
301
- path.empty? ? nil : path
302
- end
288
+ require "toys/utils/pager"
289
+ use_pager = @use_less && @stream.tty?
290
+ Utils::Pager.start(command: use_pager, fallback_io: terminal) do |io|
291
+ io.puts(str)
303
292
  end
304
- @less_path
305
293
  end
306
294
 
307
295
  def get_help_text(context, use_extra_args)
@@ -8,7 +8,7 @@ module Toys
8
8
  # in a string. Also provides an interface for controlling a spawned
9
9
  # process's streams.
10
10
  #
11
- # You may make these methods available to your tool by including the
11
+ # You can make these methods available to your tool by including the
12
12
  # following directive in your tool configuration:
13
13
  #
14
14
  # include :exec
@@ -54,54 +54,88 @@ module Toys
54
54
  # options.
55
55
  #
56
56
  # Three general strategies are available for custom stream handling. First,
57
- # you may redirect to other streams such as files, IO objects, or Ruby
57
+ # you can redirect to other streams such as files, IO objects, or Ruby
58
58
  # strings. Some of these options map directly to options provided by the
59
- # `Process#spawn` method. Second, you may use a controller to manipulate
60
- # the streams programmatically. Third, you may capture output stream data
59
+ # `Process#spawn` method. Second, you can use a controller to manipulate
60
+ # the streams programmatically. Third, you can capture output stream data
61
61
  # and make it available in the result.
62
62
  #
63
63
  # Following is a full list of the stream handling options, along with how
64
64
  # to specify them using the `:in`, `:out`, and `:err` options.
65
65
  #
66
- # * **Inherit parent stream:** You may inherit the corresponding stream
66
+ # * **Inherit parent stream:** You can inherit the corresponding stream
67
67
  # in the parent process by passing `:inherit` as the option value. This
68
68
  # is the default if the subprocess is *not* run in the background.
69
- # * **Redirect to null:** You may redirect to a null stream by passing
69
+ #
70
+ # * **Redirect to null:** You can redirect to a null stream by passing
70
71
  # `:null` as the option value. This connects to a stream that is not
71
72
  # closed but contains no data, i.e. `/dev/null` on unix systems. This
72
73
  # is the default if the subprocess is run in the background.
73
- # * **Close the stream:** You may close the stream by passing `:close` as
74
+ #
75
+ # * **Close the stream:** You can close the stream by passing `:close` as
74
76
  # the option value. This is the same as passing `:close` to
75
77
  # `Process#spawn`.
76
- # * **Redirect to a file:** You may redirect to a file. This reads from
78
+ #
79
+ # * **Redirect to a file:** You can redirect to a file. This reads from
77
80
  # an existing file when connected to `:in`, and creates or appends to a
78
81
  # file when connected to `:out` or `:err`. To specify a file, use the
79
- # setting `[:file, "/path/to/file"]`. You may also, when writing a
82
+ # setting `[:file, "/path/to/file"]`. You can also, when writing a
80
83
  # file, append an optional mode and permission code to the array. For
81
84
  # example, `[:file, "/path/to/file", "a", 0644]`.
82
- # * **Redirect to an IO object:** You may redirect to an IO object in the
83
- # parent process, by passing the IO object as the option value. You may
85
+ #
86
+ # * **Redirect to an IO object:** You can redirect to an IO object in the
87
+ # parent process, by passing the IO object as the option value. You can
84
88
  # use any IO object. For example, you could connect the child's output
85
89
  # to the parent's error using `out: $stderr`, or you could connect to
86
90
  # an existing File stream. Unlike `Process#spawn`, this works for IO
87
91
  # objects that do not have a corresponding file descriptor (such as
88
92
  # StringIO objects). In such a case, a thread will be spawned to pipe
89
93
  # the IO data through to the child process.
90
- # * **Combine with another child stream:** You may redirect one child
94
+ #
95
+ # * **Redirect to a pipe:** You can redirect to a pipe created using
96
+ # `IO.pipe` (i.e. a two-element array of read and write IO objects) by
97
+ # passing the array as the option value. This will connect the
98
+ # appropriate IO (either read or write), and close it in the parent.
99
+ # Thus, you can connect only one process to each end. If you want more
100
+ # direct control over IO closing behavior, pass the IO object (i.e. the
101
+ # element of the pipe array) directly.
102
+ #
103
+ # * **Combine with another child stream:** You can redirect one child
91
104
  # output stream to another, to combine them. To merge the child's error
92
105
  # stream into its output stream, use `err: [:child, :out]`.
93
- # * **Read from a string:** You may pass a string to the input stream by
106
+ #
107
+ # * **Read from a string:** You can pass a string to the input stream by
94
108
  # setting `[:string, "the string"]`. This works only for `:in`.
95
- # * **Capture output stream:** You may capture a stream and make it
109
+ #
110
+ # * **Capture output stream:** You can capture a stream and make it
96
111
  # available on the {Toys::Utils::Exec::Result} object, using the
97
112
  # setting `:capture`. This works only for the `:out` and `:err`
98
113
  # streams.
99
- # * **Use the controller:** You may hook a stream to the controller using
114
+ #
115
+ # * **Use the controller:** You can hook a stream to the controller using
100
116
  # the setting `:controller`. You can then manipulate the stream via the
101
117
  # controller. If you pass a block to {Toys::StandardMixins::Exec#exec},
102
118
  # it yields the {Toys::Utils::Exec::Controller}, giving you access to
103
119
  # streams.
104
120
  #
121
+ # * **Make copies of an output stream:** You can "tee," or duplicate the
122
+ # `:out` or `:err` stream and redirect those copies to various
123
+ # destinations. To specify a tee, use the setting `[:tee, ...]` where
124
+ # the additional array elements include two or more of the following.
125
+ # See the corresponding documentation above for more detail.
126
+ # * `:inherit` to direct to the parent process's stream.
127
+ # * `:capture` to capture the stream and store it in the result.
128
+ # * `:controller` to direct the stream to the controller.
129
+ # * `[:file, "/path/to/file"]` to write to a file.
130
+ # * An `IO` or `StringIO` object.
131
+ # * An array of two `IO` objects representing a pipe
132
+ #
133
+ # Additionally, the last element of the array can be a hash of options.
134
+ # Supported options include:
135
+ # * `:buffer_size` The size of the memory buffer for each element of
136
+ # the tee. Larger buffers may allow higher throughput. The default
137
+ # is 65536.
138
+ #
105
139
  # ### Result handling
106
140
  #
107
141
  # A subprocess result is represented by a {Toys::Utils::Exec::Result}
@@ -191,7 +225,7 @@ module Toys
191
225
  # not present, the command is not logged.
192
226
  #
193
227
  # * `:log_level` (Integer,false) Level for logging the actual command.
194
- # Defaults to Logger::INFO if not present. You may also pass `false` to
228
+ # Defaults to Logger::INFO if not present. You can also pass `false` to
195
229
  # disable logging of the command.
196
230
  #
197
231
  # * `:log_cmd` (String) The string logged for the actual command.
@@ -245,7 +279,7 @@ module Toys
245
279
  end
246
280
 
247
281
  ##
248
- # Execute a command. The command may be given as a single string to pass
282
+ # Execute a command. The command can be given as a single string to pass
249
283
  # to a shell, or an array of strings indicating a posix command.
250
284
  #
251
285
  # If the process is not set to run in the background, and a block is
@@ -352,7 +386,7 @@ module Toys
352
386
  ##
353
387
  # Execute a tool in the current CLI in a forked process.
354
388
  #
355
- # The command may be given as a single string or an array of strings,
389
+ # The command can be given as a single string or an array of strings,
356
390
  # representing the tool to run and the arguments to pass.
357
391
  #
358
392
  # If the process is not set to run in the background, and a block is
@@ -390,7 +424,7 @@ module Toys
390
424
  ##
391
425
  # Execute a tool in a separately spawned process.
392
426
  #
393
- # The command may be given as a single string or an array of strings,
427
+ # The command can be given as a single string or an array of strings,
394
428
  # representing the tool to run and the arguments to pass.
395
429
  #
396
430
  # If the process is not set to run in the background, and a block is
@@ -435,7 +469,7 @@ module Toys
435
469
  end
436
470
 
437
471
  ##
438
- # Execute a command. The command may be given as a single string to pass
472
+ # Execute a command. The command can be given as a single string to pass
439
473
  # to a shell, or an array of strings indicating a posix command.
440
474
  #
441
475
  # Captures standard out and returns it as a string.
@@ -540,7 +574,7 @@ module Toys
540
574
  # Captures standard out and returns it as a string.
541
575
  # Cannot be run in the background.
542
576
  #
543
- # The command may be given as a single string or an array of strings,
577
+ # The command can be given as a single string or an array of strings,
544
578
  # representing the tool to run and the arguments to pass.
545
579
  #
546
580
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
@@ -578,7 +612,7 @@ module Toys
578
612
  # Captures standard out and returns it as a string.
579
613
  # Cannot be run in the background.
580
614
  #
581
- # The command may be given as a single string or an array of strings,
615
+ # The command can be given as a single string or an array of strings,
582
616
  # representing the tool to run and the arguments to pass.
583
617
  #
584
618
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
@@ -662,6 +696,36 @@ module Toys
662
696
  0
663
697
  end
664
698
 
699
+ ##
700
+ # Returns an array of standard verbosity flags needed to replicate the
701
+ # current verbosity level. This is useful when you want to spawn tools
702
+ # with the same verbosity level as the current tool.
703
+ #
704
+ # @param short [Boolean] Whether to emit short rather than long flags.
705
+ # Default is false.
706
+ # @return [Array<String>]
707
+ #
708
+ def verbosity_flags(short: false)
709
+ verbosity = self[Context::Key::VERBOSITY]
710
+ if verbosity.positive?
711
+ if short
712
+ flag = "v" * verbosity
713
+ ["-#{flag}"]
714
+ else
715
+ ::Array.new(verbosity, "--verbose")
716
+ end
717
+ elsif verbosity.negative?
718
+ if short
719
+ flag = "q" * -verbosity
720
+ ["-#{flag}"]
721
+ else
722
+ ::Array.new(-verbosity, "--quiet")
723
+ end
724
+ else
725
+ []
726
+ end
727
+ end
728
+
665
729
  ##
666
730
  # @private
667
731
  #
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toys
4
+ module StandardMixins
5
+ ##
6
+ # A mixin that provides a pager.
7
+ #
8
+ # This mixin provides an instance of {Toys::Utils::Pager}, which invokes
9
+ # an external pager for output.
10
+ #
11
+ # @example
12
+ #
13
+ # include :pager
14
+ #
15
+ # def run
16
+ # pager do |io|
17
+ # io.puts "A long string\n"
18
+ # end
19
+ # end
20
+ #
21
+ module Pager
22
+ include Mixin
23
+
24
+ ##
25
+ # Context key for the Pager object.
26
+ # @return [Object]
27
+ #
28
+ KEY = ::Object.new.freeze
29
+
30
+ ##
31
+ # Access the Pager.
32
+ #
33
+ # If *no* block is given, returns the pager object.
34
+ #
35
+ # If a block is given, the pager is executed with the given block, and
36
+ # the exit code of the pager process is returned.
37
+ #
38
+ # @return [Toys::Utils::Pager] if no block is given.
39
+ # @return [Integer] if a block is given.
40
+ #
41
+ def pager(&block)
42
+ pager = self[KEY]
43
+ return pager unless block
44
+ self[KEY].start(&block)
45
+ end
46
+
47
+ on_initialize do
48
+ require "toys/utils/pager"
49
+ exec_service =
50
+ if defined?(::Toys::StandardMixins::Exec)
51
+ self[::Toys::StandardMixins::Exec::KEY]
52
+ end
53
+ self[KEY] = Utils::Pager.new(exec_service: exec_service)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -14,15 +14,15 @@ module Toys
14
14
  # the [XDG Base Directory Spec version
15
15
  # 0.8](https://specifications.freedesktop.org/basedir-spec/0.8/).
16
16
  #
17
- # Example usage:
17
+ # @example
18
18
  #
19
- # include :xdg
19
+ # include :xdg
20
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
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
26
  #
27
27
  module XDG
28
28
  include Mixin
@@ -136,7 +136,9 @@ module Toys
136
136
  tool_name, prefix, fragment = analyze_subtool_fragment(context)
137
137
  return unless tool_name
138
138
  subtools = context.cli.loader.list_subtools(tool_name,
139
- include_hidden: @include_hidden_subtools)
139
+ include_namespaces: true,
140
+ include_hidden: @include_hidden_subtools,
141
+ include_non_runnable: @include_hidden_subtools)
140
142
  return if subtools.empty?
141
143
  candidates = []
142
144
  subtools.each do |subtool|
@@ -214,7 +216,7 @@ module Toys
214
216
  # Create a new tool.
215
217
  # Should be created only from the DSL via the Loader.
216
218
  #
217
- # @private
219
+ # @private This interface is internal and subject to change without warning.
218
220
  #
219
221
  def initialize(parent, full_name, priority, source_root, middleware_stack, middleware_lookup,
220
222
  tool_class = nil)
@@ -241,7 +243,7 @@ module Toys
241
243
  # leaving named acceptors, mixins, and templates intact.
242
244
  # Should be called only from the DSL.
243
245
  #
244
- # @private
246
+ # @private This interface is internal and subject to change without warning.
245
247
  #
246
248
  def reset_definition
247
249
  @tool_class = @precreated_class || create_class
@@ -1276,7 +1278,7 @@ module Toys
1276
1278
  ##
1277
1279
  # Lookup the custom context directory in this tool and its ancestors.
1278
1280
  #
1279
- # @private
1281
+ # @private This interface is internal and subject to change without warning.
1280
1282
  #
1281
1283
  def lookup_custom_context_directory
1282
1284
  custom_context_directory || @parent&.lookup_custom_context_directory
@@ -1285,7 +1287,7 @@ module Toys
1285
1287
  ##
1286
1288
  # Mark this tool as having at least one module included.
1287
1289
  #
1288
- # @private
1290
+ # @private This interface is internal and subject to change without warning.
1289
1291
  #
1290
1292
  def mark_includes_modules
1291
1293
  check_definition_state
@@ -1297,7 +1299,7 @@ module Toys
1297
1299
  # Complete definition and run middleware configs. Should be called from
1298
1300
  # the Loader only.
1299
1301
  #
1300
- # @private
1302
+ # @private This interface is internal and subject to change without warning.
1301
1303
  #
1302
1304
  def finish_definition(loader)
1303
1305
  unless @definition_finished
@@ -1319,7 +1321,7 @@ module Toys
1319
1321
  ##
1320
1322
  # Run all initializers against a context. Called from the Runner.
1321
1323
  #
1322
- # @private
1324
+ # @private This interface is internal and subject to change without warning.
1323
1325
  #
1324
1326
  def run_initializers(context)
1325
1327
  @initializers.each do |func, args, kwargs|
@@ -1331,7 +1333,7 @@ module Toys
1331
1333
  # Check that the tool can still be defined. Should be called internally
1332
1334
  # or from the DSL only.
1333
1335
  #
1334
- # @private
1336
+ # @private This interface is internal and subject to change without warning.
1335
1337
  #
1336
1338
  def check_definition_state(is_arg: false, is_method: false)
1337
1339
  if @definition_finished