cmds 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cmds/stream.rb DELETED
@@ -1,239 +0,0 @@
1
- class Cmds
2
- # stream inputs and/or outputs
3
- #
4
- # originally inspired by
5
- #
6
- # https://nickcharlton.net/posts/ruby-subprocesses-with-stdout-stderr-streams.html
7
- #
8
- # with major modifications from looking at Ruby's open3 module.
9
- #
10
- def stream *subs, &input_block
11
- Cmds.debug "entering Cmds#stream",
12
- subs: subs,
13
- input_block: input_block
14
-
15
- # use `merge_options` to get the args and kwds (we will take custom
16
- # care of input in _stream)
17
- options = merge_options subs, nil
18
-
19
- # build the command string
20
- cmd = Cmds.sub @template, options[:args], options[:kwds]
21
-
22
- # call the internal function
23
- really_stream cmd, options, &input_block
24
- end
25
-
26
- private
27
-
28
- # do the actual work...
29
- def really_stream cmd, options, &input_block
30
- Cmds.debug "entering Cmds#really_stream",
31
- cmd: cmd,
32
- options: options,
33
- input_block: input_block
34
-
35
- # create the handler that will be yielded to the input block
36
- handler = IOHandler.new
37
-
38
- # handle input
39
-
40
- # default to the instance variable
41
- input = @input
42
-
43
- # if a block was provided, it might provide overriding input
44
- if input_block
45
- case input_block.arity
46
- when 0
47
- # when the input block takes no arguments it returns the input
48
- input = input_block.call
49
- when 1
50
- # when the input block takes one argument, give it the handler and
51
- # ignore the return value
52
- input_block.call handler
53
-
54
- # if input was assigned to the handler in the block, use it as input
55
- input = handler.in unless handler.in.nil?
56
- else
57
- # bad block provided
58
- raise ArgumentError.new NRSER.squish <<-BLOCK
59
- provided input block must have arity 0 or 1
60
- BLOCK
61
- end # case input.arity
62
- end # if input_block
63
-
64
- # hash of options that will be passed to `spawn`
65
- spawn_opts = {}
66
-
67
- Cmds.debug "looking at input...",
68
- input: input
69
-
70
- # (possibly) create the input pipe... this will be nil if the provided
71
- # input is io-like. in this case it will be used directly in the
72
- # `spawn` options.
73
- in_pipe = case input
74
- when nil, String
75
- Cmds.debug "input is a String or nil, creating pipe..."
76
-
77
- in_pipe = Cmds::Pipe.new "INPUT", :in
78
- spawn_opts[:in] = in_pipe.r
79
-
80
- # don't buffer input
81
- in_pipe.w.sync = true
82
- in_pipe
83
-
84
- else
85
- Cmds.debug "input should be io-like, setting spawn opt.",
86
- input: input
87
- if input == $stdin
88
- Cmds.debug "input is $stdin."
89
- end
90
- spawn_opts[:in] = input
91
- nil
92
-
93
- end # case input
94
-
95
- # (possibly) create the output pipes.
96
- #
97
- # `stream` can be told to send it's output to either:
98
- #
99
- # 1. a Proc that will invoked with each line.
100
- # 2. an io-like object that can be provided as `spawn`'s `:out` or
101
- # `:err` options.
102
- #
103
- # in case (1) a `Cmds::Pipe` wrapping read and write piped `IO` instances
104
- # will be created and assigned to the relevant of `out_pipe` or `err_pipe`.
105
- #
106
- # in case (2) the io-like object will be sent directly to `spawn` and
107
- # the relevant `out_pipe` or `err_pipe` will be `nil`.
108
- #
109
- out_pipe, err_pipe = [
110
- ["ERROR", :err],
111
- ["OUTPUT", :out],
112
- ].map do |name, sym|
113
- Cmds.debug "looking at #{ name }..."
114
- # see if hanlder.out or hanlder.err is a Proc
115
- if handler.send(sym).is_a? Proc
116
- Cmds.debug "#{ name } is a Proc, creating pipe..."
117
- pipe = Cmds::Pipe.new name, sym
118
- # the corresponding :out or :err option for spawn needs to be
119
- # the pipe's write handle
120
- spawn_opts[sym] = pipe.w
121
- # return the pipe
122
- pipe
123
-
124
- else
125
- Cmds.debug "#{ name } should be io-like, setting spawn opt.",
126
- output: handler.send(sym)
127
- spawn_opts[sym] = handler.send(sym)
128
- # the pipe is nil!
129
- nil
130
- end
131
- end # map outputs
132
-
133
- Cmds.debug "spawning...",
134
- cmd: cmd,
135
- opts: spawn_opts
136
-
137
- pid = spawn cmd, spawn_opts
138
-
139
- Cmds.debug "spawned.",
140
- pid: pid
141
-
142
- wait_thread = Process.detach pid
143
- wait_thread[:name] = "WAIT"
144
-
145
- Cmds.debug "wait thread created.",
146
- thread: wait_thread
147
-
148
- # close child ios if created
149
- # the spawned process will read from in_pipe.r so we don't need it
150
- in_pipe.r.close if in_pipe
151
- # and we don't need to write to the output pipes, that will also happen
152
- # in the spawned process
153
- [out_pipe, err_pipe].each {|pipe| pipe.w.close if pipe}
154
-
155
- # create threads to handle any pipes that were created
156
-
157
- in_thread = if in_pipe
158
- Thread.new do
159
- Thread.current[:name] = in_pipe.name
160
- Cmds.debug "thread started, writing input..."
161
-
162
- in_pipe.w.write input unless input.nil?
163
-
164
- Cmds.debug "write done, closing in_pipe.w..."
165
- in_pipe.w.close
166
-
167
- Cmds.debug "thread done."
168
- end # Thread
169
- end
170
-
171
- out_thread, err_thread = [out_pipe, err_pipe].map do |pipe|
172
- if pipe
173
- Thread.new do
174
- Thread.current[:name] = pipe.name
175
- Cmds.debug "thread started"
176
-
177
- loop do
178
- Cmds.debug "blocking on gets..."
179
- line = pipe.r.gets
180
- if line.nil?
181
- Cmds.debug "received nil, output done."
182
- else
183
- Cmds.debug NRSER.squish <<-BLOCK
184
- received #{ line.bytesize } bytes, passing to handler.
185
- BLOCK
186
- end
187
- handler.thread_send_line pipe.sym, line
188
- break if line.nil?
189
- end
190
-
191
- Cmds.debug "reading done, closing pipe.r (unless already closed)..."
192
- pipe.r.close unless pipe.r.closed?
193
-
194
- Cmds.debug "thread done."
195
- end # thread
196
- end # if pipe
197
- end # map threads
198
-
199
- Cmds.debug "handing off main thread control to the handler..."
200
- begin
201
- handler.start
202
-
203
- Cmds.debug "handler done."
204
-
205
- ensure
206
- # wait for the threads to complete
207
- Cmds.debug "joining threads..."
208
-
209
- [in_thread, out_thread, err_thread, wait_thread].each do |thread|
210
- if thread
211
- Cmds.debug "joining #{ thread[:name] } thread..."
212
- thread.join
213
- end
214
- end
215
-
216
- Cmds.debug "all threads done."
217
- end
218
-
219
- status = wait_thread.value.exitstatus
220
- Cmds.debug "exit status: #{ status.inspect }"
221
-
222
- Cmds.debug "checking @assert and exit status..."
223
- if @assert && status != 0
224
- # we don't necessarily have the err output, so we can't include it
225
- # in the error message
226
- msg = NRSER.squish <<-BLOCK
227
- streamed command `#{ cmd }` exited with status #{ status }
228
- BLOCK
229
-
230
- raise SystemCallError.new msg, status
231
- end
232
-
233
- Cmds.debug "streaming completed."
234
-
235
- return status
236
- end #really_stream
237
-
238
- # end private
239
- end
@@ -1,61 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Cmds::expand_option_hash_spec" do
4
-
5
- context "one single char key" do
6
- it "handles nil value" do
7
- expect(Cmds.expand_option_hash x: nil).to eq "-x"
8
- end
9
-
10
- it "handles simple value" do
11
- expect(Cmds.expand_option_hash x: 1).to eq "-x 1"
12
- end
13
-
14
- it "handles array value" do
15
- expect(Cmds.expand_option_hash x: [1, 2, 3]).to eq "-x 1 -x 2 -x 3"
16
- end
17
- end # single char key
18
-
19
- context "multiple single char keys" do
20
- it "order expansion by key" do
21
- expect(Cmds.expand_option_hash b: 2, a: 1, c: 3).to eq "-a 1 -b 2 -c 3"
22
- end
23
- end # multiple single char keys
24
-
25
- context "one longer key" do
26
- it "handles nil value" do
27
- expect(Cmds.expand_option_hash blah: nil).to eq "--blah"
28
- end
29
-
30
- it "handles a simple value" do
31
- expect(Cmds.expand_option_hash blah: 1).to eq "--blah=1"
32
- end
33
-
34
- it "handles an array value" do
35
- expect(Cmds.expand_option_hash blah: [1, 2, 3]).to eq "--blah=1 --blah=2 --blah=3"
36
- end
37
- end # one longer key
38
-
39
- context "multiple longer keys" do
40
- it "order expansion by key" do
41
- expect(Cmds.expand_option_hash bob: 2, al: 1, cat: 3).to eq "--al=1 --bob=2 --cat=3"
42
- end
43
- end # multiple longer keys
44
-
45
- it "handles a mess of stuff" do
46
- expect(
47
- Cmds.expand_option_hash d: 1,
48
- blah: "blow",
49
- cat: nil,
50
- x: ['m', 'e',]
51
- ).to eq "--blah=blow --cat -d 1 -x m -x e"
52
- end
53
-
54
- it "escapes paths" do
55
- expect(
56
- Cmds.expand_option_hash path: "/some folder/some where",
57
- p: "maybe ov/er here..."
58
- ).to eq '-p maybe\ ov/er\ here... --path=/some\ folder/some\ where'
59
- end
60
-
61
- end # ::expand_option_hash_spec
data/test/bin/dspec DELETED
@@ -1 +0,0 @@
1
- rspec -r ./spec/debug_helper.rb "$@"