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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +18 -16
- data/docs/guide.md +111 -6
- data/lib/toys/acceptor.rb +64 -58
- data/lib/toys/arg_parser.rb +4 -4
- data/lib/toys/cli.rb +3 -3
- data/lib/toys/completion.rb +5 -1
- data/lib/toys/context.rb +4 -3
- data/lib/toys/core.rb +1 -1
- data/lib/toys/errors.rb +14 -3
- data/lib/toys/flag.rb +10 -2
- data/lib/toys/input_file.rb +8 -8
- data/lib/toys/loader.rb +160 -92
- data/lib/toys/middleware.rb +24 -5
- data/lib/toys/settings.rb +2 -2
- data/lib/toys/source_info.rb +9 -9
- data/lib/toys/standard_middleware/show_help.rb +4 -16
- data/lib/toys/standard_mixins/exec.rb +86 -22
- data/lib/toys/standard_mixins/pager.rb +57 -0
- data/lib/toys/standard_mixins/xdg.rb +7 -7
- data/lib/toys/tool_definition.rb +10 -8
- data/lib/toys/utils/exec.rb +218 -37
- data/lib/toys/utils/help_text.rb +4 -1
- data/lib/toys/utils/pager.rb +138 -0
- data/lib/toys/wrappable_string.rb +12 -0
- metadata +6 -4
data/lib/toys/source_info.rb
CHANGED
@@ -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
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
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
|
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
|
60
|
-
# the streams programmatically. Third, you
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
-
#
|
83
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
#
|
17
|
+
# @example
|
18
18
|
#
|
19
|
-
#
|
19
|
+
# include :xdg
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
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
|
data/lib/toys/tool_definition.rb
CHANGED
@@ -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
|
-
|
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
|