Skip to content

Commit 1a8a82d

Browse files
committed
feat: send method to writer or reader by define method_missing
- Process::Result do so
1 parent ecba8e0 commit 1a8a82d

4 files changed

Lines changed: 77 additions & 49 deletions

File tree

lib/rb/io/multi_writer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class MultiWriter
1515
# If `#sync_close?` is `true`, closing this `IO` will close all of the underlying
1616
# IOs.
1717
attr_accessor :sync_close
18+
attr_accessor :writers
1819
attr_reader :closed
1920

2021
@closed = false

lib/rb/io/stapled.rb

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class IO
1515
class Stapled
1616
# If `#sync_close?` is `true`, closing this `IO` will close the underlying `IO`s.
1717
attr_accessor :sync_close
18+
attr_reader :reader
19+
attr_reader :writer
1820

1921
# Returns `true` if this `IO` is closed.
2022
#
@@ -23,6 +25,15 @@ class Stapled
2325

2426
@closed = false
2527

28+
WRITER_DELEGATE = %w[
29+
<<
30+
].freeze
31+
32+
READER_DELEGATE = %w[
33+
eof
34+
eof?
35+
].freeze
36+
2637
alias_method :sync_close?, :sync_close
2738
alias_method :closed?, :closed
2839

@@ -33,53 +44,30 @@ def initialize(reader, writer, sync_close: false)
3344
@sync_close = sync_close
3445
end
3546

36-
def read(...)
37-
check_open
38-
39-
@reader.read(...)
40-
end
41-
42-
def readlines(...)
43-
check_open
44-
45-
@reader.readlines(...)
46-
end
47-
48-
def each_line(...)
49-
check_open
50-
51-
@reader.each_line(...)
47+
def method_missing(name, *args, &block)
48+
if write_methods?(name.to_s)
49+
check_open
50+
@writer.send(name, *args, &block)
51+
elsif read_methods?(name.to_s)
52+
check_open
53+
@reader.send(name, *args, &block)
54+
else
55+
super
56+
end
5257
end
5358

54-
# Gets a string from `reader`.
55-
def gets(...)
56-
check_open
57-
58-
@reader.gets(...)
59+
def respond_to_missing?(name, include_private = false)
60+
super || write_methods?(name.to_s) || read_methods?(name.to_s)
5961
end
6062

61-
def puts(...)
62-
check_open
63-
64-
@writer.puts(...)
63+
def write_methods?(name)
64+
name.include?("put") || name.include?("prin") ||
65+
name.include?("write") || WRITER_DELEGATE.include?(name)
6566
end
6667

67-
def print(...)
68-
check_open
69-
70-
@writer.print(...)
71-
end
72-
73-
def printf(...)
74-
check_open
75-
76-
@writer.print(...)
77-
end
78-
79-
def write(...)
80-
check_open
81-
82-
@writer.write(...)
68+
def read_methods?(name)
69+
name.include?("get") || name.include?("read") ||
70+
name.include?("each") || READER_DELEGATE.include?(name)
8371
end
8472

8573
# Flushes `writer`.
@@ -91,6 +79,14 @@ def flush
9179
self
9280
end
9381

82+
def close_write
83+
@writer.close
84+
end
85+
86+
def close_read
87+
@reader.close
88+
end
89+
9490
# Closes this `IO`.
9591
#
9692
# If `sync_close?` is `true`, it will also close the underlying `IO`s.
@@ -105,14 +101,6 @@ def close
105101
end
106102
end
107103

108-
def close_write
109-
@writer.close
110-
end
111-
112-
def close_read
113-
@reader.close
114-
end
115-
116104
protected def check_open
117105
raise IOError.new("Closed stream") if closed?
118106
end

lib/rb/process.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ def initialize(stdout, stderr, status)
1616
@status = status
1717
end
1818

19+
def method_missing(name, *args, &block)
20+
@status.send(name, *args, &block)
21+
end
22+
23+
def respond_to_missing?(name, include_private = false)
24+
super || @status.respond_to?(name, include_private)
25+
end
26+
27+
def exit_code
28+
status.exitstatus
29+
end
30+
1931
def success?
2032
status.success?
2133
end

spec/rb/process_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
expect(r.success?).to eq true
1717
expect(r.ok?).to eq true
1818
expect(r.stdout).to eq "Linux\n"
19+
expect(r.exit_code).to eq 0
20+
expect(r.pid).to eq r.status.pid
1921
end
2022

2123
it "get the output and not print" do
@@ -43,4 +45,29 @@
4345
expect(tempfile.readlines).to eq ["Linux\n"]
4446
tempfile.delete
4547
end
48+
49+
# https://devdocs.io/ruby~3.4/io#class-IO-label-Reading
50+
it "responds to methods" do
51+
Process.run("bash", out: File.open(File::NULL, "r+")) do |pipe|
52+
pipe.write_nonblock("echo")
53+
pipe << " "
54+
pipe.write("hello ")
55+
pipe.print("w")
56+
pipe.printf("%s", "o")
57+
pipe.putc "r"
58+
pipe.write("l")
59+
pipe.puts "d"
60+
pipe.close_write
61+
62+
pipe.getbyte
63+
pipe.getc
64+
pipe.readbyte
65+
pipe.readchar
66+
pipe.readpartial(1)
67+
pipe.readline
68+
pipe.readlines
69+
pipe.gets
70+
pipe.read
71+
end
72+
end
4673
end

0 commit comments

Comments
 (0)