toys 0.2.2 → 0.3.0
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 +5 -5
- data/CHANGELOG.md +5 -0
- data/README.md +8 -0
- data/lib/toys.rb +6 -8
- data/lib/toys/builder.rb +95 -30
- data/lib/toys/builtins/do.rb +44 -0
- data/lib/toys/builtins/system.rb +5 -5
- data/lib/toys/cli.rb +46 -18
- data/lib/toys/context.rb +109 -21
- data/lib/toys/helpers.rb +41 -0
- data/lib/toys/helpers/exec.rb +211 -201
- data/lib/toys/helpers/file_utils.rb +7 -5
- data/lib/toys/{lookup.rb → loader.rb} +29 -18
- data/lib/toys/middleware.rb +41 -0
- data/lib/toys/middleware/base.rb +45 -0
- data/lib/toys/middleware/group_default.rb +57 -0
- data/lib/toys/middleware/set_verbosity.rb +51 -0
- data/lib/toys/middleware/show_tool_help.rb +57 -0
- data/lib/toys/middleware/show_usage_errors.rb +51 -0
- data/lib/toys/templates.rb +41 -0
- data/lib/toys/templates/clean.rb +28 -26
- data/lib/toys/templates/gem_build.rb +51 -49
- data/lib/toys/templates/minitest.rb +48 -42
- data/lib/toys/templates/rubocop.rb +28 -26
- data/lib/toys/templates/yardoc.rb +39 -37
- data/lib/toys/tool.rb +200 -294
- data/lib/toys/utils/module_lookup.rb +101 -0
- data/lib/toys/utils/usage.rb +119 -0
- data/lib/toys/version.rb +1 -1
- metadata +20 -8
data/lib/toys/context.rb
CHANGED
@@ -34,31 +34,116 @@ module Toys
|
|
34
34
|
# The object context in effect during the execution of a tool.
|
35
35
|
#
|
36
36
|
class Context
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
##
|
38
|
+
# Context key for the verbosity value. Verbosity is an integer defaulting
|
39
|
+
# to 0, with higher values meaning more verbose and lower meaning quieter.
|
40
|
+
# @return [Symbol]
|
41
|
+
#
|
42
|
+
VERBOSITY = :__verbosity
|
43
|
+
|
44
|
+
##
|
45
|
+
# Context key for the `Toys::Tool` object being executed.
|
46
|
+
# @return [Symbol]
|
47
|
+
#
|
48
|
+
TOOL = :__tool
|
49
|
+
|
50
|
+
##
|
51
|
+
# Context key for the full name of the tool being executed. Value is an
|
52
|
+
# array of strings.
|
53
|
+
# @return [Symbol]
|
54
|
+
#
|
55
|
+
TOOL_NAME = :__tool_name
|
56
|
+
|
57
|
+
##
|
58
|
+
# Context key for the active `Toys::Loader` object.
|
59
|
+
# @return [Symbol]
|
60
|
+
#
|
61
|
+
LOADER = :__loader
|
62
|
+
|
63
|
+
##
|
64
|
+
# Context key for the active `Logger` object.
|
65
|
+
# @return [Symbol]
|
66
|
+
#
|
67
|
+
LOGGER = :__logger
|
68
|
+
|
69
|
+
##
|
70
|
+
# Context key for the name of the toys binary. Value is a string.
|
71
|
+
# @return [Symbol]
|
72
|
+
#
|
73
|
+
BINARY_NAME = :__binary_name
|
74
|
+
|
75
|
+
##
|
76
|
+
# Context key for the argument list passed to the current tool. Value is
|
77
|
+
# an array of strings.
|
78
|
+
# @return [Symbol]
|
79
|
+
#
|
80
|
+
ARGS = :__args
|
81
|
+
|
82
|
+
##
|
83
|
+
# Context key for the usage error raised. Value is a string if there was
|
84
|
+
# an error, or nil if there was no error.
|
85
|
+
# @return [Symbol]
|
86
|
+
#
|
87
|
+
USAGE_ERROR = :__usage_error
|
88
|
+
|
89
|
+
def initialize(context_base, data)
|
90
|
+
@_context_base = context_base
|
91
|
+
@_data = data
|
92
|
+
@_data[LOADER] = context_base.loader
|
93
|
+
@_data[BINARY_NAME] = context_base.binary_name
|
94
|
+
@_data[LOGGER] = context_base.logger
|
42
95
|
end
|
43
96
|
|
44
|
-
|
45
|
-
|
46
|
-
|
97
|
+
def verbosity
|
98
|
+
@_data[VERBOSITY]
|
99
|
+
end
|
47
100
|
|
48
|
-
def
|
49
|
-
@
|
101
|
+
def tool
|
102
|
+
@_data[TOOL]
|
103
|
+
end
|
104
|
+
|
105
|
+
def tool_name
|
106
|
+
@_data[TOOL_NAME]
|
107
|
+
end
|
108
|
+
|
109
|
+
def args
|
110
|
+
@_data[ARGS]
|
111
|
+
end
|
112
|
+
|
113
|
+
def usage_error
|
114
|
+
@_data[USAGE_ERROR]
|
50
115
|
end
|
51
116
|
|
52
117
|
def logger
|
53
|
-
@
|
118
|
+
@_data[LOGGER]
|
119
|
+
end
|
120
|
+
|
121
|
+
def loader
|
122
|
+
@_data[LOADER]
|
54
123
|
end
|
55
124
|
|
56
125
|
def binary_name
|
57
|
-
@
|
126
|
+
@_data[BINARY_NAME]
|
127
|
+
end
|
128
|
+
|
129
|
+
def [](key)
|
130
|
+
@_data[key]
|
131
|
+
end
|
132
|
+
|
133
|
+
def []=(key, value)
|
134
|
+
@_data[key] = value
|
135
|
+
end
|
136
|
+
|
137
|
+
def options
|
138
|
+
@_data.select do |k, _v|
|
139
|
+
!k.is_a?(::Symbol) || !k.to_s.start_with?("__")
|
140
|
+
end
|
58
141
|
end
|
59
142
|
|
60
|
-
def run(*args)
|
61
|
-
@
|
143
|
+
def run(*args, exit_on_nonzero_status: false)
|
144
|
+
code = @_context_base.run(args.flatten, verbosity: @_data[VERBOSITY])
|
145
|
+
exit(code) if exit_on_nonzero_status && !code.zero?
|
146
|
+
code
|
62
147
|
end
|
63
148
|
|
64
149
|
def exit(code)
|
@@ -70,21 +155,24 @@ module Toys
|
|
70
155
|
# @private
|
71
156
|
#
|
72
157
|
class Base
|
73
|
-
def initialize(
|
74
|
-
@
|
75
|
-
@binary_name = binary_name
|
158
|
+
def initialize(loader, binary_name, logger)
|
159
|
+
@loader = loader
|
160
|
+
@binary_name = binary_name || ::File.basename($PROGRAM_NAME)
|
76
161
|
@logger = logger || ::Logger.new(::STDERR)
|
162
|
+
@base_level = @logger.level
|
77
163
|
end
|
78
164
|
|
165
|
+
attr_reader :loader
|
79
166
|
attr_reader :binary_name
|
80
167
|
attr_reader :logger
|
168
|
+
attr_reader :base_level
|
81
169
|
|
82
|
-
def run(
|
83
|
-
@
|
170
|
+
def run(args, verbosity: 0)
|
171
|
+
@loader.execute(self, args, verbosity: verbosity)
|
84
172
|
end
|
85
173
|
|
86
|
-
def create_context(
|
87
|
-
Context.new(self,
|
174
|
+
def create_context(data)
|
175
|
+
Context.new(self, data)
|
88
176
|
end
|
89
177
|
end
|
90
178
|
end
|
data/lib/toys/helpers.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright 2018 Daniel Azuma
|
2
|
+
#
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
14
|
+
# contributors to this software, may be used to endorse or promote products
|
15
|
+
# derived from this software without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
18
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
21
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
22
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
23
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
24
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
25
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
26
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
;
|
29
|
+
|
30
|
+
require "toys/utils/module_lookup"
|
31
|
+
|
32
|
+
module Toys
|
33
|
+
##
|
34
|
+
# Namespace for common helper modules
|
35
|
+
#
|
36
|
+
module Helpers
|
37
|
+
def self.lookup(name)
|
38
|
+
Utils::ModuleLookup.lookup(:helpers, name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/toys/helpers/exec.rb
CHANGED
@@ -29,250 +29,260 @@
|
|
29
29
|
|
30
30
|
require "logger"
|
31
31
|
|
32
|
-
module Toys
|
33
|
-
|
34
|
-
# A set of helper methods for invoking subcommands
|
35
|
-
#
|
36
|
-
module Exec
|
37
|
-
def configure_exec(opts = {})
|
38
|
-
@exec_config ||= {}
|
39
|
-
@exec_config.merge!(opts)
|
40
|
-
end
|
41
|
-
|
42
|
-
def exec(cmd, opts = {}, &block)
|
43
|
-
exec_opts = ExecOpts.new(self)
|
44
|
-
exec_opts.add(@exec_config) if defined? @exec_config
|
45
|
-
exec_opts.add(opts)
|
46
|
-
executor = Executor.new(exec_opts, cmd)
|
47
|
-
executor.execute(&block)
|
48
|
-
end
|
49
|
-
|
50
|
-
def ruby(args, opts = {}, &block)
|
51
|
-
cmd =
|
52
|
-
if args.is_a?(Array)
|
53
|
-
[[Exec.ruby_binary, "ruby"]] + args
|
54
|
-
else
|
55
|
-
"#{Exec.ruby_binary} #{args}"
|
56
|
-
end
|
57
|
-
exec(cmd, opts, &block)
|
58
|
-
end
|
59
|
-
|
60
|
-
def sh(cmd, opts = {})
|
61
|
-
exec(cmd, opts).exit_code
|
62
|
-
end
|
63
|
-
|
64
|
-
def capture(cmd, opts = {})
|
65
|
-
exec(cmd, opts.merge(out_to: :capture)).captured_out
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.ruby_binary
|
69
|
-
::File.join(::RbConfig::CONFIG["bindir"], ::RbConfig::CONFIG["ruby_install_name"])
|
70
|
-
end
|
71
|
-
|
32
|
+
module Toys
|
33
|
+
module Helpers
|
72
34
|
##
|
73
|
-
#
|
35
|
+
# A set of helper methods for invoking subcommands
|
74
36
|
#
|
75
|
-
|
76
|
-
def
|
77
|
-
@
|
78
|
-
@
|
79
|
-
@err = err
|
80
|
-
@out_err = out_err
|
81
|
-
@pid = pid
|
37
|
+
module Exec
|
38
|
+
def configure_exec(opts = {})
|
39
|
+
@exec_config ||= {}
|
40
|
+
@exec_config.merge!(opts)
|
82
41
|
end
|
83
42
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
43
|
+
def exec(cmd, opts = {}, &block)
|
44
|
+
exec_opts = ExecOpts.new(self)
|
45
|
+
exec_opts.add(@exec_config) if defined? @exec_config
|
46
|
+
exec_opts.add(opts)
|
47
|
+
executor = Executor.new(exec_opts, cmd)
|
48
|
+
executor.execute(&block)
|
49
|
+
end
|
89
50
|
|
90
|
-
def
|
91
|
-
|
51
|
+
def ruby(args, opts = {}, &block)
|
52
|
+
cmd =
|
53
|
+
if args.is_a?(Array)
|
54
|
+
[[Exec.ruby_binary, "ruby"]] + args
|
55
|
+
else
|
56
|
+
"#{Exec.ruby_binary} #{args}"
|
57
|
+
end
|
58
|
+
exec(cmd, opts, &block)
|
92
59
|
end
|
93
|
-
end
|
94
60
|
|
95
|
-
|
96
|
-
|
97
|
-
#
|
98
|
-
class Result
|
99
|
-
def initialize(out, err, out_err, status)
|
100
|
-
@captured_out = out
|
101
|
-
@captured_err = err
|
102
|
-
@captured_out_err = out_err
|
103
|
-
@status = status
|
61
|
+
def sh(cmd, opts = {})
|
62
|
+
exec(cmd, opts).exit_code
|
104
63
|
end
|
105
64
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
attr_reader :status
|
65
|
+
def capture(cmd, opts = {})
|
66
|
+
exec(cmd, opts.merge(out_to: :capture)).captured_out
|
67
|
+
end
|
110
68
|
|
111
|
-
def
|
112
|
-
|
69
|
+
def self.ruby_binary
|
70
|
+
::File.join(::RbConfig::CONFIG["bindir"], ::RbConfig::CONFIG["ruby_install_name"])
|
113
71
|
end
|
114
|
-
end
|
115
72
|
|
116
|
-
##
|
117
|
-
# An internal helper class storing the configuration of a subcommand invocation
|
118
|
-
# @private
|
119
|
-
#
|
120
|
-
class ExecOpts
|
121
73
|
##
|
122
|
-
#
|
123
|
-
# @private
|
74
|
+
# The object passed to a subcommand control block
|
124
75
|
#
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
].freeze
|
76
|
+
class Controller
|
77
|
+
def initialize(ins, out, err, out_err, pid)
|
78
|
+
@in = ins
|
79
|
+
@out = out
|
80
|
+
@err = err
|
81
|
+
@out_err = out_err
|
82
|
+
@pid = pid
|
83
|
+
end
|
134
84
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
85
|
+
attr_reader :in
|
86
|
+
attr_reader :out
|
87
|
+
attr_reader :err
|
88
|
+
attr_reader :out_err
|
89
|
+
attr_reader :pid
|
140
90
|
|
141
|
-
|
142
|
-
|
143
|
-
if CONFIG_KEYS.include?(k)
|
144
|
-
@config[k] = v
|
145
|
-
else
|
146
|
-
@spawn_opts[k] = v
|
147
|
-
end
|
91
|
+
def kill(signal)
|
92
|
+
::Process.kill(signal, pid)
|
148
93
|
end
|
149
94
|
end
|
150
95
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
96
|
+
##
|
97
|
+
# The return result from a subcommand
|
98
|
+
#
|
99
|
+
class Result
|
100
|
+
def initialize(out, err, out_err, status)
|
101
|
+
@captured_out = out
|
102
|
+
@captured_err = err
|
103
|
+
@captured_out_err = out_err
|
104
|
+
@status = status
|
105
|
+
end
|
155
106
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
class Executor
|
161
|
-
def initialize(exec_opts, cmd)
|
162
|
-
@cmd = Array(cmd)
|
163
|
-
@config = exec_opts.config
|
164
|
-
@context = exec_opts.context
|
165
|
-
@spawn_opts = exec_opts.spawn_opts.dup
|
166
|
-
@captures = {}
|
167
|
-
@controller_streams = {}
|
168
|
-
@join_threads = []
|
169
|
-
@child_streams = []
|
170
|
-
end
|
107
|
+
attr_reader :captured_out
|
108
|
+
attr_reader :captured_err
|
109
|
+
attr_reader :captured_out_err
|
110
|
+
attr_reader :status
|
171
111
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
setup_out_stream(:err, :err_to, :err)
|
176
|
-
setup_out_stream(:out_err, :out_err_to, [:out, :err])
|
177
|
-
log_command
|
178
|
-
wait_thread = start_process
|
179
|
-
status = control_process(wait_thread, &block)
|
180
|
-
create_result(status)
|
181
|
-
end
|
112
|
+
def exit_code
|
113
|
+
status.exitstatus
|
114
|
+
end
|
182
115
|
|
183
|
-
|
116
|
+
def success?
|
117
|
+
exit_code.zero?
|
118
|
+
end
|
184
119
|
|
185
|
-
|
186
|
-
|
187
|
-
cmd_str = @cmd.size == 1 ? @cmd.first : @cmd.inspect
|
188
|
-
@context.logger.add(@config[:log_level] || ::Logger::INFO, cmd_str)
|
120
|
+
def error?
|
121
|
+
!exit_code.zero?
|
189
122
|
end
|
190
123
|
end
|
191
124
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
125
|
+
##
|
126
|
+
# An internal helper class storing the configuration of a subcommand invocation
|
127
|
+
# @private
|
128
|
+
#
|
129
|
+
class ExecOpts
|
130
|
+
##
|
131
|
+
# Option keys that belong to exec configuration rather than spawn
|
132
|
+
# @private
|
133
|
+
#
|
134
|
+
CONFIG_KEYS = %i[
|
135
|
+
exit_on_nonzero_status
|
136
|
+
env
|
137
|
+
log_level
|
138
|
+
in_from
|
139
|
+
out_to
|
140
|
+
err_to
|
141
|
+
out_err_to
|
142
|
+
].freeze
|
143
|
+
|
144
|
+
def initialize(context)
|
145
|
+
@context = context
|
146
|
+
@config = {}
|
147
|
+
@spawn_opts = {}
|
148
|
+
end
|
200
149
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
@
|
207
|
-
|
208
|
-
yield controller
|
150
|
+
def add(config)
|
151
|
+
config.each do |k, v|
|
152
|
+
if CONFIG_KEYS.include?(k)
|
153
|
+
@config[k] = v
|
154
|
+
else
|
155
|
+
@spawn_opts[k] = v
|
156
|
+
end
|
209
157
|
end
|
210
|
-
ensure
|
211
|
-
@controller_streams.each_value(&:close)
|
212
158
|
end
|
213
|
-
|
214
|
-
|
159
|
+
|
160
|
+
attr_reader :config
|
161
|
+
attr_reader :spawn_opts
|
162
|
+
attr_reader :context
|
215
163
|
end
|
216
164
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
165
|
+
##
|
166
|
+
# An object that manages the execution of a subcommand
|
167
|
+
# @private
|
168
|
+
#
|
169
|
+
class Executor
|
170
|
+
def initialize(exec_opts, cmd)
|
171
|
+
@cmd = Array(cmd)
|
172
|
+
@config = exec_opts.config
|
173
|
+
@context = exec_opts.context
|
174
|
+
@spawn_opts = exec_opts.spawn_opts.dup
|
175
|
+
@captures = {}
|
176
|
+
@controller_streams = {}
|
177
|
+
@join_threads = []
|
178
|
+
@child_streams = []
|
221
179
|
end
|
222
|
-
Result.new(@captures[:out], @captures[:err], @captures[:out_err], status)
|
223
|
-
end
|
224
180
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
@controller_streams[:in] = w
|
235
|
-
when String
|
236
|
-
write_string_thread(w, setting)
|
237
|
-
else
|
238
|
-
raise "Unknown type for in_from"
|
239
|
-
end
|
181
|
+
def execute(&block)
|
182
|
+
setup_in_stream
|
183
|
+
setup_out_stream(:out, :out_to, :out)
|
184
|
+
setup_out_stream(:err, :err_to, :err)
|
185
|
+
setup_out_stream(:out_err, :out_err_to, [:out, :err])
|
186
|
+
log_command
|
187
|
+
wait_thread = start_process
|
188
|
+
status = control_process(wait_thread, &block)
|
189
|
+
create_result(status)
|
240
190
|
end
|
241
|
-
end
|
242
191
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
case setting
|
250
|
-
when :controller
|
251
|
-
@controller_streams[stream_name] = r
|
252
|
-
when :capture
|
253
|
-
@join_threads << capture_stream_thread(r, stream_name)
|
254
|
-
else
|
255
|
-
raise "Unknown type for #{config_key}"
|
192
|
+
private
|
193
|
+
|
194
|
+
def log_command
|
195
|
+
unless @config[:log_level] == false
|
196
|
+
cmd_str = @cmd.size == 1 ? @cmd.first : @cmd.inspect
|
197
|
+
@context.logger.add(@config[:log_level] || ::Logger::INFO, cmd_str)
|
256
198
|
end
|
257
199
|
end
|
258
|
-
end
|
259
200
|
|
260
|
-
|
261
|
-
|
201
|
+
def start_process
|
202
|
+
args = []
|
203
|
+
args << @config[:env] if @config[:env]
|
204
|
+
args.concat(@cmd)
|
205
|
+
pid = ::Process.spawn(*args, @spawn_opts)
|
206
|
+
@child_streams.each(&:close)
|
207
|
+
::Process.detach(pid)
|
208
|
+
end
|
209
|
+
|
210
|
+
def control_process(wait_thread)
|
262
211
|
begin
|
263
|
-
|
212
|
+
if block_given?
|
213
|
+
controller = Controller.new(
|
214
|
+
@controller_streams[:in], @controller_streams[:out], @controller_streams[:err],
|
215
|
+
@controller_streams[:out_err], wait_thread.pid
|
216
|
+
)
|
217
|
+
yield controller
|
218
|
+
end
|
264
219
|
ensure
|
265
|
-
|
220
|
+
@controller_streams.each_value(&:close)
|
266
221
|
end
|
222
|
+
@join_threads.each(&:join)
|
223
|
+
wait_thread.value
|
267
224
|
end
|
268
|
-
end
|
269
225
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
@
|
274
|
-
|
275
|
-
|
226
|
+
def create_result(status)
|
227
|
+
if @config[:exit_on_nonzero_status]
|
228
|
+
exit_status = status.exitstatus
|
229
|
+
@context.exit(exit_status) if exit_status != 0
|
230
|
+
end
|
231
|
+
Result.new(@captures[:out], @captures[:err], @captures[:out_err], status)
|
232
|
+
end
|
233
|
+
|
234
|
+
def setup_in_stream
|
235
|
+
setting = @config[:in_from]
|
236
|
+
if setting
|
237
|
+
r, w = ::IO.pipe
|
238
|
+
@spawn_opts[:in] = r
|
239
|
+
w.sync = true
|
240
|
+
@child_streams << r
|
241
|
+
case setting
|
242
|
+
when :controller
|
243
|
+
@controller_streams[:in] = w
|
244
|
+
when String
|
245
|
+
write_string_thread(w, setting)
|
246
|
+
else
|
247
|
+
raise "Unknown type for in_from"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def setup_out_stream(stream_name, config_key, spawn_key)
|
253
|
+
setting = @config[config_key]
|
254
|
+
if setting
|
255
|
+
r, w = ::IO.pipe
|
256
|
+
@spawn_opts[spawn_key] = w
|
257
|
+
@child_streams << w
|
258
|
+
case setting
|
259
|
+
when :controller
|
260
|
+
@controller_streams[stream_name] = r
|
261
|
+
when :capture
|
262
|
+
@join_threads << capture_stream_thread(r, stream_name)
|
263
|
+
else
|
264
|
+
raise "Unknown type for #{config_key}"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def write_string_thread(stream, string)
|
270
|
+
::Thread.new do
|
271
|
+
begin
|
272
|
+
stream.write string
|
273
|
+
ensure
|
274
|
+
stream.close
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def capture_stream_thread(stream, name)
|
280
|
+
::Thread.new do
|
281
|
+
begin
|
282
|
+
@captures[name] = stream.read
|
283
|
+
ensure
|
284
|
+
stream.close
|
285
|
+
end
|
276
286
|
end
|
277
287
|
end
|
278
288
|
end
|