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
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
|