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.
- 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
|