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.
- 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 "$@"
|