toys-core 0.10.0 → 0.10.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a9f077375706e67827afa561f71081c121d0eb00c30be6586501e6c76ab47c5
4
- data.tar.gz: 305e31b023d22f687a2406efdd0cea377a1c9725626c8320cebbf013b10f145b
3
+ metadata.gz: 3f0e62a1c71082677fd2c257f95adec476cd8c1f5d83a8f42854e48cfe52d1bf
4
+ data.tar.gz: d5940e36d77ce386ce7be949b2e16af08454095300a5beb3ffafdf4f691abe26
5
5
  SHA512:
6
- metadata.gz: 661b472175296ea3224e3dee08902ca8b2bf04e78f949109a91f4fa471c0b7904a454e0b18e845cf46d8cf04458872c84f55fd3f43bcbb2f3ecd40478783b8a5
7
- data.tar.gz: f9761ab43fffd295af8629906ae3fdee621c42d5df49a92b3539cc68a9588546338a4fdd3a70459639c3127c261de443c5f2359778807d281b451b4c2655316a
6
+ metadata.gz: a59b68df5403e100330518056d0e8d9b8792f9e01e36792b445413300e40e2637a86847af9afe565084a55e3bf3b332f1eff15cacc42a56c95597c94a47bd27a
7
+ data.tar.gz: a8d943e4ccb7a2b9f3278a0eabf4c33485fb09d2fce2440d3f3c3cdc605a30977a0d43da993018b6affb51933686f69e038b5bf1847df54e1cdfab981af77309
@@ -1,5 +1,30 @@
1
1
  # Release History
2
2
 
3
+ ### 0.10.5 / 2020-07-18
4
+
5
+ * IMPROVED: The bundler mixin silences bundler output during bundle setup.
6
+ * IMPROVED: The bundler mixin allows toys and toys-core to be in the Gemfile. It checks their version requirements against the running Toys version, and either adds the corret version to the bundle or raises IncompatibleToysError.
7
+ * IMPROVED: The bundler mixin utomatically updates the bundle if install fails (typically because a transitive dependency has been explicitly updated.)
8
+ * FIXED: Some cases of transitive dependency handling by the bundler mixin.
9
+ * FIXED: Fixed a crash when computing suggestions, when running with a bundle on Ruby 2.6 or earlier.
10
+
11
+ ### 0.10.4 / 2020-07-11
12
+
13
+ * IMPROVED: Bundler integration can now handle Toys itself being in the bundle, as long as the version requirements cover the running Toys version.
14
+ * IMPROVED: Passing `static: true` to the `:bundler` mixin installs the bundle at definition rather than execution time.
15
+
16
+ ### 0.10.3 / 2020-07-04
17
+
18
+ * FIXED: The `exec_separate_tool` method in the `:exec` mixin no longer throws ENOEXEC on Windows.
19
+
20
+ ### 0.10.2 / 2020-07-03
21
+
22
+ * FIXED: The load path no longer loses the toys and toys-core directories after a bundle install.
23
+
24
+ ### 0.10.1 / 2020-03-07
25
+
26
+ * FIXED: Setting `:exit_on_nonzero_status` explicitly to false now works as expected.
27
+
3
28
  ### 0.10.0 / 2020-02-24
4
29
 
5
30
  Functional changes:
data/README.md CHANGED
@@ -258,7 +258,7 @@ itself. However, the `toys-core` gem is a dependency, and your users will need
258
258
  to have it installed. You could alleviate this by wrapping your executable in a
259
259
  gem that can declare `toys-core` as a dependency explicitly.
260
260
 
261
- The [examples directory](https://github.com/dazuma/toys/tree/master/toys-core/examples)
261
+ The [examples directory](https://github.com/dazuma/toys/tree/main/toys-core/examples)
262
262
  includes a few simple examples that you can use as a starting point.
263
263
 
264
264
  To experiment with the examples, clone the Toys repo from GitHub:
@@ -272,7 +272,7 @@ Navigate to the simple-gem example:
272
272
 
273
273
  This example wraps the simple "greet" executable that we
274
274
  [covered earlier](#Add_some_functionality) in a gem. You can see the
275
- [executable file](https://github.com/dazuma/toys/tree/master/toys-core/examples/simple-gem/bin/toys-core-simple-example)
275
+ [executable file](https://github.com/dazuma/toys/tree/main/toys-core/examples/simple-gem/bin/toys-core-simple-example)
276
276
  in the bin directory.
277
277
 
278
278
  Try it out by building and installing the gem. From the `examples/simple-gem`
@@ -297,16 +297,16 @@ break it up into multiple files. The multi-file gem example demonstrates this.
297
297
  $ cd ../multi-file-gem
298
298
 
299
299
  This executable's implementation resides in its
300
- [lib directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib),
300
+ [lib directory](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/lib),
301
301
  a technique that may be familiar to writers of command line executables. More
302
302
  interestingly, the tools themselves are no longer defined in a block passed to
303
303
  the CLI object, but have been moved into a separate
304
- ["tools" directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/tools).
304
+ ["tools" directory](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/tools).
305
305
  This directory has the same structure and supports the same features that are
306
306
  available when writing complex sets of tools in a `.toys` directory. You then
307
307
  configure the CLI object to look in this directory for its tools definitions,
308
308
  as you can see in
309
- [the code](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib/toys-core-multi-gem-example.rb).
309
+ [the code](https://github.com/dazuma/toys/tree/main/toys-core/examples/multi-file-gem/lib/toys-core-multi-gem-example.rb).
310
310
 
311
311
  Try it out now. From the `examples/multi-file-gem` directory, run:
312
312
 
@@ -58,6 +58,9 @@ module Toys
58
58
  #
59
59
  attr_accessor :executable_path
60
60
  end
61
+
62
+ # @private
63
+ CORE_LIB_PATH = __dir__
61
64
  end
62
65
 
63
66
  require "toys/acceptor"
@@ -16,21 +16,30 @@ module Toys
16
16
  ::RUBY_PLATFORM == "java"
17
17
  end
18
18
 
19
+ # @private
20
+ def self.windows?
21
+ ::RbConfig::CONFIG["host_os"] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
22
+ end
23
+
19
24
  # @private
20
25
  def self.allow_fork?
21
- !jruby? && ::RbConfig::CONFIG["host_os"] !~ /mswin/
26
+ !jruby? && !windows?
22
27
  end
23
28
 
24
29
  # @private
25
30
  def self.supports_suggestions?
26
31
  unless defined?(@supports_suggestions)
27
- require "rubygems"
28
32
  begin
29
33
  require "did_you_mean"
30
- @supports_suggestions = defined?(::DidYouMean::SpellChecker)
31
34
  rescue ::LoadError
32
- @supports_suggestions = false
35
+ require "rubygems"
36
+ begin
37
+ require "did_you_mean"
38
+ rescue ::LoadError
39
+ # Oh well, it's not available
40
+ end
33
41
  end
42
+ @supports_suggestions = defined?(::DidYouMean::SpellChecker)
34
43
  end
35
44
  @supports_suggestions
36
45
  end
@@ -9,7 +9,7 @@ module Toys
9
9
  # Current version of Toys core.
10
10
  # @return [String]
11
11
  #
12
- VERSION = "0.10.0"
12
+ VERSION = "0.10.5"
13
13
  end
14
14
 
15
15
  ## @private deprecated
@@ -7,6 +7,10 @@ module Toys
7
7
  #
8
8
  # The following parameters can be passed when including this mixin:
9
9
  #
10
+ # * `:static` (Boolean) If `true`, installs the bundle immediately, when
11
+ # defining the tool. If `false` (the default), installs the bundle just
12
+ # before the tool runs.
13
+ #
10
14
  # * `:groups` (Array<String>) The groups to include in setup
11
15
  #
12
16
  # * `:search_dirs` (Array<String,Symbol>) Directories to search for a
@@ -41,32 +45,39 @@ module Toys
41
45
  module Bundler
42
46
  include Mixin
43
47
 
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)
48
+ on_initialize do |static: false, search_dirs: nil, **kwargs|
49
+ unless static
50
+ require "toys/utils/gems"
51
+ search_dirs = ::Toys::StandardMixins::Bundler.resolve_search_dirs(
52
+ search_dirs,
53
+ self[::Toys::Context::Key::CONTEXT_DIRECTORY],
54
+ self[::Toys::Context::Key::TOOL_SOURCE]
55
+ )
56
+ ::Toys::StandardMixins::Bundler.setup_bundle(search_dirs, **kwargs)
57
+ end
58
+ end
59
+
60
+ on_include do |static: false, search_dirs: nil, **kwargs|
61
+ if static
62
+ require "toys/utils/gems"
63
+ search_dirs = ::Toys::StandardMixins::Bundler.resolve_search_dirs(
64
+ search_dirs, context_directory, source_info
65
+ )
66
+ ::Toys::StandardMixins::Bundler.setup_bundle(search_dirs, **kwargs)
67
+ end
57
68
  end
58
69
 
59
70
  ## @private
60
- def self.resolve_search_dirs(search_dirs, context)
71
+ def self.resolve_search_dirs(search_dirs, context_dir, source_info)
61
72
  search_dirs ||= [:toys, :context, :current]
62
73
  Array(search_dirs).flat_map do |dir|
63
74
  case dir
64
75
  when :context
65
- context[::Toys::Context::Key::CONTEXT_DIRECTORY]
76
+ context_dir
66
77
  when :current
67
78
  ::Dir.getwd
68
79
  when :toys
69
- toys_dir_stack(context[::Toys::Context::Key::TOOL_SOURCE])
80
+ toys_dir_stack(source_info)
70
81
  when ::String
71
82
  dir
72
83
  else
@@ -84,6 +95,19 @@ module Toys
84
95
  end
85
96
  dirs
86
97
  end
98
+
99
+ ## @private
100
+ def self.setup_bundle(search_dirs,
101
+ groups: nil,
102
+ on_missing: nil,
103
+ on_conflict: nil,
104
+ terminal: nil,
105
+ input: nil,
106
+ output: nil)
107
+ gems = ::Toys::Utils::Gems.new(on_missing: on_missing, on_conflict: on_conflict,
108
+ terminal: terminal, input: input, output: output)
109
+ gems.bundle(groups: groups, search_dirs: search_dirs)
110
+ end
87
111
  end
88
112
  end
89
113
  end
@@ -16,54 +16,211 @@ module Toys
16
16
  # This is a frontend for {Toys::Utils::Exec}. More information is
17
17
  # available in that class's documentation.
18
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
+ #
19
152
  # ## Configuration Options
20
153
  #
21
- # Subprocesses may be configured using the options in the
22
- # {Toys::Utils::Exec} class. These include a variety of options supported
23
- # by `Process#spawn`, and some options supported by {Toys::Utils::Exec}
24
- # itself.
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.
25
186
  #
26
- # You can set default configuration by passing options to the `include`
27
- # directive. For example, to log commands at the debug level for all
28
- # subprocesses spawned by this tool:
187
+ # * `:err` Connects the standard error stream of the subprocess. See the
188
+ # section on stream handling.
29
189
  #
30
- # include :exec, log_level: Logger::DEBUG
190
+ # Options related to logging and reporting:
31
191
  #
32
- # Two special options are also recognized by the mixin.
192
+ # * `:logger` (Logger) Logger to use for logging the actual command. If
193
+ # not present, the command is not logged.
33
194
  #
34
- # * A **:result_callback** proc may take a second argument. If it does,
35
- # the context object is passed as the second argument. This is useful
36
- # if a `:result_callback` is applied to the entire tool by passing it
37
- # to the `include` directive. In that case, `self` is not set to the
38
- # context object as it normally would be in a tool's `run` method, so
39
- # you cannot access it otherwise. For example, here is how to log the
40
- # 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.
41
198
  #
42
- # tool "mytool" do
43
- # callback = proc do |result, context|
44
- # context.logger.info "Exit code: #{result.exit_code}"
45
- # end
46
- # include :exec, result_callback: callback
47
- # # ...
48
- # end
199
+ # * `:log_cmd` (String) The string logged for the actual command.
200
+ # Defaults to the `inspect` representation of the command.
49
201
  #
50
- # You may also pass a symbol as the `:result_callback`. The method with
51
- # that name is then called as the callback. The method must take one
52
- # 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.
53
204
  #
54
- # * If **:exit_on_nonzero_status** is set to true, a nonzero exit code
55
- # returned by the subprocess will also cause the tool to exit
56
- # immediately with that same code.
205
+ # In addition, the following options recognized by
206
+ # [`Process#spawn`](https://ruby-doc.org/core/Process.html#method-c-spawn)
207
+ # are supported.
57
208
  #
58
- # This is particularly useful as an option to the `include` directive,
59
- # where it causes any subprocess failure to abort the tool, similar to
60
- # setting `set -e` in a bash script.
209
+ # * `:chdir` (String) Set the working directory for the command.
61
210
  #
62
- # include :exec, exit_on_nonzero_status: true
211
+ # * `:close_others` (Boolean) Whether to close non-redirected
212
+ # non-standard file descriptors.
63
213
  #
64
- # **:e** can be used as a shortcut for **:exit_on_nonzero_status**
214
+ # * `:new_pgroup` (Boolean) Create new process group (Windows only).
65
215
  #
66
- # include :exec, e: true
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`.
67
224
  #
68
225
  module Exec
69
226
  include Mixin
@@ -89,10 +246,10 @@ module Toys
89
246
  end
90
247
 
91
248
  ##
92
- # Set default configuration keys.
249
+ # Set default configuration options.
93
250
  #
94
- # All options listed in the {Toys::Utils::Exec} documentation are
95
- # supported, plus the `exit_on_nonzero_status` option.
251
+ # See the {Toys::StandardMixins::Exec} module documentation for a
252
+ # description of the options.
96
253
  #
97
254
  # @param opts [keywords] The default options.
98
255
  # @return [self]
@@ -110,12 +267,25 @@ module Toys
110
267
  # If the process is not set to run in the background, and a block is
111
268
  # provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
112
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
+ #
113
282
  # @param cmd [String,Array<String>] The command to execute.
114
- # @param opts [keywords] The command options. All options listed in the
115
- # {Toys::Utils::Exec} documentation are supported, plus the
116
- # `exit_on_nonzero_status` option.
283
+ # @param opts [keywords] The command options. See the section on
284
+ # Configuration Options in the {Toys::StandardMixins::Exec} module
285
+ # documentation.
117
286
  # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
118
- # the subprocess streams.
287
+ # the subprocess. See the section on Controlling Processes in the
288
+ # {Toys::StandardMixins::Exec} module documentation.
119
289
  #
120
290
  # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
121
291
  # the process is running in the background.
@@ -133,12 +303,19 @@ module Toys
133
303
  # If the process is not set to run in the background, and a block is
134
304
  # provided, a {Toys::Utils::Exec::Controller} will be yielded to it.
135
305
  #
306
+ # ## Example
307
+ #
308
+ # Execute a small script with warnings
309
+ #
310
+ # exec_ruby("-w", "-e", "(1..10).each { |i| puts i }")
311
+ #
136
312
  # @param args [String,Array<String>] The arguments to ruby.
137
- # @param opts [keywords] The command options. All options listed in the
138
- # {Toys::Utils::Exec} documentation are supported, plus the
139
- # `exit_on_nonzero_status` option.
313
+ # @param opts [keywords] The command options. See the section on
314
+ # Configuration Options in the {Toys::StandardMixins::Exec} module
315
+ # documentation.
140
316
  # @yieldparam controller [Toys::Utils::Exec::Controller] A controller for
141
- # for the subprocess streams.
317
+ # the subprocess. See the section on Controlling Processes in the
318
+ # {Toys::StandardMixins::Exec} module documentation.
142
319
  #
143
320
  # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
144
321
  # the process is running in the background.
@@ -160,12 +337,23 @@ module Toys
160
337
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
161
338
  # do not support this method because they do not support fork.
162
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
+ #
163
350
  # @param func [Proc] The proc to call.
164
- # @param opts [keywords] The command options. Most options listed in the
165
- # {Toys::Utils::Exec} documentation are supported, plus the
166
- # `exit_on_nonzero_status` option.
167
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
168
- # for the subprocess streams.
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.
169
357
  #
170
358
  # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
171
359
  # the process is running in the background.
@@ -189,12 +377,19 @@ module Toys
189
377
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
190
378
  # do not support this method because they do not support fork.
191
379
  #
380
+ # ## Example
381
+ #
382
+ # Run the "system update" tool and pass it an argument.
383
+ #
384
+ # exec_tool(["system", "update", "--verbose"])
385
+ #
192
386
  # @param cmd [String,Array<String>] The tool to execute.
193
- # @param opts [keywords] The command options. Most options listed in the
194
- # {Toys::Utils::Exec} documentation are supported, plus the
195
- # `exit_on_nonzero_status` option.
196
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
197
- # for the subprocess streams.
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.
198
393
  #
199
394
  # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
200
395
  # the process is running in the background.
@@ -229,12 +424,19 @@ module Toys
229
424
  # run a tool that uses a different bundle. It may also be necessary on
230
425
  # environments without "fork" (such as JRuby or Ruby on Windows).
231
426
  #
427
+ # ## Example
428
+ #
429
+ # Run the "system update" tool and pass it an argument.
430
+ #
431
+ # exec_separate_tool(["system", "update", "--verbose"])
432
+ #
232
433
  # @param cmd [String,Array<String>] The tool to execute.
233
- # @param opts [keywords] The command options. Most options listed in the
234
- # {Toys::Utils::Exec} documentation are supported, plus the
235
- # `exit_on_nonzero_status` option.
236
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
237
- # for the subprocess streams.
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.
238
440
  #
239
441
  # @return [Toys::Utils::Exec::Controller] The subprocess controller, if
240
442
  # the process is running in the background.
@@ -257,12 +459,20 @@ module Toys
257
459
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
258
460
  # yielded to it.
259
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
+ #
260
469
  # @param cmd [String,Array<String>] The command to execute.
261
- # @param opts [keywords] The command options. All options listed in the
262
- # {Toys::Utils::Exec} documentation are supported, plus the
263
- # `exit_on_nonzero_status` option.
264
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
265
- # for the subprocess streams.
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.
266
476
  #
267
477
  # @return [String] What was written to standard out.
268
478
  #
@@ -280,12 +490,20 @@ module Toys
280
490
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
281
491
  # yielded to it.
282
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
+ #
283
500
  # @param args [String,Array<String>] The arguments to ruby.
284
- # @param opts [keywords] The command options. All options listed in the
285
- # {Toys::Utils::Exec} documentation are supported, plus the
286
- # `exit_on_nonzero_status` option.
287
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
288
- # for the subprocess streams.
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.
289
507
  #
290
508
  # @return [String] What was written to standard out.
291
509
  #
@@ -306,12 +524,23 @@ module Toys
306
524
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
307
525
  # do not support this method because they do not support fork.
308
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
+ #
309
537
  # @param func [Proc] The proc to call.
310
- # @param opts [keywords] The command options. Most options listed in the
311
- # {Toys::Utils::Exec} documentation are supported, plus the
312
- # `exit_on_nonzero_status` option.
313
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
314
- # for the subprocess streams.
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.
315
544
  #
316
545
  # @return [String] What was written to standard out.
317
546
  #
@@ -335,12 +564,20 @@ module Toys
335
564
  # Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows)
336
565
  # do not support this method because they do not support fork.
337
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
+ #
338
574
  # @param cmd [String,Array<String>] The tool to execute.
339
- # @param opts [keywords] The command options. Most options listed in the
340
- # {Toys::Utils::Exec} documentation are supported, plus the
341
- # `exit_on_nonzero_status` option.
342
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
343
- # for the subprocess streams.
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.
344
581
  #
345
582
  # @return [String] What was written to standard out.
346
583
  #
@@ -375,12 +612,20 @@ module Toys
375
612
  # run a tool that uses a different bundle. It may also be necessary on
376
613
  # environments without "fork" (such as JRuby or Ruby on Windows).
377
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
+ #
378
622
  # @param cmd [String,Array<String>] The tool to execute.
379
- # @param opts [keywords] The command options. Most options listed in the
380
- # {Toys::Utils::Exec} documentation are supported, plus the
381
- # `exit_on_nonzero_status` option.
382
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
383
- # for the subprocess streams.
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.
384
629
  #
385
630
  # @return [String] What was written to standard out.
386
631
  #
@@ -397,12 +642,20 @@ module Toys
397
642
  # If a block is provided, a {Toys::Utils::Exec::Controller} will be
398
643
  # yielded to it.
399
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
+ #
400
652
  # @param cmd [String] The shell command to execute.
401
- # @param opts [keywords] The command options. All options listed in the
402
- # {Toys::Utils::Exec} documentation are supported, plus the
403
- # `exit_on_nonzero_status` option.
404
- # @yieldparam controller [Toys::Utils::Exec::Controller] A controller
405
- # for the subprocess streams.
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.
406
659
  #
407
660
  # @return [Integer] The exit code
408
661
  #
@@ -432,42 +685,50 @@ module Toys
432
685
 
433
686
  ## @private
434
687
  def self._setup_exec_opts(opts, context)
688
+ count = 0
689
+ result_callback = nil
435
690
  if opts.key?(:result_callback)
436
- opts = _setup_result_callback_option(opts, context)
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
437
700
  end
438
- if opts.key?(:exit_on_nonzero_status) || opts.key?(:e)
439
- opts = _setup_e_option(opts, context)
701
+ if count > 1
702
+ raise ::ArgumentError,
703
+ "You can provide at most one of: result_callback, exit_on_nonzero_status, e"
440
704
  end
705
+ opts = opts.merge(result_callback: result_callback) if count == 1
441
706
  opts
442
707
  end
443
708
 
444
709
  ## @private
445
- def self._setup_e_option(opts, context)
446
- e_options = [:exit_on_nonzero_status, :e]
447
- if e_options.any? { |k| opts[k] }
448
- result_callback = proc { |r| context.exit(r.exit_code) if r.error? }
449
- opts = opts.merge(result_callback: result_callback)
450
- end
451
- opts.reject { |k, _v| e_options.include?(k) }
710
+ def self._interpret_e(value, context)
711
+ value ? proc { |r| context.exit(r.exit_code) if r.error? } : nil
452
712
  end
453
713
 
454
714
  ## @private
455
- def self._setup_result_callback_option(opts, context)
456
- orig_callback = opts[:result_callback]
457
- result_callback =
458
- if orig_callback.is_a?(::Symbol)
459
- context.method(orig_callback)
460
- elsif orig_callback.respond_to?(:call)
461
- proc { |r| orig_callback.call(r, context) }
462
- end
463
- opts.merge(result_callback: result_callback)
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
464
725
  end
465
726
 
466
727
  ## @private
467
728
  def self._setup_clean_process(cmd)
468
729
  raise ::ArgumentError, "Toys process is unknown" unless ::Toys.executable_path
469
730
  cmd = ::Shellwords.split(cmd) if cmd.is_a?(::String)
470
- cmd = Array(::Toys.executable_path) + cmd
731
+ cmd = [::RbConfig.ruby, "--disable=gems", ::Toys.executable_path] + cmd
471
732
  if defined?(::Bundler)
472
733
  if ::Bundler.respond_to?(:with_unbundled_env)
473
734
  ::Bundler.with_unbundled_env { yield(cmd) }