toys-core 0.9.2 → 0.10.2
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/.yardopts +2 -1
- data/CHANGELOG.md +47 -0
- data/LICENSE.md +1 -1
- data/README.md +3 -3
- data/lib/toys-core.rb +14 -21
- data/lib/toys/acceptor.rb +0 -21
- data/lib/toys/arg_parser.rb +1 -22
- data/lib/toys/cli.rb +102 -70
- data/lib/toys/compat.rb +49 -41
- data/lib/toys/completion.rb +0 -21
- data/lib/toys/context.rb +0 -23
- data/lib/toys/core.rb +1 -22
- data/lib/toys/dsl/flag.rb +0 -21
- data/lib/toys/dsl/flag_group.rb +0 -21
- data/lib/toys/dsl/positional_arg.rb +0 -21
- data/lib/toys/dsl/tool.rb +136 -51
- data/lib/toys/errors.rb +1 -22
- data/lib/toys/flag.rb +0 -21
- data/lib/toys/flag_group.rb +0 -21
- data/lib/toys/input_file.rb +0 -21
- data/lib/toys/loader.rb +42 -78
- data/lib/toys/middleware.rb +146 -77
- data/lib/toys/mixin.rb +0 -21
- data/lib/toys/module_lookup.rb +3 -26
- data/lib/toys/positional_arg.rb +0 -21
- data/lib/toys/source_info.rb +49 -38
- data/lib/toys/standard_middleware/add_verbosity_flags.rb +0 -23
- data/lib/toys/standard_middleware/apply_config.rb +42 -0
- data/lib/toys/standard_middleware/handle_usage_errors.rb +7 -28
- data/lib/toys/standard_middleware/set_default_descriptions.rb +0 -23
- data/lib/toys/standard_middleware/show_help.rb +0 -23
- data/lib/toys/standard_middleware/show_root_version.rb +0 -23
- data/lib/toys/standard_mixins/bundler.rb +89 -0
- data/lib/toys/standard_mixins/exec.rb +478 -128
- data/lib/toys/standard_mixins/fileutils.rb +0 -21
- data/lib/toys/standard_mixins/gems.rb +2 -24
- data/lib/toys/standard_mixins/highline.rb +0 -21
- data/lib/toys/standard_mixins/terminal.rb +0 -21
- data/lib/toys/template.rb +0 -21
- data/lib/toys/tool.rb +22 -34
- data/lib/toys/utils/completion_engine.rb +0 -21
- data/lib/toys/utils/exec.rb +142 -71
- data/lib/toys/utils/gems.rb +181 -63
- data/lib/toys/utils/help_text.rb +0 -21
- data/lib/toys/utils/terminal.rb +46 -37
- data/lib/toys/wrappable_string.rb +0 -21
- metadata +25 -9
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
module StandardMiddleware
|
26
5
|
##
|
@@ -28,8 +7,6 @@ module Toys
|
|
28
7
|
# `--version` flag is given.
|
29
8
|
#
|
30
9
|
class ShowRootVersion
|
31
|
-
include Middleware
|
32
|
-
|
33
10
|
##
|
34
11
|
# Default version flags
|
35
12
|
# @return [Array<String>]
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Toys
|
4
|
+
module StandardMixins
|
5
|
+
##
|
6
|
+
# Ensures that a bundle is installed and set up when this tool is run.
|
7
|
+
#
|
8
|
+
# The following parameters can be passed when including this mixin:
|
9
|
+
#
|
10
|
+
# * `:groups` (Array<String>) The groups to include in setup
|
11
|
+
#
|
12
|
+
# * `:search_dirs` (Array<String,Symbol>) Directories to search for a
|
13
|
+
# Gemfile.
|
14
|
+
#
|
15
|
+
# You can pass full directory paths, and/or any of the following:
|
16
|
+
# * `:context` - the current context directory
|
17
|
+
# * `:current` - the current working directory
|
18
|
+
# * `:toys` - the Toys directory containing the tool definition
|
19
|
+
#
|
20
|
+
# The default is to search `[:toys, :context, :current]` in that order.
|
21
|
+
#
|
22
|
+
# * `:on_missing` (Symbol) What to do if a needed gem is not installed.
|
23
|
+
#
|
24
|
+
# Supported values:
|
25
|
+
# * `:confirm` - prompt the user on whether to install (default)
|
26
|
+
# * `:error` - raise an exception
|
27
|
+
# * `:install` - just install the gem
|
28
|
+
#
|
29
|
+
# * `:on_conflict` (Symbol) What to do if bundler has already been run
|
30
|
+
# with a different Gemfile.
|
31
|
+
#
|
32
|
+
# Supported values:
|
33
|
+
# * `:error` - raise an exception (default)
|
34
|
+
# * `:ignore` - just silently proceed without bundling again
|
35
|
+
# * `:warn` - print a warning and proceed without bundling again
|
36
|
+
#
|
37
|
+
# * `:terminal` (Toys::Utils::Terminal) Terminal to use (optional)
|
38
|
+
# * `:input` (IO) Input IO (optional, defaults to STDIN)
|
39
|
+
# * `:output` (IO) Output IO (optional, defaults to STDOUT)
|
40
|
+
#
|
41
|
+
module Bundler
|
42
|
+
include Mixin
|
43
|
+
|
44
|
+
on_initialize do
|
45
|
+
|groups: nil,
|
46
|
+
search_dirs: nil,
|
47
|
+
on_missing: nil,
|
48
|
+
on_conflict: nil,
|
49
|
+
terminal: nil,
|
50
|
+
input: nil,
|
51
|
+
output: nil|
|
52
|
+
require "toys/utils/gems"
|
53
|
+
search_dirs = ::Toys::StandardMixins::Bundler.resolve_search_dirs(search_dirs, self)
|
54
|
+
gems = ::Toys::Utils::Gems.new(on_missing: on_missing, on_conflict: on_conflict,
|
55
|
+
terminal: terminal, input: input, output: output)
|
56
|
+
gems.bundle(groups: groups, search_dirs: search_dirs)
|
57
|
+
end
|
58
|
+
|
59
|
+
## @private
|
60
|
+
def self.resolve_search_dirs(search_dirs, context)
|
61
|
+
search_dirs ||= [:toys, :context, :current]
|
62
|
+
Array(search_dirs).flat_map do |dir|
|
63
|
+
case dir
|
64
|
+
when :context
|
65
|
+
context[::Toys::Context::Key::CONTEXT_DIRECTORY]
|
66
|
+
when :current
|
67
|
+
::Dir.getwd
|
68
|
+
when :toys
|
69
|
+
toys_dir_stack(context[::Toys::Context::Key::TOOL_SOURCE])
|
70
|
+
when ::String
|
71
|
+
dir
|
72
|
+
else
|
73
|
+
raise ::ArgumentError, "Unrecognized search_dir: #{dir.inspect}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
## @private
|
79
|
+
def self.toys_dir_stack(source_info)
|
80
|
+
dirs = []
|
81
|
+
while source_info
|
82
|
+
dirs << source_info.source_path if source_info.source_type == :directory
|
83
|
+
source_info = source_info.parent
|
84
|
+
end
|
85
|
+
dirs
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,26 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright 2019 Daniel Azuma
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
# of this software and associated documentation files (the "Software"), to deal
|
7
|
-
# in the Software without restriction, including without limitation the rights
|
8
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
# copies of the Software, and to permit persons to whom the Software is
|
10
|
-
# furnished to do so, subject to the following conditions:
|
11
|
-
#
|
12
|
-
# The above copyright notice and this permission notice shall be included in
|
13
|
-
# all copies or substantial portions of the Software.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
20
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
21
|
-
# IN THE SOFTWARE.
|
22
|
-
;
|
23
|
-
|
24
3
|
module Toys
|
25
4
|
module StandardMixins
|
26
5
|
##
|
@@ -37,54 +16,211 @@ module Toys
|
|
37
16
|
# This is a frontend for {Toys::Utils::Exec}. More information is
|
38
17
|
# available in that class's documentation.
|
39
18
|
#
|
19
|
+
# ## Features
|
20
|
+
#
|
21
|
+
# ### Controlling processes
|
22
|
+
#
|
23
|
+
# A process can be started in the *foreground* or the *background*. If you
|
24
|
+
# start a foreground process, it will "take over" your standard input and
|
25
|
+
# output streams by default, and it will keep control until it completes.
|
26
|
+
# If you start a background process, its streams will be redirected to null
|
27
|
+
# by default, and control will be returned to you immediately.
|
28
|
+
#
|
29
|
+
# When a process is running, you can control it using a
|
30
|
+
# {Toys::Utils::Exec::Controller} object. Use a controller to interact with
|
31
|
+
# the process's input and output streams, send it signals, or wait for it
|
32
|
+
# to complete.
|
33
|
+
#
|
34
|
+
# When running a process in the foreground, the controller will be yielded
|
35
|
+
# to an optional block. For example, the following code starts a process in
|
36
|
+
# the foreground and passes its output stream to a controller.
|
37
|
+
#
|
38
|
+
# exec(["git", "init"], out: :controller) do |controller|
|
39
|
+
# loop do
|
40
|
+
# line = controller.out.gets
|
41
|
+
# break if line.nil?
|
42
|
+
# puts "Got line: #{line}"
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# When running a process in the background, the controller is returned from
|
47
|
+
# the method that starts the process:
|
48
|
+
#
|
49
|
+
# controller = exec_service.exec(["git", "init"], background: true)
|
50
|
+
#
|
51
|
+
# ### Stream handling
|
52
|
+
#
|
53
|
+
# By default, subprocess streams are connected to the corresponding streams
|
54
|
+
# in the parent process. You can change this behavior, redirecting streams
|
55
|
+
# or providing ways to control them, using the `:in`, `:out`, and `:err`
|
56
|
+
# options.
|
57
|
+
#
|
58
|
+
# Three general strategies are available for custom stream handling. First,
|
59
|
+
# you may redirect to other streams such as files, IO objects, or Ruby
|
60
|
+
# strings. Some of these options map directly to options provided by the
|
61
|
+
# `Process#spawn` method. Second, you may use a controller to manipulate
|
62
|
+
# the streams programmatically. Third, you may capture output stream data
|
63
|
+
# and make it available in the result.
|
64
|
+
#
|
65
|
+
# Following is a full list of the stream handling options, along with how
|
66
|
+
# to specify them using the `:in`, `:out`, and `:err` options.
|
67
|
+
#
|
68
|
+
# * **Inherit parent stream:** You may inherit the corresponding stream
|
69
|
+
# in the parent process by passing `:inherit` as the option value. This
|
70
|
+
# is the default if the subprocess is *not* run in the background.
|
71
|
+
# * **Redirect to null:** You may redirect to a null stream by passing
|
72
|
+
# `:null` as the option value. This connects to a stream that is not
|
73
|
+
# closed but contains no data, i.e. `/dev/null` on unix systems. This
|
74
|
+
# is the default if the subprocess is run in the background.
|
75
|
+
# * **Close the stream:** You may close the stream by passing `:close` as
|
76
|
+
# the option value. This is the same as passing `:close` to
|
77
|
+
# `Process#spawn`.
|
78
|
+
# * **Redirect to a file:** You may redirect to a file. This reads from
|
79
|
+
# an existing file when connected to `:in`, and creates or appends to a
|
80
|
+
# file when connected to `:out` or `:err`. To specify a file, use the
|
81
|
+
# setting `[:file, "/path/to/file"]`. You may also, when writing a
|
82
|
+
# file, append an optional mode and permission code to the array. For
|
83
|
+
# example, `[:file, "/path/to/file", "a", 0644]`.
|
84
|
+
# * **Redirect to an IO object:** You may redirect to an IO object in the
|
85
|
+
# parent process, by passing the IO object as the option value. You may
|
86
|
+
# use any IO object. For example, you could connect the child's output
|
87
|
+
# to the parent's error using `out: $stderr`, or you could connect to
|
88
|
+
# an existing File stream. Unlike `Process#spawn`, this works for IO
|
89
|
+
# objects that do not have a corresponding file descriptor (such as
|
90
|
+
# StringIO objects). In such a case, a thread will be spawned to pipe
|
91
|
+
# the IO data through to the child process.
|
92
|
+
# * **Combine with another child stream:** You may redirect one child
|
93
|
+
# output stream to another, to combine them. To merge the child's error
|
94
|
+
# stream into its output stream, use `err: [:child, :out]`.
|
95
|
+
# * **Read from a string:** You may pass a string to the input stream by
|
96
|
+
# setting `[:string, "the string"]`. This works only for `:in`.
|
97
|
+
# * **Capture output stream:** You may capture a stream and make it
|
98
|
+
# available on the {Toys::Utils::Exec::Result} object, using the
|
99
|
+
# setting `:capture`. This works only for the `:out` and `:err`
|
100
|
+
# streams.
|
101
|
+
# * **Use the controller:** You may hook a stream to the controller using
|
102
|
+
# the setting `:controller`. You can then manipulate the stream via the
|
103
|
+
# controller. If you pass a block to {Toys::StandardMixins::Exec#exec},
|
104
|
+
# it yields the {Toys::Utils::Exec::Controller}, giving you access to
|
105
|
+
# streams.
|
106
|
+
#
|
107
|
+
# ### Result handling
|
108
|
+
#
|
109
|
+
# A subprocess result is represented by a {Toys::Utils::Exec::Result}
|
110
|
+
# object, which includes the exit code, the content of any captured output
|
111
|
+
# streams, and any exeption raised when attempting to run the process.
|
112
|
+
# When you run a process in the foreground, the method will return a result
|
113
|
+
# object. When you run a process in the background, you can obtain the
|
114
|
+
# result from the controller once the process completes.
|
115
|
+
#
|
116
|
+
# The following example demonstrates running a process in the foreground
|
117
|
+
# and getting the exit code:
|
118
|
+
#
|
119
|
+
# result = exec(["git", "init"])
|
120
|
+
# puts "exit code: #{result.exit_code}"
|
121
|
+
#
|
122
|
+
# The following example demonstrates starting a process in the background,
|
123
|
+
# waiting for it to complete, and getting its exit code:
|
124
|
+
#
|
125
|
+
# controller = exec(["git", "init"], background: true)
|
126
|
+
# result = controller.result(timeout: 1.0)
|
127
|
+
# if result
|
128
|
+
# puts "exit code: #{result.exit_code}"
|
129
|
+
# else
|
130
|
+
# puts "timed out"
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# You can also provide a callback that is executed once a process
|
134
|
+
# completes. This callback can be specified as a method name or a `Proc`
|
135
|
+
# object, and will be passed the result object. For example:
|
136
|
+
#
|
137
|
+
# def run
|
138
|
+
# exec(["git", "init"], result_callback: :handle_result)
|
139
|
+
# end
|
140
|
+
# def handle_result(result)
|
141
|
+
# puts "exit code: #{result.exit_code}"
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# Finally, you can force your tool to exit if a subprocess fails, similar
|
145
|
+
# to setting the `set -e` option in bash, by setting the
|
146
|
+
# `:exit_on_nonzero_status` option. This is often set as a default
|
147
|
+
# configuration for all subprocesses run in a tool, by passing it as an
|
148
|
+
# argument to the `include` directive:
|
149
|
+
#
|
150
|
+
# include :exec, exit_on_nonzero_status: true
|
151
|
+
#
|
40
152
|
# ## Configuration Options
|
41
153
|
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# by
|
45
|
-
#
|
154
|
+
# A variety of options can be used to control subprocesses. These can be
|
155
|
+
# provided to any method that starts a subprocess. You can also set
|
156
|
+
# defaults by passing them as keyword arguments when you `include` the
|
157
|
+
# mixin.
|
158
|
+
#
|
159
|
+
# Options that affect the behavior of subprocesses:
|
160
|
+
#
|
161
|
+
# * `:env` (Hash) Environment variables to pass to the subprocess.
|
162
|
+
# Keys represent variable names and should be strings. Values should be
|
163
|
+
# either strings or `nil`, which unsets the variable.
|
164
|
+
#
|
165
|
+
# * `:background` (Boolean) Runs the process in the background if `true`.
|
166
|
+
#
|
167
|
+
# Options related to handling results
|
168
|
+
#
|
169
|
+
# * `:result_callback` (Proc,Symbol) A procedure that is called, and
|
170
|
+
# passed the result object, when the subprocess exits. You can provide
|
171
|
+
# a `Proc` object, or the name of a method as a `Symbol`.
|
172
|
+
#
|
173
|
+
# * `:exit_on_nonzero_status` (Boolean) If set to true, a nonzero exit
|
174
|
+
# code will cause the tool to exit immediately with that same code.
|
175
|
+
#
|
176
|
+
# * `:e` (Boolean) A short name for `:exit_on_nonzero_status`.
|
177
|
+
#
|
178
|
+
# Options for connecting input and output streams. See the section above on
|
179
|
+
# stream handling for info on the values that can be passed.
|
180
|
+
#
|
181
|
+
# * `:in` Connects the input stream of the subprocess. See the section on
|
182
|
+
# stream handling.
|
183
|
+
#
|
184
|
+
# * `:out` Connects the standard output stream of the subprocess. See the
|
185
|
+
# section on stream handling.
|
46
186
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# subprocesses spawned by this tool:
|
187
|
+
# * `:err` Connects the standard error stream of the subprocess. See the
|
188
|
+
# section on stream handling.
|
50
189
|
#
|
51
|
-
#
|
190
|
+
# Options related to logging and reporting:
|
52
191
|
#
|
53
|
-
#
|
192
|
+
# * `:logger` (Logger) Logger to use for logging the actual command. If
|
193
|
+
# not present, the command is not logged.
|
54
194
|
#
|
55
|
-
# *
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# to the `include` directive. In that case, `self` is not set to the
|
59
|
-
# context object as it normally would be in a tool's `run` method, so
|
60
|
-
# you cannot access it otherwise. For example, here is how to log the
|
61
|
-
# exit code for every subcommand:
|
195
|
+
# * `:log_level` (Integer,false) Level for logging the actual command.
|
196
|
+
# Defaults to Logger::INFO if not present. You may also pass `false` to
|
197
|
+
# disable logging of the command.
|
62
198
|
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# context.logger.info "Exit code: #{result.exit_code}"
|
66
|
-
# end
|
67
|
-
# include :exec, result_callback: callback
|
68
|
-
# # ...
|
69
|
-
# end
|
199
|
+
# * `:log_cmd` (String) The string logged for the actual command.
|
200
|
+
# Defaults to the `inspect` representation of the command.
|
70
201
|
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
# argument, the result object.
|
202
|
+
# * `:name` (Object) An optional object that can be used to identify this
|
203
|
+
# subprocess. It is available in the controller and result objects.
|
74
204
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
205
|
+
# In addition, the following options recognized by
|
206
|
+
# [`Process#spawn`](https://ruby-doc.org/core/Process.html#method-c-spawn)
|
207
|
+
# are supported.
|
78
208
|
#
|
79
|
-
#
|
80
|
-
# where it causes any subprocess failure to abort the tool, similar to
|
81
|
-
# setting `set -e` in a bash script.
|
209
|
+
# * `:chdir` (String) Set the working directory for the command.
|
82
210
|
#
|
83
|
-
#
|
211
|
+
# * `:close_others` (Boolean) Whether to close non-redirected
|
212
|
+
# non-standard file descriptors.
|
84
213
|
#
|
85
|
-
#
|
214
|
+
# * `:new_pgroup` (Boolean) Create new process group (Windows only).
|
86
215
|
#
|
87
|
-
#
|
216
|
+
# * `:pgroup` (Integer,true,nil) The process group setting.
|
217
|
+
#
|
218
|
+
# * `:umask` (Integer) Umask setting for the new process.
|
219
|
+
#
|
220
|
+
# * `:unsetenv_others` (Boolean) Clear environment variables except those
|
221
|
+
# explicitly set.
|
222
|
+
#
|
223
|
+
# Any other option key will result in an `ArgumentError`.
|
88
224
|
#
|
89
225
|
module Exec
|
90
226
|
include Mixin
|
@@ -110,10 +246,10 @@ module Toys
|
|
110
246
|
end
|
111
247
|
|
112
248
|
##
|
113
|
-
# Set default configuration
|
249
|
+
# Set default configuration options.
|
114
250
|
#
|
115
|
-
#
|
116
|
-
#
|
251
|
+
# See the {Toys::StandardMixins::Exec} module documentation for a
|
252
|
+
# description of the options.
|
117
253
|
#
|
118
254
|
# @param opts [keywords] The default options.
|
119
255
|
# @return [self]
|
@@ -131,12 +267,25 @@ module Toys
|
|
131
267
|
# If the process is not set to run in the background, and a block is
|
132
268
|
# provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
|
133
269
|
#
|
270
|
+
# ## Examples
|
271
|
+
#
|
272
|
+
# Run a command without a shell, and print the exit code (0 for success):
|
273
|
+
#
|
274
|
+
# result = exec(["git", "init"])
|
275
|
+
# puts "exit code: #{result.exit_code}"
|
276
|
+
#
|
277
|
+
# Run a shell command:
|
278
|
+
#
|
279
|
+
# result = exec("cd mydir && git init")
|
280
|
+
# puts "exit code: #{result.exit_code}"
|
281
|
+
#
|
134
282
|
# @param cmd [String,Array<String>] The command to execute.
|
135
|
-
# @param opts [keywords] The command options.
|
136
|
-
# {Toys::
|
137
|
-
#
|
283
|
+
# @param opts [keywords] The command options. See the section on
|
284
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
285
|
+
# documentation.
|
138
286
|
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
139
|
-
# the subprocess
|
287
|
+
# the subprocess. See the section on Controlling Processes in the
|
288
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
140
289
|
#
|
141
290
|
# @return [Toys::Utils::Exec::Controller] The subprocess controller, if
|
142
291
|
# the process is running in the background.
|
@@ -154,12 +303,19 @@ module Toys
|
|
154
303
|
# If the process is not set to run in the background, and a block is
|
155
304
|
# provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
|
156
305
|
#
|
306
|
+
# ## Example
|
307
|
+
#
|
308
|
+
# Execute a small script with warnings
|
309
|
+
#
|
310
|
+
# exec_ruby("-w", "-e", "(1..10).each { |i| puts i }")
|
311
|
+
#
|
157
312
|
# @param args [String,Array<String>] The arguments to ruby.
|
158
|
-
# @param opts [keywords] The command options.
|
159
|
-
# {Toys::
|
160
|
-
#
|
313
|
+
# @param opts [keywords] The command options. See the section on
|
314
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
315
|
+
# documentation.
|
161
316
|
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
162
|
-
#
|
317
|
+
# the subprocess. See the section on Controlling Processes in the
|
318
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
163
319
|
#
|
164
320
|
# @return [Toys::Utils::Exec::Controller] The subprocess controller, if
|
165
321
|
# the process is running in the background.
|
@@ -173,17 +329,31 @@ module Toys
|
|
173
329
|
alias ruby exec_ruby
|
174
330
|
|
175
331
|
##
|
176
|
-
# Execute a proc in a subprocess.
|
332
|
+
# Execute a proc in a forked subprocess.
|
177
333
|
#
|
178
334
|
# If the process is not set to run in the background, and a block is
|
179
335
|
# provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
|
180
336
|
#
|
337
|
+
# Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
|
338
|
+
# do not support this method because they do not support fork.
|
339
|
+
#
|
340
|
+
# ## Example
|
341
|
+
#
|
342
|
+
# Run a proc in a forked process.
|
343
|
+
#
|
344
|
+
# code = proc do
|
345
|
+
# puts "Spawned process ID is #{Process.pid}"
|
346
|
+
# end
|
347
|
+
# puts "Main process ID is #{Process.pid}"
|
348
|
+
# exec_proc(code)
|
349
|
+
#
|
181
350
|
# @param func [Proc] The proc to call.
|
182
|
-
# @param opts [keywords] The command options.
|
183
|
-
# {Toys::
|
184
|
-
#
|
185
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
186
|
-
#
|
351
|
+
# @param opts [keywords] The command options. See the section on
|
352
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
353
|
+
# documentation.
|
354
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
355
|
+
# the subprocess. See the section on Controlling Processes in the
|
356
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
187
357
|
#
|
188
358
|
# @return [Toys::Utils::Exec::Controller] The subprocess controller, if
|
189
359
|
# the process is running in the background.
|
@@ -196,18 +366,30 @@ module Toys
|
|
196
366
|
end
|
197
367
|
|
198
368
|
##
|
199
|
-
# Execute a tool
|
200
|
-
#
|
369
|
+
# Execute a tool in the current CLI in a forked process.
|
370
|
+
#
|
371
|
+
# The command may be given as a single string or an array of strings,
|
372
|
+
# representing the tool to run and the arguments to pass.
|
201
373
|
#
|
202
374
|
# If the process is not set to run in the background, and a block is
|
203
375
|
# provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
|
204
376
|
#
|
377
|
+
# Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
|
378
|
+
# do not support this method because they do not support fork.
|
379
|
+
#
|
380
|
+
# ## Example
|
381
|
+
#
|
382
|
+
# Run the "system update" tool and pass it an argument.
|
383
|
+
#
|
384
|
+
# exec_tool(["system", "update", "--verbose"])
|
385
|
+
#
|
205
386
|
# @param cmd [String,Array<String>] The tool to execute.
|
206
|
-
# @param opts [keywords] The command options.
|
207
|
-
# {Toys::
|
208
|
-
#
|
209
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
210
|
-
#
|
387
|
+
# @param opts [keywords] The command options. See the section on
|
388
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
389
|
+
# documentation.
|
390
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
391
|
+
# the subprocess. See the section on Controlling Processes in the
|
392
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
211
393
|
#
|
212
394
|
# @return [Toys::Utils::Exec::Controller] The subprocess controller, if
|
213
395
|
# the process is running in the background.
|
@@ -220,6 +402,53 @@ module Toys
|
|
220
402
|
self[KEY].exec_proc(func, **opts, &block)
|
221
403
|
end
|
222
404
|
|
405
|
+
##
|
406
|
+
# Execute a tool in a separately spawned process.
|
407
|
+
#
|
408
|
+
# The command may be given as a single string or an array of strings,
|
409
|
+
# representing the tool to run and the arguments to pass.
|
410
|
+
#
|
411
|
+
# If the process is not set to run in the background, and a block is
|
412
|
+
# provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
|
413
|
+
#
|
414
|
+
# An entirely separate spawned process is run for this tool, using the
|
415
|
+
# setting of {Toys.executable_path}. Thus, this method can be run only if
|
416
|
+
# that setting is present. The normal Toys gem does set it, but if you
|
417
|
+
# are writing your own executable using Toys-Core, you will need to set
|
418
|
+
# it explicitly for this method to work. Furthermore, Bundler, if
|
419
|
+
# present, is reset to its "unbundled" environment. Thus, the tool found,
|
420
|
+
# the behavior of the CLI, and the gem environment, might not be the same
|
421
|
+
# as those of the calling tool.
|
422
|
+
#
|
423
|
+
# This method is often used if you are already in a bundle and need to
|
424
|
+
# run a tool that uses a different bundle. It may also be necessary on
|
425
|
+
# environments without "fork" (such as JRuby or Ruby on Windows).
|
426
|
+
#
|
427
|
+
# ## Example
|
428
|
+
#
|
429
|
+
# Run the "system update" tool and pass it an argument.
|
430
|
+
#
|
431
|
+
# exec_separate_tool(["system", "update", "--verbose"])
|
432
|
+
#
|
433
|
+
# @param cmd [String,Array<String>] The tool to execute.
|
434
|
+
# @param opts [keywords] The command options. See the section on
|
435
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
436
|
+
# documentation.
|
437
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
438
|
+
# the subprocess. See the section on Controlling Processes in the
|
439
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
440
|
+
#
|
441
|
+
# @return [Toys::Utils::Exec::Controller] The subprocess controller, if
|
442
|
+
# the process is running in the background.
|
443
|
+
# @return [Toys::Utils::Exec::Result] The result, if the process ran in
|
444
|
+
# the foreground.
|
445
|
+
#
|
446
|
+
def exec_separate_tool(cmd, **opts, &block)
|
447
|
+
Exec._setup_clean_process(cmd) do |clean_cmd|
|
448
|
+
exec(clean_cmd, **opts, &block)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
223
452
|
##
|
224
453
|
# Execute a command. The command may be given as a single string to pass
|
225
454
|
# to a shell, or an array of strings indicating a posix command.
|
@@ -230,12 +459,20 @@ module Toys
|
|
230
459
|
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
231
460
|
# yielded to it.
|
232
461
|
#
|
462
|
+
# ## Example
|
463
|
+
#
|
464
|
+
# Capture the output of an echo command
|
465
|
+
#
|
466
|
+
# str = capture(["echo", "hello"])
|
467
|
+
# assert_equal("hello\n", str)
|
468
|
+
#
|
233
469
|
# @param cmd [String,Array<String>] The command to execute.
|
234
|
-
# @param opts [keywords] The command options.
|
235
|
-
# {Toys::
|
236
|
-
#
|
237
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
238
|
-
#
|
470
|
+
# @param opts [keywords] The command options. See the section on
|
471
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
472
|
+
# documentation.
|
473
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
474
|
+
# the subprocess. See the section on Controlling Processes in the
|
475
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
239
476
|
#
|
240
477
|
# @return [String] What was written to standard out.
|
241
478
|
#
|
@@ -253,12 +490,20 @@ module Toys
|
|
253
490
|
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
254
491
|
# yielded to it.
|
255
492
|
#
|
493
|
+
# ## Example
|
494
|
+
#
|
495
|
+
# Capture the output of a ruby script.
|
496
|
+
#
|
497
|
+
# str = capture_ruby("-e", "(1..3).each { |i| puts i }")
|
498
|
+
# assert_equal "1\n2\n3\n", str
|
499
|
+
#
|
256
500
|
# @param args [String,Array<String>] The arguments to ruby.
|
257
|
-
# @param opts [keywords] The command options.
|
258
|
-
# {Toys::
|
259
|
-
#
|
260
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
261
|
-
#
|
501
|
+
# @param opts [keywords] The command options. See the section on
|
502
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
503
|
+
# documentation.
|
504
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
505
|
+
# the subprocess. See the section on Controlling Processes in the
|
506
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
262
507
|
#
|
263
508
|
# @return [String] What was written to standard out.
|
264
509
|
#
|
@@ -268,7 +513,7 @@ module Toys
|
|
268
513
|
end
|
269
514
|
|
270
515
|
##
|
271
|
-
# Execute a proc in a subprocess.
|
516
|
+
# Execute a proc in a forked subprocess.
|
272
517
|
#
|
273
518
|
# Captures standard out and returns it as a string.
|
274
519
|
# Cannot be run in the background.
|
@@ -276,12 +521,26 @@ module Toys
|
|
276
521
|
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
277
522
|
# yielded to it.
|
278
523
|
#
|
524
|
+
# Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
|
525
|
+
# do not support this method because they do not support fork.
|
526
|
+
#
|
527
|
+
# ## Example
|
528
|
+
#
|
529
|
+
# Run a proc in a forked process and capture its output:
|
530
|
+
#
|
531
|
+
# code = proc do
|
532
|
+
# puts Process.pid
|
533
|
+
# end
|
534
|
+
# forked_pid = capture_proc(code).chomp
|
535
|
+
# puts "I forked PID #{forked_pid}"
|
536
|
+
#
|
279
537
|
# @param func [Proc] The proc to call.
|
280
|
-
# @param opts [keywords] The command options.
|
281
|
-
# {Toys::
|
282
|
-
#
|
283
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
284
|
-
#
|
538
|
+
# @param opts [keywords] The command options. See the section on
|
539
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
540
|
+
# documentation.
|
541
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
542
|
+
# the subprocess. See the section on Controlling Processes in the
|
543
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
285
544
|
#
|
286
545
|
# @return [String] What was written to standard out.
|
287
546
|
#
|
@@ -291,21 +550,34 @@ module Toys
|
|
291
550
|
end
|
292
551
|
|
293
552
|
##
|
294
|
-
# Execute a tool
|
295
|
-
# of strings, representing the tool to run and the arguments to pass.
|
553
|
+
# Execute a tool in the current CLI in a forked process.
|
296
554
|
#
|
297
555
|
# Captures standard out and returns it as a string.
|
298
556
|
# Cannot be run in the background.
|
299
557
|
#
|
558
|
+
# The command may be given as a single string or an array of strings,
|
559
|
+
# representing the tool to run and the arguments to pass.
|
560
|
+
#
|
300
561
|
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
301
562
|
# yielded to it.
|
302
563
|
#
|
564
|
+
# Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
|
565
|
+
# do not support this method because they do not support fork.
|
566
|
+
#
|
567
|
+
# ## Example
|
568
|
+
#
|
569
|
+
# Run the "system version" tool and capture its output.
|
570
|
+
#
|
571
|
+
# str = capture_tool(["system", "version"]).chomp
|
572
|
+
# puts "Version was #{str}"
|
573
|
+
#
|
303
574
|
# @param cmd [String,Array<String>] The tool to execute.
|
304
|
-
# @param opts [keywords] The command options.
|
305
|
-
# {Toys::
|
306
|
-
#
|
307
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
308
|
-
#
|
575
|
+
# @param opts [keywords] The command options. See the section on
|
576
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
577
|
+
# documentation.
|
578
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
579
|
+
# the subprocess. See the section on Controlling Processes in the
|
580
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
309
581
|
#
|
310
582
|
# @return [String] What was written to standard out.
|
311
583
|
#
|
@@ -315,6 +587,54 @@ module Toys
|
|
315
587
|
self[KEY].capture_proc(func, **opts, &block)
|
316
588
|
end
|
317
589
|
|
590
|
+
##
|
591
|
+
# Execute a tool in a separately spawned process.
|
592
|
+
#
|
593
|
+
# Captures standard out and returns it as a string.
|
594
|
+
# Cannot be run in the background.
|
595
|
+
#
|
596
|
+
# The command may be given as a single string or an array of strings,
|
597
|
+
# representing the tool to run and the arguments to pass.
|
598
|
+
#
|
599
|
+
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
600
|
+
# yielded to it.
|
601
|
+
#
|
602
|
+
# An entirely separate spawned process is run for this tool, using the
|
603
|
+
# setting of {Toys.executable_path}. Thus, this method can be run only if
|
604
|
+
# that setting is present. The normal Toys gem does set it, but if you
|
605
|
+
# are writing your own executable using Toys-Core, you will need to set
|
606
|
+
# it explicitly for this method to work. Furthermore, Bundler, if
|
607
|
+
# present, is reset to its "unbundled" environment. Thus, the tool found,
|
608
|
+
# the behavior of the CLI, and the gem environment, might not be the same
|
609
|
+
# as those of the calling tool.
|
610
|
+
#
|
611
|
+
# This method is often used if you are already in a bundle and need to
|
612
|
+
# run a tool that uses a different bundle. It may also be necessary on
|
613
|
+
# environments without "fork" (such as JRuby or Ruby on Windows).
|
614
|
+
#
|
615
|
+
# ## Example
|
616
|
+
#
|
617
|
+
# Run the "system version" tool and capture its output.
|
618
|
+
#
|
619
|
+
# str = capture_separate_tool(["system", "version"]).chomp
|
620
|
+
# puts "Version was #{str}"
|
621
|
+
#
|
622
|
+
# @param cmd [String,Array<String>] The tool to execute.
|
623
|
+
# @param opts [keywords] The command options. See the section on
|
624
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
625
|
+
# documentation.
|
626
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
627
|
+
# the subprocess. See the section on Controlling Processes in the
|
628
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
629
|
+
#
|
630
|
+
# @return [String] What was written to standard out.
|
631
|
+
#
|
632
|
+
def capture_separate_tool(cmd, **opts, &block)
|
633
|
+
Exec._setup_clean_process(cmd) do |clean_cmd|
|
634
|
+
capture(clean_cmd, **opts, &block)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
318
638
|
##
|
319
639
|
# Execute the given string in a shell. Returns the exit code.
|
320
640
|
# Cannot be run in the background.
|
@@ -322,12 +642,20 @@ module Toys
|
|
322
642
|
# If a block is provided, a {Toys::Utils::Exec::Controller} will be
|
323
643
|
# yielded to it.
|
324
644
|
#
|
645
|
+
# ## Example
|
646
|
+
#
|
647
|
+
# Run a shell script
|
648
|
+
#
|
649
|
+
# exit_code = sh("cd mydir && git init")
|
650
|
+
# puts exit_code == 0 ? "Success!" : "Failed!"
|
651
|
+
#
|
325
652
|
# @param cmd [String] The shell command to execute.
|
326
|
-
# @param opts [keywords] The command options.
|
327
|
-
# {Toys::
|
328
|
-
#
|
329
|
-
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller
|
330
|
-
#
|
653
|
+
# @param opts [keywords] The command options. See the section on
|
654
|
+
# Configuration Options in the {Toys::StandardMixins::Exec} module
|
655
|
+
# documentation.
|
656
|
+
# @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
|
657
|
+
# the subprocess. See the section on Controlling Processes in the
|
658
|
+
# {Toys::StandardMixins::Exec} module documentation.
|
331
659
|
#
|
332
660
|
# @return [Integer] The exit code
|
333
661
|
#
|
@@ -357,37 +685,59 @@ module Toys
|
|
357
685
|
|
358
686
|
## @private
|
359
687
|
def self._setup_exec_opts(opts, context)
|
688
|
+
count = 0
|
689
|
+
result_callback = nil
|
360
690
|
if opts.key?(:result_callback)
|
361
|
-
|
691
|
+
result_callback = _interpret_result_callback(opts[:result_callback], context)
|
692
|
+
count += 1
|
693
|
+
end
|
694
|
+
[:exit_on_nonzero_status, :e].each do |sym|
|
695
|
+
if opts.key?(sym)
|
696
|
+
result_callback = _interpret_e(opts[sym], context)
|
697
|
+
count += 1
|
698
|
+
opts = opts.reject { |k, _v| k == sym }
|
699
|
+
end
|
362
700
|
end
|
363
|
-
if
|
364
|
-
|
701
|
+
if count > 1
|
702
|
+
raise ::ArgumentError,
|
703
|
+
"You can provide at most one of: result_callback, exit_on_nonzero_status, e"
|
365
704
|
end
|
705
|
+
opts = opts.merge(result_callback: result_callback) if count == 1
|
366
706
|
opts
|
367
707
|
end
|
368
708
|
|
369
709
|
## @private
|
370
|
-
def self.
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
710
|
+
def self._interpret_e(value, context)
|
711
|
+
value ? proc { |r| context.exit(r.exit_code) if r.error? } : nil
|
712
|
+
end
|
713
|
+
|
714
|
+
## @private
|
715
|
+
def self._interpret_result_callback(value, context)
|
716
|
+
if value.is_a?(::Symbol)
|
717
|
+
context.method(value)
|
718
|
+
elsif value.respond_to?(:call)
|
719
|
+
proc { |r| context.instance_eval { value.call(r, context) } }
|
720
|
+
elsif value.nil?
|
721
|
+
nil
|
722
|
+
else
|
723
|
+
raise ::ArgumentError, "Bad value for result_callback"
|
724
|
+
end
|
379
725
|
end
|
380
726
|
|
381
727
|
## @private
|
382
|
-
def self.
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
728
|
+
def self._setup_clean_process(cmd)
|
729
|
+
raise ::ArgumentError, "Toys process is unknown" unless ::Toys.executable_path
|
730
|
+
cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
|
731
|
+
cmd = Array(::Toys.executable_path) + cmd
|
732
|
+
if defined?(::Bundler)
|
733
|
+
if ::Bundler.respond_to?(:with_unbundled_env)
|
734
|
+
::Bundler.with_unbundled_env { yield(cmd) }
|
735
|
+
else
|
736
|
+
::Bundler.with_clean_env { yield(cmd) }
|
389
737
|
end
|
390
|
-
|
738
|
+
else
|
739
|
+
yield(cmd)
|
740
|
+
end
|
391
741
|
end
|
392
742
|
end
|
393
743
|
end
|