Skip to content

Commit 62e250b

Browse files
committed
refactor: Process.run return Result Type now
- Use output: error: instead of out: err: - MultiIO not add STDOUT twice
1 parent 518b75d commit 62e250b

File tree

3 files changed

+89
-19
lines changed

3 files changed

+89
-19
lines changed

.rubocop.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,14 @@ Metrics/BlockLength:
1010
Metrics/AbcSize:
1111
Enabled: false
1212

13+
Metrics/CyclomaticComplexity:
14+
Enabled: false
15+
16+
Metrics/PerceivedComplexity:
17+
Enabled: false
18+
1319
Style/ConditionalAssignment:
1420
Enabled: false
21+
22+
Style/GlobalStdStream:
23+
Enabled: false

lib/rb/process.rb

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,75 @@
11
# frozen_string_literal: true
22

33
require "multi_io"
4+
require "stringio"
45
require_relative "process/version"
56

67
module Process
7-
def self.run(*args, log_file: nil, **options)
8-
if log_file && !log_file.is_a?(File)
9-
raise ArgumentError.new("log_file must be a File with mode")
10-
end
8+
def self.run(*args, output: STDOUT, error: STDERR, **options)
9+
output_strio = StringIO.new
10+
error_strio = StringIO.new
1111

12-
mio = log_file ? MultiIO.new($stdout, log_file) : $stdout
13-
result = String.new
12+
output_writter = if !output.is_a?(IO)
13+
output
14+
elsif output != STDOUT
15+
MultiIO.new(STDOUT, output, output_strio)
16+
else
17+
MultiIO.new(STDOUT, output_strio)
18+
end
19+
error_writter = if !error.is_a?(IO)
20+
error
21+
elsif error != STDERR
22+
MultiIO.new(STDERR, error, error_strio)
23+
else
24+
MultiIO.new(STDERR, error_strio)
25+
end
1426

15-
IO.popen(*args, **options) do |pipe|
16-
if block_given?
17-
yield pipe
18-
pipe.close_write
19-
end
20-
while !pipe.eof
21-
line = pipe.gets
22-
mio.write(line)
23-
result << line
27+
# spawn don't support MultiIO nor StringIO
28+
in_r, in_w = IO.pipe
29+
out_r, out_w = IO.pipe
30+
err_r, err_w = IO.pipe
31+
32+
# override the options, so put the option after the options
33+
pid = spawn(*args, **options, in: in_r, out: out_w, err: err_w)
34+
35+
if block_given?
36+
begin
37+
yield in_w, out_r, err_r
38+
rescue StandardError => e
39+
Process.detach(pid)
40+
raise e
2441
end
2542
end
2643

27-
log_file.close if log_file
28-
result
44+
in_w.close
45+
out_w.close
46+
err_w.close
47+
48+
out_r.each_line do |line|
49+
output_writter.write(line)
50+
end
51+
52+
err_r.each_line do |line|
53+
error_writter.write(line)
54+
end
55+
56+
in_r.close
57+
out_r.close
58+
err_r.close
59+
60+
pid, status = Process.wait2(pid)
61+
62+
output.close unless !output.is_a?(IO) || output == STDOUT
63+
error.close unless !error.is_a?(IO) || error == STDERR
64+
output_strio.close
65+
error_strio.close
66+
67+
case status.success?
68+
when true
69+
output_strio.string
70+
else
71+
[output_strio.string, error_strio.string, status]
72+
end
2973
end
3074

3175
def self.output(...)

spec/rb/process_spec.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@
1515
expect(Process.run("uname")).to eq "Linux\n"
1616
end
1717

18+
it "print once Linux\n" do
19+
expect(Process.run("uname", output: STDOUT)).to eq "Linux\n"
20+
end
21+
22+
it "looks like ruby Open3 when bad" do
23+
r = Process.run("echo good && echo bad >&2 && exit 1")
24+
case r
25+
when String
26+
expect(r.chomp).to eq "good"
27+
else
28+
o, e, s = r
29+
expect(o.chomp).to eq "good"
30+
expect(e.chomp).to eq "bad"
31+
expect(s.exitstatus).to eq 1
32+
end
33+
end
34+
1835
it "get the output and not print" do
1936
expect(Process.output("uname").chomp).to eq "Linux"
2037
end
@@ -28,12 +45,12 @@
2845
end
2946

3047
it "answer with cmd with ruby style" do
31-
expect(Process.run("bash", "r+") { |pipe| pipe.puts "uname" }).to eq "Linux\n"
48+
expect(Process.run("bash") { |i, _o, _e| i.puts "uname" }).to eq "Linux\n"
3249
end
3350

3451
it "print and also log to file" do
3552
tempfile = Tempfile.new(["test_", ".log"])
36-
Process.run("uname", log_file: File.open(tempfile.path, "w"))
53+
Process.run("uname", output: File.open(tempfile.path, "w"))
3754

3855
expect(tempfile.readlines).to eq ["Linux\n"]
3956
tempfile.delete

0 commit comments

Comments
 (0)