Module: TTY::Command::ChildProcess
- Defined in:
- lib/tty/command/child_process.rb
Class Method Summary collapse
-
.close_fds(*fds) ⇒ Object
private
Close all streams.
-
.convert(spawn_key, spawn_value) ⇒ Object
private
Convert option pari to recognized spawn option pair.
-
.convert_to_fd(object) ⇒ Object
private
Convert file name to file handle.
-
.fd?(object) ⇒ Boolean
private
Determine if object is a fd.
-
.fd_to_process_key(object) ⇒ Object
private
Convert fd to name :in, :out, :err.
-
.normalize_redirect_options(options) ⇒ Hash
private
Normalize spawn fd into :in, :out, :err keys.
-
.spawn(cmd) ⇒ pid, ...
Execute command in a child process with all IO streams piped in and out.
-
.try_loading_pty(verbose = false) ⇒ Boolean
private
Try loading pty module.
-
.try_reading(object) ⇒ Object
private
Attempts to read object content.
Class Method Details
.close_fds(*fds) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Close all streams
88 89 90 |
# File 'lib/tty/command/child_process.rb', line 88 def close_fds(*fds) fds.each { |fd| fd && !fd.closed? && fd.close } end |
.convert(spawn_key, spawn_value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert option pari to recognized spawn option pair
131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/tty/command/child_process.rb', line 131 def convert(spawn_key, spawn_value) key = fd_to_process_key(spawn_key) value = spawn_value if key.to_s == "in" value = convert_to_fd(spawn_value) end if fd?(spawn_value) value = fd_to_process_key(spawn_value) value = [:child, value] # redirect in child process end [key, value] end |
.convert_to_fd(object) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert file name to file handle
191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/tty/command/child_process.rb', line 191 def convert_to_fd(object) return object if fd?(object) if object.is_a?(::String) && ::File.exist?(object) return object end tmp = ::Tempfile.new(::SecureRandom.uuid.split("-")[0]) content = try_reading(object) tmp.write(content) tmp.rewind tmp end |
.fd?(object) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Determine if object is a fd
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/tty/command/child_process.rb', line 152 def fd?(object) case object when :stdin, :stdout, :stderr, :in, :out, :err, STDIN, STDOUT, STDERR, $stdin, $stdout, $stderr, ::IO true when ::Integer object >= 0 else respond_to?(:to_i) && !object.to_io.nil? end end |
.fd_to_process_key(object) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convert fd to name :in, :out, :err
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/tty/command/child_process.rb', line 168 def fd_to_process_key(object) case object when STDIN, $stdin, :in, :stdin, 0 :in when STDOUT, $stdout, :out, :stdout, 1 :out when STDERR, $stderr, :err, :stderr, 2 :err when Integer object >= 0 ? IO.for_fd(object) : nil when IO object when respond_to?(:to_io) object.to_io else raise ExecuteError, "Wrong execute redirect: #{object.inspect}" end end |
.normalize_redirect_options(options) ⇒ Hash
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Normalize spawn fd into :in, :out, :err keys.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/tty/command/child_process.rb', line 112 def () .reduce({}) do |opts, (key, value)| if fd?(key) spawn_key, spawn_value = convert(key, value) opts[spawn_key] = spawn_value elsif key.is_a?(Array) && key.all?(&method(:fd?)) key.each do |k| spawn_key, spawn_value = convert(k, value) opts[spawn_key] = spawn_value end end opts end end |
.spawn(cmd) ⇒ pid, ...
Execute command in a child process with all IO streams piped in and out. The interface is similar to Process.spawn
The caller should ensure that all IO objects are closed when the child process is finished. However, when block is provided this will be taken care of automatically.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/tty/command/child_process.rb', line 23 def spawn(cmd) process_opts = (cmd.) binmode = cmd.[:binmode] || false pty = cmd.[:pty] || false verbose = cmd.[:verbose] pty = try_loading_pty(verbose) if pty require("pty") if pty # load within this scope # Create pipes in_rd, in_wr = pty ? PTY.open : IO.pipe("utf-8") # reading out_rd, out_wr = pty ? PTY.open : IO.pipe("utf-8") # writing err_rd, err_wr = pty ? PTY.open : IO.pipe("utf-8") # error in_wr.sync = true if binmode in_wr.binmode out_rd.binmode err_rd.binmode end if pty in_wr.raw! out_wr.raw! err_wr.raw! end # redirect fds opts = { in: in_rd, out: out_wr, err: err_wr } unless TTY::Command.windows? close_child_fds = { in_wr => :close, out_rd => :close, err_rd => :close } opts.merge!(close_child_fds) end opts.merge!(process_opts) pid = Process.spawn(cmd.to_command, opts) # close streams in parent process talking to the child close_fds(in_rd, out_wr, err_wr) tuple = [pid, in_wr, out_rd, err_rd] if block_given? begin return yield(*tuple) ensure # ensure parent pipes are closed close_fds(in_wr, out_rd, err_rd) end else tuple end end |
.try_loading_pty(verbose = false) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Try loading pty module
98 99 100 101 102 103 104 |
# File 'lib/tty/command/child_process.rb', line 98 def try_loading_pty(verbose = false) require 'pty' true rescue LoadError warn("Requested PTY device but the system doesn't support it.") if verbose false end |
.try_reading(object) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Attempts to read object content
209 210 211 212 213 214 215 216 217 |
# File 'lib/tty/command/child_process.rb', line 209 def try_reading(object) if object.respond_to?(:read) object.read elsif object.respond_to?(:to_s) object.to_s else object end end |