cmds 0.0.9 → 0.1.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.
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 "$@"