cmds 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/Rakefile +7 -2
- data/bin/rake +3 -0
- data/bin/rspec +3 -0
- data/cmds.gemspec +1 -1
- data/lib/cmds/cmd.rb +376 -0
- data/lib/cmds/debug.rb +2 -2
- data/lib/cmds/erb_context.rb +3 -3
- data/lib/cmds/io_handler.rb +2 -2
- data/lib/cmds/pipe.rb +1 -1
- data/lib/cmds/result.rb +10 -6
- data/lib/cmds/shell_eruby.rb +2 -2
- data/lib/cmds/spawn.rb +251 -0
- data/lib/cmds/sugar.rb +145 -222
- data/lib/cmds/util/defaults.rb +71 -0
- data/lib/cmds/util/params.rb +56 -0
- data/lib/cmds/util/tokenize_option.rb +118 -0
- data/lib/cmds/util/tokenize_options.rb +50 -0
- data/lib/cmds/util.rb +35 -195
- data/lib/cmds/version.rb +2 -2
- data/lib/cmds.rb +3 -36
- data/scratch/proxy.rb +1 -1
- data/spec/cmds/assert_spec.rb +1 -1
- data/spec/cmds/capture_spec.rb +9 -25
- data/spec/cmds/chomp_spec.rb +7 -7
- data/spec/cmds/curry_spec.rb +1 -1
- data/spec/cmds/err_spec.rb +9 -6
- data/spec/cmds/error_spec.rb +6 -6
- data/spec/cmds/ok_spec.rb +2 -2
- data/spec/cmds/out_spec.rb +7 -7
- data/spec/cmds/{sub_spec.rb → prepare_spec.rb} +20 -32
- data/spec/cmds/stream_spec.rb +4 -4
- data/spec/cmds/util/tokenize_option_spec.rb +119 -0
- data/spec/cmds_spec.rb +11 -0
- metadata +15 -12
- data/lib/cmds/capture.rb +0 -47
- data/lib/cmds/stream.rb +0 -239
- data/spec/cmds/expand_option_hash_spec.rb +0 -61
- data/test/bin/dspec +0 -1
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 "$@"
|