progeny 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e16d4fb0220a00c09eba23fc3b5f43cf72facb831a8d70f8afb2e4c48507b964
4
- data.tar.gz: 6f852ef9c5fb2f0dafd6a153cc9045b5d37021f189e7da18d2ec137b7d9d3338
3
+ metadata.gz: 5b4b80a4ad000727e13f154cef131b4299d27eed0ed4f200586defa86c60d829
4
+ data.tar.gz: 31a7c9a885fc3ada1ff8a66ae85a7400b56a0bcb17c1dca0ee9649eac56d7919
5
5
  SHA512:
6
- metadata.gz: 2f808a8bcdae021ab23ef31ad18df2c3d39b359f9b3752bc5924e143a00757359e64ff0586c617b1e8908cec1bb5e0f9b1076834edce27595e1733f76ddcdbc6
7
- data.tar.gz: 97bbb478f5c1ad1fabd55491784da44562d28d909be6f9c67586643f3e740fc1189e298cb2ea05a3ae46b0726a299f2d1cdba0307e19ffec31c83e3a41e7d049
6
+ metadata.gz: 15a7df8729f641d566a5b69d388592cbe536177c165c4260954a8a23d57c735d3cb4af242ca958f85de79198b33ffecaaba4551d514e2674720f409f433e930b
7
+ data.tar.gz: 27049a985faef5bd21a349feaece2480b70eba62b3ed253caf941e48e4af841f79797538e5ffaef78518da90ba13cd96a0db439e9d59b1363ba3cce254e7f991
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-05-05
4
+
5
+ ### Added
6
+
7
+ - `Progeny::Command#spawn_with_pipes` for easier migration from `posix-spawn`.
8
+
3
9
  ## [0.1.0] - 2023-05-03
4
10
 
5
11
  - Initial release. Forked `posix-spawn` gem.
data/README.md CHANGED
@@ -12,10 +12,11 @@ strings and is therefore not well-suited to streaming large quantities of data
12
12
  in and out of commands. That said, it has some benefits:
13
13
 
14
14
  - **Simple** - requires little code for simple stream input and capture.
15
- - **Internally non-blocking** (using `select(2)`) - handles all pipe hang cases
16
- due to exceeding `PIPE_BUF` limits on one or more streams.
17
- - **Uses Ruby under the hood** - It leverages Ruby's `Open3#popen3` and `Process.spawn`
18
- behind the scenes so it's widely supported and consistently getting performance updates.
15
+ - **Internally non-blocking** (using `select(2)`) - handles all pipe hang
16
+ cases due to exceeding `PIPE_BUF` limits on one or more streams.
17
+ - **Uses Ruby under the hood** - It leverages Ruby's `Process.spawn` behind
18
+ the scenes so it's widely supported and consistently getting performance
19
+ updates.
19
20
 
20
21
  `Progeny::Command` takes the [standard `Process::spawn`
21
22
  arguments](https://ruby-doc.org/current/Process.html#method-c-spawn) when
@@ -132,10 +133,9 @@ end
132
133
  ```
133
134
 
134
135
  You will need to remove the include statements and replace any use of `#spawn`
135
- with Ruby's native `Process.spawn` and `#popen4` with Open3's `#popen3`.
136
+ with Ruby's native `Process.spawn` and `#popen4` with `Progeny::Command.spawn_with_pipes`
136
137
  ```diff
137
138
  - require 'posix/spawn'
138
- + require 'open3'
139
139
 
140
140
  class YourSpawnerClass
141
141
  - include POSIX::Spawn
@@ -148,16 +148,14 @@ class YourSpawnerClass
148
148
 
149
149
  def calculate(expression)
150
150
  - pid, in, out, err = popen4('bc')
151
- + in, out, err, wait_thr = Open3.popen3('bc')
152
- + pid = wait_thr[:pid] # if pid is needed
151
+ + pid, in, out, err = Progeny::Command.spawn_with_pipes('bc')
153
152
  in.write(expression)
154
153
  in.close
155
154
  out.read
156
155
  ensure
157
156
  [in, out, err].each { |io| io.close if !io.closed? }
158
- - Process::waitpid(pid)
159
- - $?
160
- + wait_thr.value
157
+ Process::waitpid(pid)
158
+ $?
161
159
  end
162
160
  end
163
161
  ```
@@ -1,5 +1,3 @@
1
- require "open3"
2
-
3
1
  module Progeny
4
2
  # Progeny::Command includes logic for executing child processes and
5
3
  # reading/writing from their standard input, output, and error streams. It's
@@ -123,6 +121,41 @@ module Progeny
123
121
  new(*(args + [{ :noexec => true }.merge(options)]))
124
122
  end
125
123
 
124
+ # Spawn a child process with all standard IO streams piped in and out of
125
+ # the spawning process. Supports the standard `Process.spawn` interface.
126
+ #
127
+ # Returns a [pid, stdin, stdout, stderr] tuple, where pid is the new
128
+ # process's pid, stdin is a writeable IO object, and stdout / stderr are
129
+ # readable IO objects. The caller should take care to close all IO objects
130
+ # when finished and the child process's status must be collected by a call
131
+ # to Process::waitpid or equivalent.
132
+ def self.spawn_with_pipes(*argv)
133
+ if argv.last.is_a?(Hash)
134
+ opts = argv.pop.dup
135
+ else
136
+ opts = {}
137
+ end
138
+
139
+ ird, iwr = IO.pipe
140
+ ord, owr = IO.pipe
141
+ erd, ewr = IO.pipe
142
+
143
+ opts = opts.merge(
144
+ # redirect fds # close other sides
145
+ :in => ird, iwr => :close,
146
+ :out => owr, ord => :close,
147
+ :err => ewr, erd => :close
148
+ )
149
+ pid = spawn(*(argv + [opts]))
150
+ [pid, iwr, ord, erd]
151
+ ensure
152
+ # we're in the parent, close child-side fds
153
+ [ird, owr, ewr].each { |fd| fd.close if fd }
154
+ end
155
+
156
+ # All data written to the child process's stdin stream as a String.
157
+ attr_reader :input
158
+
126
159
  # All data written to the child process's stdout stream as a String.
127
160
  attr_reader :out
128
161
 
@@ -149,14 +182,14 @@ module Progeny
149
182
  # immediately when a new instance of this object is created, or
150
183
  # can be called explicitly when creating the Command via `build`.
151
184
  def exec!
152
- stdin, stdout, stderr, wait_thread = Open3.popen3(@env, *(@argv + [@options]))
153
- @pid = wait_thread[:pid]
185
+ pid, stdin, stdout, stderr = self.class.spawn_with_pipes(@env, *@argv, @options)
186
+ @pid = pid
154
187
 
155
188
  # async read from all streams into buffers
156
189
  read_and_write(@input, stdin, stdout, stderr, @timeout, @max)
157
190
 
158
- # wait_thr.value waits for the termination of the process and returns exit status
159
- @status = wait_thread.value
191
+ # wait for the termination of the process and return exit status
192
+ @status = waitpid(pid)
160
193
  rescue Object
161
194
  [stdin, stdout, stderr].each { |fd| fd.close rescue nil }
162
195
  if @status.nil?
@@ -165,7 +198,7 @@ module Progeny
165
198
  else
166
199
  ::Process.kill('-TERM', pid) rescue nil
167
200
  end
168
- @status = wait_thread.value rescue nil
201
+ @status = waitpid(pid) rescue nil
169
202
  end
170
203
  raise
171
204
  ensure
@@ -268,5 +301,13 @@ module Progeny
268
301
 
269
302
  [@out, @err]
270
303
  end
304
+
305
+ # Wait for the child process to exit
306
+ #
307
+ # Returns the Process::Status object obtained by reaping the process.
308
+ def waitpid(pid)
309
+ Process::waitpid(pid)
310
+ $?
311
+ end
271
312
  end
272
313
  end
@@ -1,3 +1,3 @@
1
1
  module Progeny
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: progeny
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luan Vieira
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-03 00:00:00.000000000 Z
11
+ date: 2023-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -77,5 +77,5 @@ requirements: []
77
77
  rubygems_version: 3.4.10
78
78
  signing_key:
79
79
  specification_version: 4
80
- summary: A popen3 wrapper with a nice interface and extra options.
80
+ summary: A process spawn wrapper with a nice interface and extra options.
81
81
  test_files: []