toys-core 0.13.1 → 0.14.1

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