toys-core 0.10.0 → 0.10.5

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