cmds 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 327a49d0895e5e91d1d35c9ab352357a4a577f65
|
4
|
+
data.tar.gz: f2f7bd60a0b9bcdfca5a7e50a28ac386f329d33a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c22d98773434a8c16df0420d599222e00df2f5fcca68e448f889f16671d35db3ab441faa8d6194419c690f435b60e28b1992cc2cbddb0614c79f26fa378e5033
|
7
|
+
data.tar.gz: ca4371971382c85c6438bafac00c40f637a9e7dbeb2e232838ea9ae2e11ffdc7de5f6989ccd76560f2d09cac452d7da5438e291a29b1baa24dc64e93e8abbacb
|
data/Rakefile
CHANGED
@@ -1 +1,153 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'pastel'
|
3
|
+
require 'json'
|
4
|
+
require 'pp'
|
5
|
+
require 'tempfile'
|
6
|
+
|
1
7
|
require "bundler/gem_tasks"
|
8
|
+
|
9
|
+
Bundler.require
|
10
|
+
|
11
|
+
def configure_logger
|
12
|
+
logger = Logger.new $stderr
|
13
|
+
|
14
|
+
logger.level = case ENV['log']
|
15
|
+
when 'debug'
|
16
|
+
Logger::DEBUG
|
17
|
+
when 'info'
|
18
|
+
Logger::INFO
|
19
|
+
when 'warn'
|
20
|
+
Logger::WARN
|
21
|
+
when 'error'
|
22
|
+
Logger::ERROR
|
23
|
+
else
|
24
|
+
Logger::INFO
|
25
|
+
end
|
26
|
+
|
27
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
28
|
+
formatted = if Thread.current[:name]
|
29
|
+
"[rake #{ severity } - #{ Thread.current[:name ] }] #{msg}\n"
|
30
|
+
else
|
31
|
+
"[rake #{ severity }] #{msg}\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
if severity == 'DEBUG'
|
35
|
+
formatted = Pastel.new.cyan(formatted)
|
36
|
+
end
|
37
|
+
|
38
|
+
formatted
|
39
|
+
end
|
40
|
+
logger
|
41
|
+
end
|
42
|
+
|
43
|
+
def log
|
44
|
+
$logger ||= configure_logger
|
45
|
+
end
|
46
|
+
|
47
|
+
namespace :debug do
|
48
|
+
desc "turn debug logging on"
|
49
|
+
task :conf do
|
50
|
+
ENV['log'] ||= 'debug'
|
51
|
+
Cmds.enable_debug
|
52
|
+
end
|
53
|
+
|
54
|
+
task :proxy => :conf do
|
55
|
+
Cmds.new("./test/questions.rb").proxy
|
56
|
+
end
|
57
|
+
|
58
|
+
namespace :capture do
|
59
|
+
desc "capture with io-like input with debugging enabled"
|
60
|
+
task :io_input => :conf do
|
61
|
+
File.open "./test/lines.txt" do |f|
|
62
|
+
Cmds.new("./test/echo_cmd.rb", input: f).capture
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # ns capture
|
66
|
+
|
67
|
+
namespace :stream do
|
68
|
+
input = NRSER.dedent <<-BLOCK
|
69
|
+
one
|
70
|
+
two
|
71
|
+
three
|
72
|
+
BLOCK
|
73
|
+
|
74
|
+
desc "output to blocks"
|
75
|
+
task :blocks => :conf do
|
76
|
+
Cmds.stream "ls" do |io|
|
77
|
+
io.on_out do |line|
|
78
|
+
puts "line: #{ line.inspect }"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "use a file as output"
|
84
|
+
task :file_out => :conf do
|
85
|
+
f = Tempfile.new "blah"
|
86
|
+
Cmds.stream "echo here" do |io|
|
87
|
+
io.out = f
|
88
|
+
end
|
89
|
+
|
90
|
+
f.rewind
|
91
|
+
puts f.read
|
92
|
+
f.close
|
93
|
+
f.unlink
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "input block value"
|
97
|
+
task :value => :conf do
|
98
|
+
Cmds.stream "wc -l" do
|
99
|
+
input
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "input block hanlder"
|
104
|
+
task :handler => :conf do
|
105
|
+
Cmds.stream "wc -l" do |io|
|
106
|
+
io.on_in do
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
desc "input io"
|
112
|
+
task :io => :conf do
|
113
|
+
File.open "./test/lines.txt" do |f|
|
114
|
+
Cmds.stream("wc -l") { f }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# this was a vauge idea that doesn't yet work, and may never
|
119
|
+
# need a better understanding probably, so gonna punt for now
|
120
|
+
desc "play with questions"
|
121
|
+
task :questions => :conf do
|
122
|
+
q = nil
|
123
|
+
a = nil
|
124
|
+
|
125
|
+
as = {
|
126
|
+
"what is your name?" => 'nrser',
|
127
|
+
"what is your quest?" => 'make this shit work somehow',
|
128
|
+
"what is you favorite color?" => 'blue',
|
129
|
+
}
|
130
|
+
|
131
|
+
Cmds.stream "./test/questions.rb" do |io|
|
132
|
+
io.on_out do |line|
|
133
|
+
puts "on_out called"
|
134
|
+
q = line
|
135
|
+
puts "questions asked: #{ q }"
|
136
|
+
a = as[q]
|
137
|
+
raise "unknown question: #{ q }" unless a
|
138
|
+
puts "setting answer to #{ a }..."
|
139
|
+
end
|
140
|
+
|
141
|
+
io.on_in do |f|
|
142
|
+
puts "on_in called"
|
143
|
+
if a
|
144
|
+
puts "responding with #{ a }."
|
145
|
+
f.write a
|
146
|
+
else
|
147
|
+
puts "no response ready."
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end # namespace stream
|
153
|
+
end # namespace debug
|
data/ansible/dev.yml
CHANGED
data/cmds.gemspec
CHANGED
data/lib/cmds/capture.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
class Cmds
|
2
|
+
# invokes the command and returns a Result with the captured outputs
|
3
|
+
def capture *subs, &input_block
|
4
|
+
Cmds.debug "entering Cmds#capture",
|
5
|
+
subs: subs,
|
6
|
+
input_block: input_block
|
7
|
+
|
8
|
+
# merge any stored args and kwds and replace input if provided
|
9
|
+
options = merge_options subs, input_block
|
10
|
+
Cmds.debug "merged options:",
|
11
|
+
options: options
|
12
|
+
|
13
|
+
# build the command string
|
14
|
+
cmd = Cmds.sub @template, options[:args], options[:kwds]
|
15
|
+
Cmds.debug "built command string: #{ cmd.inspect }"
|
16
|
+
|
17
|
+
out = ''
|
18
|
+
err = ''
|
19
|
+
|
20
|
+
Cmds.debug "calling Cmds#really_stream..."
|
21
|
+
status = really_stream cmd, options do |io|
|
22
|
+
# send the input to stream, which sends it to spawn
|
23
|
+
io.in = options[:input]
|
24
|
+
|
25
|
+
# and concat the output lines as they come in
|
26
|
+
io.on_out do |line|
|
27
|
+
out += line
|
28
|
+
end
|
29
|
+
|
30
|
+
io.on_err do |line|
|
31
|
+
err += line
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Cmds.debug "Cmds#really_stream completed",
|
35
|
+
status: status
|
36
|
+
|
37
|
+
# build a Result
|
38
|
+
# result = Cmds::Result.new cmd, status, out_reader.value, err_reader.value
|
39
|
+
result = Cmds::Result.new cmd, status, out, err
|
40
|
+
|
41
|
+
# tell the Result to assert if the Cmds has been told to, which will
|
42
|
+
# raise a SystemCallError with the exit status if it was non-zero
|
43
|
+
result.assert if @assert
|
44
|
+
|
45
|
+
return result
|
46
|
+
end # #capture
|
47
|
+
end
|
data/lib/cmds/debug.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
# debug logging stuff
|
4
|
+
class Cmds
|
5
|
+
|
6
|
+
module Debug
|
7
|
+
# constants
|
8
|
+
|
9
|
+
# change the color of debug output by thread name (if present)
|
10
|
+
THREAD_COLORS = {
|
11
|
+
'INPUT' => :cyan,
|
12
|
+
'OUTPUT' => :green,
|
13
|
+
'ERROR' => :red,
|
14
|
+
}
|
15
|
+
# available Pastel styles:
|
16
|
+
#
|
17
|
+
# clear, reset, bold, dark, dim, italic, underline, underscore, inverse, hidden, strikethrough,
|
18
|
+
# black, red, green, yellow, blue, magenta, cyan, white,
|
19
|
+
# on_black, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white,
|
20
|
+
# bright_black, bright_red, bright_green, bright_yellow, bright_blue, bright_magenta, bright_cyan, bright_white,
|
21
|
+
# on_bright_black, on_bright_red, on_bright_green, on_bright_yellow, on_bright_blue, on_bright_magenta, on_bright_cyan, on_bright_white
|
22
|
+
#
|
23
|
+
|
24
|
+
# class variables
|
25
|
+
@@on = false
|
26
|
+
@@logger = nil
|
27
|
+
|
28
|
+
# class methods
|
29
|
+
# =============
|
30
|
+
|
31
|
+
# get the Logger instance. may be `nil`.
|
32
|
+
def self.logger
|
33
|
+
@@logger
|
34
|
+
end
|
35
|
+
|
36
|
+
# test if the logger is configured.
|
37
|
+
def self.configured?
|
38
|
+
!! @@logger
|
39
|
+
end
|
40
|
+
|
41
|
+
# configure the Logger with optional destination
|
42
|
+
def self.configure dest = $stdout
|
43
|
+
require 'pastel'
|
44
|
+
@@pastel = Pastel.new
|
45
|
+
|
46
|
+
@@logger = Logger.new dest
|
47
|
+
@@logger.level = Logger::DEBUG
|
48
|
+
@@logger.formatter = proc do |severity, datetime, progname, msg|
|
49
|
+
if Thread.current[:name]
|
50
|
+
msg = "[Cmds #{ severity } - #{ Thread.current[:name ] }] #{msg}\n"
|
51
|
+
|
52
|
+
if color = THREAD_COLORS[Thread.current[:name]]
|
53
|
+
msg = @@pastel.method(color).call msg
|
54
|
+
end
|
55
|
+
|
56
|
+
msg
|
57
|
+
else
|
58
|
+
"[Cmds #{ severity }] #{msg}\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# turn debug logging on. if you provide a block it will turn debug logging
|
64
|
+
# on for that block and off at the end.
|
65
|
+
def self.on &block
|
66
|
+
configure unless configured?
|
67
|
+
@@on = true
|
68
|
+
if block
|
69
|
+
yield
|
70
|
+
off
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# turn debug logging off.
|
75
|
+
def self.off
|
76
|
+
@@on = false
|
77
|
+
end
|
78
|
+
|
79
|
+
# test if debug logging is on.
|
80
|
+
def self.on?
|
81
|
+
@@on
|
82
|
+
end
|
83
|
+
|
84
|
+
# format a debug message with optional key / values to print
|
85
|
+
def self.format msg, values = {}
|
86
|
+
if values.empty?
|
87
|
+
msg
|
88
|
+
else
|
89
|
+
msg + "\n" + values.map {|k, v| " #{ k }: #{ v.inspect }" }.join("\n")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end # module Debug
|
94
|
+
|
95
|
+
# log a debug message along with an optional hash of values.
|
96
|
+
def self.debug msg, values = {}
|
97
|
+
# don't even bother unless debug logging is turned on
|
98
|
+
return unless Debug.on?
|
99
|
+
Debug.logger.debug Debug.format(msg, values)
|
100
|
+
end
|
101
|
+
end # class Cmds
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Cmds
|
2
|
+
class ERBContext < BasicObject
|
3
|
+
def initialize args, kwds
|
4
|
+
@args = args
|
5
|
+
@kwds = kwds
|
6
|
+
@arg_index = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing sym, *args, &block
|
10
|
+
if args.empty? && block.nil?
|
11
|
+
if sym.to_s[-1] == '?'
|
12
|
+
key = sym.to_s[0...-1].to_sym
|
13
|
+
@kwds[key]
|
14
|
+
else
|
15
|
+
@kwds.fetch sym
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_binding
|
23
|
+
::Kernel.send :binding
|
24
|
+
end
|
25
|
+
|
26
|
+
def arg
|
27
|
+
@args.fetch(@arg_index).tap {@arg_index += 1}
|
28
|
+
end
|
29
|
+
end # end ERBContext
|
30
|
+
end # class Cmds
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class Cmds
|
2
|
+
class IOHandler
|
3
|
+
attr_accessor :in, :out, :err
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@queue = Queue.new
|
7
|
+
@in = nil
|
8
|
+
@out = $stdout
|
9
|
+
@err = $stderr
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_out &block
|
13
|
+
@out = block
|
14
|
+
end
|
15
|
+
|
16
|
+
# called in seperate thread handling process IO
|
17
|
+
def thread_send_out line
|
18
|
+
@queue << [:out, line]
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_err &block
|
22
|
+
@err = block
|
23
|
+
end
|
24
|
+
|
25
|
+
# called in seperate thread handling process IO
|
26
|
+
def thread_send_err line
|
27
|
+
@queue << [:err, line]
|
28
|
+
end
|
29
|
+
|
30
|
+
def thread_send_line sym, line
|
31
|
+
@queue << [sym, line]
|
32
|
+
end
|
33
|
+
|
34
|
+
def start
|
35
|
+
# if out is a proc, it's not done
|
36
|
+
out_done = ! @out.is_a?(Proc)
|
37
|
+
# same for err
|
38
|
+
err_done = ! @err.is_a?(Proc)
|
39
|
+
|
40
|
+
until out_done && err_done
|
41
|
+
key, line = @queue.pop
|
42
|
+
|
43
|
+
case key
|
44
|
+
when :out
|
45
|
+
if line.nil?
|
46
|
+
out_done = true
|
47
|
+
else
|
48
|
+
handle_line @out, line
|
49
|
+
end
|
50
|
+
|
51
|
+
when :err
|
52
|
+
if line.nil?
|
53
|
+
err_done = true
|
54
|
+
else
|
55
|
+
handle_line @err, line
|
56
|
+
end
|
57
|
+
|
58
|
+
else
|
59
|
+
raise "bad key: #{ key.inspect }"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end #start
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def handle_line dest, line
|
67
|
+
if dest.is_a? Proc
|
68
|
+
dest.call line
|
69
|
+
else
|
70
|
+
dest.puts line
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# end private
|
75
|
+
end # end IOHandler
|
76
|
+
end # class Cmds
|
data/lib/cmds/pipe.rb
ADDED
data/lib/cmds/result.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class Cmds
|
2
|
+
class Result
|
3
|
+
attr_reader :cmd, :status, :out, :err
|
4
|
+
|
5
|
+
def initialize cmd, status, out, err
|
6
|
+
@cmd = cmd
|
7
|
+
@status = status
|
8
|
+
@out = out
|
9
|
+
@err = err
|
10
|
+
end
|
11
|
+
|
12
|
+
def ok?
|
13
|
+
@status == 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def error?
|
17
|
+
! ok?
|
18
|
+
end
|
19
|
+
|
20
|
+
# raises an error if there was one
|
21
|
+
# returns the Result so that it can be chained
|
22
|
+
def assert
|
23
|
+
if error?
|
24
|
+
msg = NRSER.squish <<-BLOCK
|
25
|
+
command `#{ @cmd }` exited with status #{ @status }
|
26
|
+
and stderr #{ err.inspect }
|
27
|
+
BLOCK
|
28
|
+
|
29
|
+
raise SystemCallError.new msg, @status
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end # raise_error
|
33
|
+
end
|
34
|
+
end # class Cmds
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
|
3
|
+
class Cmds
|
4
|
+
# extension of Erubis' EscapedEruby (which auto-escapes `<%= %>` and
|
5
|
+
# leaves `<%== %>` raw) that calls `Cmds.expand_sub` on the value
|
6
|
+
class ShellEruby < Erubis::EscapedEruby
|
7
|
+
def escaped_expr code
|
8
|
+
"::Cmds.expand_sub(#{code.strip})"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end # class Cmds
|