cmds 0.0.3 → 0.0.4
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/Rakefile +152 -0
- data/ansible/dev.yml +5 -0
- data/cmds.gemspec +1 -0
- data/lib/cmds/capture.rb +47 -0
- data/lib/cmds/debug.rb +101 -0
- data/lib/cmds/erb_context.rb +30 -0
- data/lib/cmds/io_handler.rb +76 -0
- data/lib/cmds/pipe.rb +13 -0
- data/lib/cmds/result.rb +34 -0
- data/lib/cmds/shell_eruby.rb +11 -0
- data/lib/cmds/stream.rb +239 -0
- data/lib/cmds/sugar.rb +76 -0
- data/lib/cmds/util.rb +254 -0
- data/lib/cmds/version.rb +1 -1
- data/lib/cmds.rb +19 -376
- data/scratch/popen3.rb +33 -0
- data/scratch/proxy.rb +5 -0
- data/spec/cmds/assert_spec.rb +16 -0
- data/spec/cmds/capture_spec.rb +108 -0
- data/spec/cmds/curry_spec.rb +4 -4
- data/spec/cmds/error_spec.rb +7 -2
- data/spec/cmds/ok_spec.rb +11 -1
- data/spec/cmds/replace_shortcuts_spec.rb +105 -65
- data/spec/cmds/stream_spec.rb +58 -0
- data/spec/debug_helper.rb +3 -0
- data/spec/spec_helper.rb +64 -5
- data/test/answers.txt +3 -0
- data/test/bin/dspec +1 -0
- data/test/echo_cmd.rb +1 -0
- data/test/lines.txt +4 -0
- data/test/questions.rb +15 -0
- data/test/tick.rb +6 -0
- metadata +46 -9
- data/scratch/blah.rb +0 -6
- data/spec/cmds/call_spec.rb +0 -27
- data/spec/cmds/raise_on_error_spec.rb +0 -11
- data/spec/cmds/run_spec.rb +0 -49
data/lib/cmds.rb
CHANGED
@@ -1,393 +1,36 @@
|
|
1
1
|
# stdlib
|
2
2
|
require 'shellwords'
|
3
3
|
require 'open3'
|
4
|
-
require '
|
4
|
+
require 'thread'
|
5
5
|
|
6
6
|
# deps
|
7
7
|
require 'nrser'
|
8
8
|
|
9
9
|
# project
|
10
|
+
require "cmds/capture"
|
11
|
+
require "cmds/debug"
|
12
|
+
require "cmds/erb_context"
|
13
|
+
require "cmds/io_handler"
|
14
|
+
require "cmds/pipe"
|
15
|
+
require "cmds/result"
|
16
|
+
require "cmds/shell_eruby"
|
17
|
+
require "cmds/stream"
|
18
|
+
require "cmds/sugar"
|
19
|
+
require "cmds/util"
|
10
20
|
require "cmds/version"
|
11
21
|
|
12
22
|
class Cmds
|
13
|
-
|
14
|
-
attr_reader :cmd, :status, :out, :err
|
15
|
-
|
16
|
-
def initialize cmd, status, out, err
|
17
|
-
@cmd = cmd
|
18
|
-
@status = status
|
19
|
-
@out = out
|
20
|
-
@err = err
|
21
|
-
end
|
22
|
-
|
23
|
-
def ok?
|
24
|
-
@status == 0
|
25
|
-
end
|
26
|
-
|
27
|
-
def error?
|
28
|
-
! ok?
|
29
|
-
end
|
30
|
-
|
31
|
-
# raises an error if there was one
|
32
|
-
def raise_error
|
33
|
-
if error?
|
34
|
-
msg = NRSER.squish <<-BLOCK
|
35
|
-
command `#{ @cmd }` exited with status #{ @status }
|
36
|
-
and stderr output #{ err.inspect }
|
37
|
-
BLOCK
|
38
|
-
|
39
|
-
raise SystemCallError.new msg, @status
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# extension of Erubis' EscapedEruby (which auto-escapes `<%= %>` and
|
45
|
-
# leaves `<%== %>` raw) that calls `Cmds.expand_sub` on the value
|
46
|
-
class ShellEruby < Erubis::EscapedEruby
|
47
|
-
def escaped_expr code
|
48
|
-
"::Cmds.expand_sub(#{code.strip})"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class ERBContext < BasicObject
|
53
|
-
def initialize args, kwds
|
54
|
-
@args = args
|
55
|
-
@kwds = kwds
|
56
|
-
@arg_index = 0
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing sym, *args, &block
|
60
|
-
if args.empty? && block.nil?
|
61
|
-
if sym.to_s[-1] == '?'
|
62
|
-
key = sym.to_s[0...-1].to_sym
|
63
|
-
@kwds[key]
|
64
|
-
else
|
65
|
-
@kwds.fetch sym
|
66
|
-
end
|
67
|
-
else
|
68
|
-
super
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def get_binding
|
73
|
-
::Kernel.send :binding
|
74
|
-
end
|
75
|
-
|
76
|
-
def arg
|
77
|
-
@args.fetch(@arg_index).tap {@arg_index += 1}
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# shortcut for Shellwords.escape
|
82
|
-
#
|
83
|
-
# also makes it easier to change or customize or whatever
|
84
|
-
def self.esc str
|
85
|
-
Shellwords.escape str
|
86
|
-
end
|
87
|
-
|
88
|
-
# escape option hash.
|
89
|
-
#
|
90
|
-
# this is only useful for the two common option styles:
|
91
|
-
#
|
92
|
-
# - single character keys become `-<char> <value>`
|
93
|
-
#
|
94
|
-
# {x: 1} => "-x 1"
|
95
|
-
#
|
96
|
-
# - longer keys become `--<key>=<value>` options
|
97
|
-
#
|
98
|
-
# {blah: 2} => "--blah=2"
|
99
|
-
#
|
100
|
-
# if you have something else, you're going to have to just put it in
|
101
|
-
# the cmd itself, like:
|
102
|
-
#
|
103
|
-
# Cmds "blah -assholeOptionOn:%{s}", "ok"
|
104
|
-
#
|
105
|
-
# or whatever similar shit said command requires.
|
106
|
-
#
|
107
|
-
# however, if the value is an Array, it will repeat the option for each
|
108
|
-
# value:
|
109
|
-
#
|
110
|
-
# {x: [1, 2, 3]} => "-x 1 -x 2 -x 3"
|
111
|
-
# {blah: [1, 2, 3]} => "--blah=1 --blah=2 --blah=3"
|
112
|
-
#
|
113
|
-
# i can't think of any right now, but i swear i've seen commands that take
|
114
|
-
# opts that way.
|
115
|
-
#
|
116
|
-
def self.expand_option_hash hash
|
117
|
-
hash.map {|key, values|
|
118
|
-
# keys need to be strings
|
119
|
-
key = key.to_s unless key.is_a? String
|
120
|
-
|
121
|
-
[key, values]
|
122
|
-
|
123
|
-
}.sort {|(key_a, values_a), (key_b, values_b)|
|
124
|
-
# sort by the (now string) keys
|
125
|
-
key_a <=> key_b
|
126
|
-
|
127
|
-
}.map {|key, values|
|
128
|
-
# for simplicity's sake, treat all values like an array
|
129
|
-
values = [values] unless values.is_a? Array
|
130
|
-
|
131
|
-
# keys of length 1 expand to `-x v` form
|
132
|
-
expanded = if key.length == 1
|
133
|
-
values.map {|value|
|
134
|
-
if value.nil?
|
135
|
-
"-#{ esc key }"
|
136
|
-
else
|
137
|
-
"-#{ esc key } #{ esc value}"
|
138
|
-
end
|
139
|
-
}
|
140
|
-
|
141
|
-
# longer keys expand to `--key=value` form
|
142
|
-
else
|
143
|
-
values.map {|value|
|
144
|
-
if value.nil?
|
145
|
-
"--#{ esc key }"
|
146
|
-
else
|
147
|
-
"--#{ esc key }=#{ esc value }"
|
148
|
-
end
|
149
|
-
}
|
150
|
-
end
|
151
|
-
}.flatten.join ' '
|
152
|
-
end # ::expand_option_hash
|
153
|
-
|
154
|
-
# expand one of the substitutions
|
155
|
-
def self.expand_sub sub
|
156
|
-
case sub
|
157
|
-
when nil
|
158
|
-
# nil is just an empty string, NOT an empty string bash token
|
159
|
-
''
|
160
|
-
when Hash
|
161
|
-
expand_option_hash sub
|
162
|
-
else
|
163
|
-
esc sub.to_s
|
164
|
-
end
|
165
|
-
end # ::expand_sub
|
166
|
-
|
167
|
-
# substitute values into a command, escaping them for the shell and
|
168
|
-
# offering convenient expansions for some structures.
|
169
|
-
#
|
170
|
-
# `cmd` is a string that can be substituted via ruby's `%` operator, like
|
171
|
-
#
|
172
|
-
# "git diff %s"
|
173
|
-
#
|
174
|
-
# for positional substitution, or
|
175
|
-
#
|
176
|
-
# "git diff %{path}"
|
177
|
-
#
|
178
|
-
# for keyword substitution.
|
179
|
-
#
|
180
|
-
# `subs` is either:
|
181
|
-
#
|
182
|
-
# - an Array when `cmd` has positional placeholders
|
183
|
-
# - a Hash when `cmd` has keyword placeholders.
|
184
|
-
#
|
185
|
-
# the elements of the `subs` array or values of the `subs` hash are:
|
186
|
-
#
|
187
|
-
# - strings that are substituted into `cmd` after being escaped:
|
188
|
-
#
|
189
|
-
# sub "git diff %{path}", path: "some path/to somewhere"
|
190
|
-
# # => 'git diff some\ path/to\ somewhere'
|
191
|
-
#
|
192
|
-
# - hashes that are expanded into options:
|
193
|
-
#
|
194
|
-
# sub "psql %{opts} %{database} < %{filepath}",
|
195
|
-
# database: "blah",
|
196
|
-
# filepath: "/where ever/it/is.psql",
|
197
|
-
# opts: {
|
198
|
-
# username: "bingo bob",
|
199
|
-
# host: "localhost",
|
200
|
-
# port: 12345,
|
201
|
-
# }
|
202
|
-
# # => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
|
203
|
-
#
|
204
|
-
def self.sub cmd, args = [], kwds = {}
|
205
|
-
raise TypeError.new("args must be an Array") unless args.is_a? Array
|
206
|
-
raise TypeError.new("kwds must be an Hash") unless kwds.is_a? Hash
|
207
|
-
|
208
|
-
context = ERBContext.new(args, kwds)
|
209
|
-
erb = ShellEruby.new(replace_shortcuts cmd)
|
210
|
-
|
211
|
-
NRSER.squish erb.result(context.get_binding)
|
212
|
-
end # ::sub
|
213
|
-
|
214
|
-
def self.subs_to_args_kwds_input subs
|
215
|
-
args = []
|
216
|
-
kwds = {}
|
217
|
-
input = nil
|
218
|
-
|
219
|
-
case subs.length
|
220
|
-
when 0
|
221
|
-
# nothing to do
|
222
|
-
when 1
|
223
|
-
# can either be a hash, which is interpreted as a keywords,
|
224
|
-
# or an array, which is interpreted as positional arguments
|
225
|
-
case subs[0]
|
226
|
-
when Hash
|
227
|
-
kwds = subs[0]
|
228
|
-
|
229
|
-
when Array
|
230
|
-
args = subs[0]
|
231
|
-
|
232
|
-
else
|
233
|
-
raise TypeError.new NRSER.squish <<-BLOCK
|
234
|
-
first *subs arg must be Array or Hash, not #{ subs[0].inspect }
|
235
|
-
BLOCK
|
236
|
-
end
|
237
|
-
|
238
|
-
when 2, 3
|
239
|
-
# first arg needs to be an array, second a hash, and optional third
|
240
|
-
# can be input
|
241
|
-
unless subs[0].is_a? Array
|
242
|
-
raise TypeError.new NRSER.squish <<-BLOCK
|
243
|
-
first *subs arg needs to be an array, not #{ subs[0].inspect }
|
244
|
-
BLOCK
|
245
|
-
end
|
246
|
-
|
247
|
-
unless subs[1].is_a? Hash
|
248
|
-
raise TypeError.new NRSER.squish <<-BLOCK
|
249
|
-
second *subs arg needs to be a Hash, not #{ subs[1].inspect }
|
250
|
-
BLOCK
|
251
|
-
end
|
252
|
-
|
253
|
-
args, kwds, input = subs
|
254
|
-
else
|
255
|
-
raise ArgumentError.new NRSER.squish <<-BLOCK
|
256
|
-
must provide one or two *subs arguments, received #{ 1 + subs.length }
|
257
|
-
BLOCK
|
258
|
-
end
|
259
|
-
|
260
|
-
[args, kwds, input]
|
261
|
-
end
|
262
|
-
|
263
|
-
# create a new Cmd from template and subs and call it
|
264
|
-
def self.run template, *subs
|
265
|
-
args, kwds, input = subs_to_args_kwds_input subs
|
266
|
-
self.new(template, args: args, kwds: kwds, input: input).call
|
267
|
-
end
|
268
|
-
|
269
|
-
def self.ok? template, *subs
|
270
|
-
args, kwds, input = subs_to_args_kwds_input subs
|
271
|
-
self.new(template, args: args, kwds: kwds, input: input).ok?
|
272
|
-
end
|
273
|
-
|
274
|
-
def self.error? template, *subs
|
275
|
-
args, kwds, input = subs_to_args_kwds_input subs
|
276
|
-
self.new(template, args: args, kwds: kwds, input: input).error?
|
277
|
-
end
|
278
|
-
|
279
|
-
def self.raise_on_error template, *subs
|
280
|
-
args, kwds, input = subs_to_args_kwds_input subs
|
281
|
-
self.new(
|
282
|
-
template,
|
283
|
-
args: args,
|
284
|
-
kwds: kwds,
|
285
|
-
input: input,
|
286
|
-
raise_on_error: true
|
287
|
-
).call
|
288
|
-
end
|
289
|
-
|
290
|
-
def self.replace_shortcuts template
|
291
|
-
template
|
292
|
-
.gsub(
|
293
|
-
# %s => <%= arg %>
|
294
|
-
/(\A|[[:space:]])\%s(\Z|[[:space:]])/,
|
295
|
-
'\1<%= arg %>\2'
|
296
|
-
)
|
297
|
-
.gsub(
|
298
|
-
# %%s => %s (escpaing)
|
299
|
-
/(\A|[[:space:]])(\%+)\%s(\Z|[[:space:]])/,
|
300
|
-
'\1\2s\3'
|
301
|
-
)
|
302
|
-
.gsub(
|
303
|
-
# %{key} => <%= key %>, %{key?} => <%= key? %>
|
304
|
-
/(\A|[[:space:]])\%\{([a-zA-Z_]+\??)\}(\Z|[[:space:]])/,
|
305
|
-
'\1<%= \2 %>\3'
|
306
|
-
)
|
307
|
-
.gsub(
|
308
|
-
# %%{key} => %{key}, %%{key?} => %{key?} (escpaing)
|
309
|
-
/(\A|[[:space:]])(\%+)\%\{([a-zA-Z_]+\??)\}(\Z|[[:space:]])/,
|
310
|
-
'\1\2{\3}\4'
|
311
|
-
)
|
312
|
-
.gsub(
|
313
|
-
# %<key>s => <%= key %>, %<key?>s => <%= key? %>
|
314
|
-
/(\A|[[:space:]])\%\<([a-zA-Z_]+\??)\>s(\Z|[[:space:]])/,
|
315
|
-
'\1<%= \2 %>\3'
|
316
|
-
)
|
317
|
-
.gsub(
|
318
|
-
# %%<key>s => %<key>s, %%<key?>s => %<key?>s (escaping)
|
319
|
-
/(\A|[[:space:]])(\%+)\%\<([a-zA-Z_]+\??)\>s(\Z|[[:space:]])/,
|
320
|
-
'\1\2<\3>s\4'
|
321
|
-
)
|
322
|
-
end
|
323
|
-
|
324
|
-
attr_reader :tempalte, :args, :kwds, :input, :raise_on_error
|
23
|
+
attr_reader :template, :args, :kwds, :input, :assert
|
325
24
|
|
326
25
|
def initialize template, opts = {}
|
26
|
+
Cmds.debug "Cmds constructed",
|
27
|
+
template: template,
|
28
|
+
options: opts
|
29
|
+
|
327
30
|
@template = template
|
328
31
|
@args = opts[:args] || []
|
329
32
|
@kwds = opts[:kwds] || {}
|
330
33
|
@input = opts[:input] || nil
|
331
|
-
@
|
332
|
-
end #initialize
|
333
|
-
|
334
|
-
def call *subs
|
335
|
-
# merge any stored args and kwds and get any overriding input
|
336
|
-
args, kwds, input = merge_subs subs
|
337
|
-
|
338
|
-
cmd = Cmds.sub @template, args, kwds
|
339
|
-
|
340
|
-
out, err, status = if input.nil?
|
341
|
-
Open3.capture3 cmd
|
342
|
-
else
|
343
|
-
Open3.capture3 cmd, stdin_data: input
|
344
|
-
end
|
345
|
-
|
346
|
-
result = Cmds::Result.new cmd, status.exitstatus, out, err
|
347
|
-
|
348
|
-
result.raise_error if @raise_on_error
|
349
|
-
|
350
|
-
return result
|
351
|
-
end #call
|
352
|
-
|
353
|
-
# returns a new `Cmds` with the subs merged in
|
354
|
-
def curry *subs
|
355
|
-
args, kwds, input = merge_subs(subs)
|
356
|
-
self.class.new @template, args: args, kwds: kwds, input: input
|
357
|
-
end
|
358
|
-
|
359
|
-
def ok?
|
360
|
-
call.ok?
|
361
|
-
end
|
362
|
-
|
363
|
-
def error?
|
364
|
-
call.error?
|
365
|
-
end
|
366
|
-
|
367
|
-
private
|
368
|
-
|
369
|
-
def merge_subs subs
|
370
|
-
# break `subs` into `args` and `kwds`
|
371
|
-
args, kwds, input = Cmds.subs_to_args_kwds_input subs
|
372
|
-
|
373
|
-
# use any default input if we didn't get a new one
|
374
|
-
input = @input if input.nil?
|
375
|
-
|
376
|
-
[@args + args, @kwds.merge(kwds), input]
|
377
|
-
end #merge_subs
|
378
|
-
|
379
|
-
# end private
|
380
|
-
end # Cmds
|
381
|
-
|
382
|
-
# convenience for Cmds::run
|
383
|
-
def Cmds *args
|
384
|
-
Cmds.run *args
|
385
|
-
end
|
386
|
-
|
387
|
-
def Cmds? *args
|
388
|
-
Cmds.ok? *args
|
389
|
-
end
|
390
|
-
|
391
|
-
def Cmds! *args
|
392
|
-
Cmds.raise_on_error *args
|
393
|
-
end
|
34
|
+
@assert = opts[:assert] || false
|
35
|
+
end # #initialize
|
36
|
+
end
|
data/scratch/popen3.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'thread'
|
5
|
+
require 'pty'
|
6
|
+
|
7
|
+
master, slave = PTY.open
|
8
|
+
|
9
|
+
Open3.popen3("./test/tick.rb 10") do |stdin, stdout, stderr, thread|
|
10
|
+
{
|
11
|
+
stdout => ["out", $stdout],
|
12
|
+
stderr => ["err", $stderr],
|
13
|
+
}.each do |src, (name, dest)|
|
14
|
+
|
15
|
+
puts "starting #{ name } thread"
|
16
|
+
Thread.new do
|
17
|
+
loop do
|
18
|
+
puts "getting #{ name } line..."
|
19
|
+
line = src.gets
|
20
|
+
puts "got #{ name } line."
|
21
|
+
if line.nil?
|
22
|
+
puts "#{ name } done, breaking."
|
23
|
+
break
|
24
|
+
else
|
25
|
+
puts "wiriting #{ line.bytesize } bytes to #{ name }."
|
26
|
+
dest.puts line
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
thread.join
|
33
|
+
end
|
data/scratch/proxy.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Cmds::assert" do
|
4
|
+
it "should raise an error when the command fails" do
|
5
|
+
expect{ Cmds.assert "exit 1" }.to raise_error Errno::EPERM
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should do the same for Cmds!" do
|
9
|
+
expect{ Cmds! "exit 1" }.to raise_error Errno::EPERM
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be chainable when the command is ok" do
|
13
|
+
expect( Cmds!("echo hey").out ).to eq "hey\n"
|
14
|
+
expect( Cmds.new("echo hey").capture.assert.out ).to eq "hey\n"
|
15
|
+
end
|
16
|
+
end # Cmds::run
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Cmds::capture" do
|
4
|
+
it "captures stdout" do
|
5
|
+
expect(
|
6
|
+
Cmds.new(%{ruby -e '$stdout.puts "hey"'}).capture.out
|
7
|
+
).to eq "hey\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "captures stderr" do
|
11
|
+
expect(
|
12
|
+
Cmds.new(%{ruby -e '$stderr.puts "ho"'}).capture.err
|
13
|
+
).to eq "ho\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
context "echo_cmd.rb 'hello world!'" do
|
17
|
+
|
18
|
+
shared_examples "executes correctly" do
|
19
|
+
it_behaves_like "ok"
|
20
|
+
|
21
|
+
it "should have 'hello world!' as ARGV[0]" do
|
22
|
+
expect( JSON.load(result.out)['ARGV'][0] ).to eq "hello world!"
|
23
|
+
end
|
24
|
+
end # executes correctly
|
25
|
+
|
26
|
+
context "positional args" do
|
27
|
+
let(:result) {
|
28
|
+
Cmds "./test/echo_cmd.rb <%= arg %>", ["hello world!"]
|
29
|
+
}
|
30
|
+
|
31
|
+
it_behaves_like "executes correctly"
|
32
|
+
end
|
33
|
+
|
34
|
+
context "keyword args" do
|
35
|
+
let(:result) {
|
36
|
+
Cmds "./test/echo_cmd.rb <%= s %>", s: "hello world!"
|
37
|
+
}
|
38
|
+
|
39
|
+
it_behaves_like "executes correctly"
|
40
|
+
end
|
41
|
+
|
42
|
+
end # context echo_cmd.rb 'hello world!'
|
43
|
+
|
44
|
+
# context "feeding kwargs to args cmd" do
|
45
|
+
# let(:result) {
|
46
|
+
# Cmds "./test/echo_cmd.rb %s", s: "sup y'all"
|
47
|
+
# }
|
48
|
+
|
49
|
+
# it "" do
|
50
|
+
# expect( result.cmd ).to eq nil
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
|
54
|
+
it "should error when second (subs) arg is not a hash or array" do
|
55
|
+
expect {
|
56
|
+
Cmds "./test/echo_cmd.rb <%= arg %>", "hello world!"
|
57
|
+
}.to raise_error TypeError
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is reusable" do
|
61
|
+
args_cmd = Cmds.new "./test/echo_cmd.rb <%= arg %>"
|
62
|
+
kwds_cmd = Cmds.new "./test/echo_cmd.rb <%= s %>"
|
63
|
+
|
64
|
+
args = ["arg one", "arg two", "arg three"]
|
65
|
+
|
66
|
+
args.each do |arg|
|
67
|
+
results = [
|
68
|
+
args_cmd.capture([arg]),
|
69
|
+
kwds_cmd.capture(s: arg)
|
70
|
+
]
|
71
|
+
|
72
|
+
results.each do |result|
|
73
|
+
expect( echo_cmd_argv result ).to eq [arg]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # is reusable
|
77
|
+
|
78
|
+
context "input" do
|
79
|
+
let(:input) {
|
80
|
+
<<-BLOCK
|
81
|
+
one
|
82
|
+
two
|
83
|
+
three
|
84
|
+
four!
|
85
|
+
BLOCK
|
86
|
+
}
|
87
|
+
|
88
|
+
it "accepts input via options" do
|
89
|
+
cmd = Cmds.new(ECHO_CMD, input: input)
|
90
|
+
expect( echo_cmd_stdin cmd.capture ).to eq input
|
91
|
+
end
|
92
|
+
|
93
|
+
it "accepts input via block" do
|
94
|
+
cmd = Cmds.new ECHO_CMD
|
95
|
+
expect( echo_cmd_stdin cmd.capture { input } ).to eq input
|
96
|
+
end
|
97
|
+
|
98
|
+
it "accepts input from a stream" do
|
99
|
+
File.open "./test/lines.txt" do |f|
|
100
|
+
input = f.read
|
101
|
+
f.rewind
|
102
|
+
|
103
|
+
cmd = Cmds.new ECHO_CMD
|
104
|
+
expect( echo_cmd_stdin cmd.capture { f } ).to eq input
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end # context input
|
108
|
+
end # Cmds::capture
|
data/spec/cmds/curry_spec.rb
CHANGED
@@ -2,13 +2,13 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Cmds::curry" do
|
4
4
|
it "currys" do
|
5
|
-
base = Cmds.new "
|
5
|
+
base = Cmds.new "#{ ECHO_CMD } <%= x %> <%= y %>"
|
6
6
|
|
7
7
|
x1 = base.curry x: 1
|
8
8
|
x2 = base.curry x: 2
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
expect( echo_cmd_argv x1.call y: 'why' ).to eq ['1', 'why']
|
11
|
+
expect( echo_cmd_argv x2.call y: 'who' ).to eq ['2', 'who']
|
12
|
+
expect( echo_cmd_argv base.call x: 3, y: 4 ).to eq ['3', '4']
|
13
13
|
end # it currys
|
14
14
|
end # Cmds::run
|
data/spec/cmds/error_spec.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Cmds::error?" do
|
4
|
-
it "works" do
|
4
|
+
it "works through instance method" do
|
5
|
+
expect( Cmds.new("true").error? ).to be false
|
6
|
+
expect( Cmds.new("false").error? ).to be true
|
7
|
+
end
|
8
|
+
|
9
|
+
it "works through class method" do
|
5
10
|
expect( Cmds.error? "true").to be false
|
6
11
|
expect( Cmds.error? "false").to be true
|
7
12
|
end
|
8
|
-
end # Cmds::
|
13
|
+
end # Cmds::error?
|
data/spec/cmds/ok_spec.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Cmds::ok?" do
|
4
|
-
it "works" do
|
4
|
+
it "works through instance method" do
|
5
|
+
expect( Cmds.new("true").ok? ).to be true
|
6
|
+
expect( Cmds.new("false").ok? ).to be false
|
7
|
+
end
|
8
|
+
|
9
|
+
it "works through class method" do
|
5
10
|
expect( Cmds.ok? "true").to be true
|
6
11
|
expect( Cmds.ok? "false").to be false
|
7
12
|
end
|
13
|
+
|
14
|
+
it "workds through global method" do
|
15
|
+
expect( Cmds? "true" ).to be true
|
16
|
+
expect( Cmds? "false" ).to be false
|
17
|
+
end
|
8
18
|
end # Cmds::ok?
|