ttycoke 0.2.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.
- data/.gitignore +16 -0
- data/.togglerc +10 -0
- data/.tork.rb +2 -0
- data/.travis.yml +6 -0
- data/LICENSE +21 -0
- data/README.md +150 -0
- data/Rakefile +25 -0
- data/bin/ttycoke +10 -0
- data/config/config.yaml +5 -0
- data/config/ttycoke.d/id.yaml +4 -0
- data/config/ttycoke.d/lsmod.yaml +3 -0
- data/config/ttycoke.d/tail_focus.yaml +14 -0
- data/config/ttycoke.d/tail_tork_logs.yaml +26 -0
- data/config/ttycoke.d/top.yaml +8 -0
- data/doc/TTYCoke/ANSI.html +598 -0
- data/doc/TTYCoke/ANSIColor.html +586 -0
- data/doc/TTYCoke/CLI.html +242 -0
- data/doc/TTYCoke/Config/Configuration.html +210 -0
- data/doc/TTYCoke/Config.html +347 -0
- data/doc/TTYCoke/Errors/CustomizationFailed.html +135 -0
- data/doc/TTYCoke/Errors/FallbackError.html +216 -0
- data/doc/TTYCoke/Errors/ProgramNotFoundError.html +216 -0
- data/doc/TTYCoke/Errors/TTYCokeError.html +206 -0
- data/doc/TTYCoke/Errors/TTYCokeTypeError.html +216 -0
- data/doc/TTYCoke/Errors/YamlSyntaxError.html +216 -0
- data/doc/TTYCoke/Errors.html +106 -0
- data/doc/TTYCoke/LineParser.html +210 -0
- data/doc/TTYCoke/Log.html +391 -0
- data/doc/TTYCoke/Parser.html +210 -0
- data/doc/TTYCoke/Platform.html +398 -0
- data/doc/TTYCoke/Run.html +198 -0
- data/doc/TTYCoke/TTYCokeLogFormat.html +204 -0
- data/doc/TTYCoke/Version.html +136 -0
- data/doc/TTYCoke.html +110 -0
- data/doc/_index.html +279 -0
- data/doc/class_list.html +47 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +55 -0
- data/doc/css/style.css +322 -0
- data/doc/file.README.html +153 -0
- data/doc/file_list.html +49 -0
- data/doc/frames.html +13 -0
- data/doc/index.html +153 -0
- data/doc/js/app.js +205 -0
- data/doc/js/full_list.js +167 -0
- data/doc/js/jquery.js +16 -0
- data/doc/method_list.html +238 -0
- data/doc/top-level-namespace.html +130 -0
- data/lib/ttycoke/ansi.rb +127 -0
- data/lib/ttycoke/cli.rb +33 -0
- data/lib/ttycoke/config.rb +39 -0
- data/lib/ttycoke/errors.rb +40 -0
- data/lib/ttycoke/import.rb +72 -0
- data/lib/ttycoke/log.rb +84 -0
- data/lib/ttycoke/parser.rb +59 -0
- data/lib/ttycoke/platform.rb +33 -0
- data/lib/ttycoke/run.rb +59 -0
- data/lib/ttycoke/version.rb +10 -0
- data/lib/ttycoke.rb +15 -0
- data/test/data/ansi_lines.yaml +12 -0
- data/test/data/config.yaml +4 -0
- data/test/data/syntax_error_config.yaml +6 -0
- data/test/data/ttycoke.d/id.yaml +4 -0
- data/test/data/ttycoke.d/lsmod.yaml +3 -0
- data/test/data/ttycoke.d/tail_focus.yaml +14 -0
- data/test/data/ttycoke.d/tail_tork_logs.yaml +26 -0
- data/test/data/ttycoke.d/top.yaml +8 -0
- data/test/test_helper.rb +14 -0
- data/test/ttycoke/test_ansi.rb +89 -0
- data/test/ttycoke/test_cli.rb +8 -0
- data/test/ttycoke/test_config.rb +23 -0
- data/test/ttycoke/test_log.rb +22 -0
- data/test/ttycoke/test_parser.rb +24 -0
- data/ttycoke.gemspec +16 -0
- metadata +120 -0
data/lib/ttycoke/ansi.rb
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
module TTYCoke
|
|
3
|
+
module ANSI
|
|
4
|
+
# Most of this section was taken from:
|
|
5
|
+
# https://github.com/flori/term-ansicolor
|
|
6
|
+
ATTRIBUTES = [
|
|
7
|
+
[ :clear , 0 ], # String#clear is already used to empty string.
|
|
8
|
+
[ :reset , 0 ], # synonym for :clear
|
|
9
|
+
[ :bold , 1 ],
|
|
10
|
+
[ :dark , 2 ],
|
|
11
|
+
[ :italic , 3 ], # not widely implemented
|
|
12
|
+
[ :underline , 4 ],
|
|
13
|
+
[ :underscore , 4 ], # synonym for :underline
|
|
14
|
+
[ :blink , 5 ],
|
|
15
|
+
[ :rapid_blink , 6 ], # not widely implemented
|
|
16
|
+
[ :negative , 7 ], # no reverse because of String#reverse
|
|
17
|
+
[ :concealed , 8 ],
|
|
18
|
+
[ :strikethrough , 9 ], # not widely implemented
|
|
19
|
+
[ :black , 30 ],
|
|
20
|
+
[ :red , 31 ],
|
|
21
|
+
[ :green , 32 ],
|
|
22
|
+
[ :yellow , 33 ],
|
|
23
|
+
[ :blue , 34 ],
|
|
24
|
+
[ :magenta , 35 ],
|
|
25
|
+
[ :cyan , 36 ],
|
|
26
|
+
[ :white , 37 ],
|
|
27
|
+
[ :on_black , 40 ],
|
|
28
|
+
[ :on_red , 41 ],
|
|
29
|
+
[ :on_green , 42 ],
|
|
30
|
+
[ :on_yellow , 43 ],
|
|
31
|
+
[ :on_blue , 44 ],
|
|
32
|
+
[ :on_magenta , 45 ],
|
|
33
|
+
[ :on_cyan , 46 ],
|
|
34
|
+
[ :on_white , 47 ],
|
|
35
|
+
[ :intense_black , 90 ], # High intensity, aixterm (OS X)
|
|
36
|
+
[ :intense_red , 91 ],
|
|
37
|
+
[ :intense_green , 92 ],
|
|
38
|
+
[ :intense_yellow , 93 ],
|
|
39
|
+
[ :intense_blue , 94 ],
|
|
40
|
+
[ :intense_magenta , 95 ],
|
|
41
|
+
[ :intense_cyan , 96 ],
|
|
42
|
+
[ :intense_white , 97 ],
|
|
43
|
+
[ :on_intense_black , 100 ], # High intensity background, aixterm (OS X)
|
|
44
|
+
[ :on_intense_red , 101 ],
|
|
45
|
+
[ :on_intense_green , 102 ],
|
|
46
|
+
[ :on_intense_yellow , 103 ],
|
|
47
|
+
[ :on_intense_blue , 104 ],
|
|
48
|
+
[ :on_intense_magenta , 105 ],
|
|
49
|
+
[ :on_intense_cyan , 106 ],
|
|
50
|
+
[ :on_intense_white , 107 ]
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
ATTRIBUTE_NAMES = ATTRIBUTES.transpose.first
|
|
54
|
+
|
|
55
|
+
# Returns true if TTYCoke::ANSIColor supports the +feature+.
|
|
56
|
+
#
|
|
57
|
+
# The feature :clear, that is mixing the clear color attribute into String,
|
|
58
|
+
# is only supported on ruby implementations, that do *not* already
|
|
59
|
+
# implement the String#clear method. It's better to use the reset color
|
|
60
|
+
# attribute instead.
|
|
61
|
+
def support?(feature)
|
|
62
|
+
case feature
|
|
63
|
+
when :clear
|
|
64
|
+
!String.instance_methods(false).map(&:to_sym).include?(:clear)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
# Returns true, if the coloring function of this module
|
|
68
|
+
# is switched on, false otherwise.
|
|
69
|
+
def self.coloring?
|
|
70
|
+
@coloring
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Turns the coloring on or off globally, so you can easily do
|
|
74
|
+
# this for example:
|
|
75
|
+
# TTYCoke::ANSI::coloring = STDOUT.isatty
|
|
76
|
+
def self.coloring=(val)
|
|
77
|
+
@coloring = val
|
|
78
|
+
end
|
|
79
|
+
self.coloring = true
|
|
80
|
+
|
|
81
|
+
ATTRIBUTES.each do |c, v|
|
|
82
|
+
eval <<-EOT
|
|
83
|
+
def #{c}(string = nil)
|
|
84
|
+
result = ''
|
|
85
|
+
result << "\e[#{v}m" if TTYCoke::ANSI.coloring?
|
|
86
|
+
if block_given?
|
|
87
|
+
result << yield
|
|
88
|
+
elsif string.respond_to?(:to_str)
|
|
89
|
+
result << string.to_str
|
|
90
|
+
elsif respond_to?(:to_str)
|
|
91
|
+
result << to_str
|
|
92
|
+
else
|
|
93
|
+
return result #only switch on
|
|
94
|
+
end
|
|
95
|
+
result << "\e[0m" if TTYCoke::ANSI.coloring?
|
|
96
|
+
result
|
|
97
|
+
end
|
|
98
|
+
EOT
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Regular expression that is used to scan for ANSI-sequences while
|
|
102
|
+
# uncoloring strings.
|
|
103
|
+
COLORED_REGEXP = /\e\[(?:(?:[349]|10)[0-7]|[0-9])?m/
|
|
104
|
+
|
|
105
|
+
# Returns an uncolored version of the string, that is all
|
|
106
|
+
# ANSI-sequences are stripped from the string.
|
|
107
|
+
def uncolored(string = nil) # :yields:
|
|
108
|
+
if block_given?
|
|
109
|
+
yield.to_str.gsub(COLORED_REGEXP, '')
|
|
110
|
+
elsif string.respond_to?(:to_str)
|
|
111
|
+
string.to_str.gsub(COLORED_REGEXP, '')
|
|
112
|
+
elsif respond_to?(:to_str)
|
|
113
|
+
to_str.gsub(COLORED_REGEXP, '')
|
|
114
|
+
else
|
|
115
|
+
''
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
module_function
|
|
120
|
+
|
|
121
|
+
# Returns an array of all TTYCoke::ANSIColor attributes as symbols.
|
|
122
|
+
def attributes
|
|
123
|
+
ATTRIBUTE_NAMES
|
|
124
|
+
end
|
|
125
|
+
extend self
|
|
126
|
+
end
|
|
127
|
+
end
|
data/lib/ttycoke/cli.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'optparse'
|
|
3
|
+
module TTYCoke
|
|
4
|
+
class CLI
|
|
5
|
+
include TTYCoke::Log
|
|
6
|
+
|
|
7
|
+
def parse_options argv
|
|
8
|
+
log_rescue(self, __method__, caller) {
|
|
9
|
+
@opts=nil
|
|
10
|
+
@opts = OptionParser.new do |o|
|
|
11
|
+
o.banner = "Usage: ttycoke [-v] [-h] command [<args>]"
|
|
12
|
+
o.separator ""
|
|
13
|
+
o.on("-h", "--help", "Print this help.") {
|
|
14
|
+
return $stderr.puts(@opts)
|
|
15
|
+
}
|
|
16
|
+
o.on("-v", "--version", "Print version.") {
|
|
17
|
+
return $stderr.puts(TTYCoke::Version::STRING)
|
|
18
|
+
}
|
|
19
|
+
o.on("-d", "--debug", "Enable debug output.") {
|
|
20
|
+
$DEBUG = true
|
|
21
|
+
}
|
|
22
|
+
o.separator ""
|
|
23
|
+
end
|
|
24
|
+
@opts.parse!(argv) rescue return $stderr.puts(@opts)
|
|
25
|
+
if argv.empty?
|
|
26
|
+
$stderr.puts(@opts)
|
|
27
|
+
else
|
|
28
|
+
TTYCoke::Run.new(argv)
|
|
29
|
+
end
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
module TTYCoke
|
|
5
|
+
class Config
|
|
6
|
+
include TTYCoke::Log
|
|
7
|
+
|
|
8
|
+
def initialize(config_file=ENV['HOME'] + "/.ttycokerc")
|
|
9
|
+
log_rescue(self, __method__, caller, TTYCoke::Errors::YamlSyntaxError) {
|
|
10
|
+
@files = {
|
|
11
|
+
init: config_file,
|
|
12
|
+
tty_coke: File.dirname(__FILE__) + '/../../config/config.yaml'
|
|
13
|
+
}
|
|
14
|
+
raw_yaml = YAML::load(File.open(File.expand_path(file)))
|
|
15
|
+
paths = raw_yaml.fetch('import')
|
|
16
|
+
importer = File.dirname(file)
|
|
17
|
+
@config = TTYCoke::Import.import(raw_yaml, paths, {}, importer)
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_program prgm
|
|
22
|
+
if @config.keys.include?(prgm)
|
|
23
|
+
@config.fetch(prgm)
|
|
24
|
+
else
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def file
|
|
30
|
+
log_rescue(self, __method__, caller) {
|
|
31
|
+
if FileTest.exist?(@files.fetch(:init))
|
|
32
|
+
@files.fetch(:init)
|
|
33
|
+
else
|
|
34
|
+
@files.fetch(:tty_coke)
|
|
35
|
+
end
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
module TTYCoke
|
|
3
|
+
module Errors
|
|
4
|
+
class TTYCokeError < StandardError
|
|
5
|
+
@@used_codes = []
|
|
6
|
+
|
|
7
|
+
def self.status_code code = nil
|
|
8
|
+
if code
|
|
9
|
+
raise "Status code already in use: #{code}" if @@used_codes.include?(code)
|
|
10
|
+
@@used_codes << code
|
|
11
|
+
end
|
|
12
|
+
define_method(:status_code) { code }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class FallbackError < TTYCokeError
|
|
17
|
+
status_code(55)
|
|
18
|
+
def initialize(error)
|
|
19
|
+
super "TTYCoke was not able to fallback to provided program." +
|
|
20
|
+
"Parent error class ::#{error.class.name} (#{error.message})."
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class ProgramNotFoundError < TTYCokeError
|
|
25
|
+
status_code(58)
|
|
26
|
+
def initialize(error)
|
|
27
|
+
super "Program does not exist." +
|
|
28
|
+
"Parent error class ::#{error.class.name} (#{error.message})."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class YamlSyntaxError < TTYCokeError
|
|
33
|
+
status_code(57)
|
|
34
|
+
def initialize(error)
|
|
35
|
+
super "Maybe a yaml or regex syntax error. " +
|
|
36
|
+
"Parent error class ::#{error.class.name} (#{error.message})."
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module TTYCoke
|
|
2
|
+
module Import
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def import result, paths, origins={}, importer=$0
|
|
6
|
+
Array(paths).each do |path|
|
|
7
|
+
begin
|
|
8
|
+
data = YAML.load_file(importer + path)
|
|
9
|
+
rescue => error
|
|
10
|
+
error.message << ' when importing %s into %s' %
|
|
11
|
+
[path, importer].map(&:inspect)
|
|
12
|
+
raise error
|
|
13
|
+
end
|
|
14
|
+
mark_origin data, path, origins
|
|
15
|
+
merge result, data, path, origins
|
|
16
|
+
end
|
|
17
|
+
result
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def merge dst_hash, src_hash, src_file, origins={}, backtrace=[]
|
|
21
|
+
src_hash.each do |key, src_val|
|
|
22
|
+
backtrace.push key
|
|
23
|
+
|
|
24
|
+
catch :merged do
|
|
25
|
+
if dst_hash.key? key
|
|
26
|
+
dst_val = dst_hash[key]
|
|
27
|
+
|
|
28
|
+
dst_file = origins[dst_val]
|
|
29
|
+
section = backtrace.join(':')
|
|
30
|
+
|
|
31
|
+
if src_val.nil?
|
|
32
|
+
LOG.warn 'empty section %s in %s removes value %s from %s' %
|
|
33
|
+
[section, src_file, dst_val, dst_file].map(&:inspect)
|
|
34
|
+
|
|
35
|
+
dst_hash.delete key
|
|
36
|
+
throw :merged
|
|
37
|
+
|
|
38
|
+
elsif dst_val.is_a? Hash and src_val.is_a? Hash
|
|
39
|
+
merge dst_val, src_val, src_file, origins, backtrace
|
|
40
|
+
throw :merged
|
|
41
|
+
|
|
42
|
+
elsif dst_val.is_a? Array
|
|
43
|
+
dst_val.concat Array(src_val)
|
|
44
|
+
throw :merged
|
|
45
|
+
|
|
46
|
+
else
|
|
47
|
+
LOG.warn 'value %s from %s overrides %s from %s in section %s' %
|
|
48
|
+
[src_val, src_file, dst_val, dst_file, section].map(&:inspect)
|
|
49
|
+
# fall through
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
dst_hash[key] = src_val
|
|
53
|
+
end
|
|
54
|
+
backtrace.pop
|
|
55
|
+
end
|
|
56
|
+
dst_hash
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def mark_origin data, origin, result
|
|
62
|
+
result[data] = origin
|
|
63
|
+
if data.respond_to? :each
|
|
64
|
+
data.each do |*values|
|
|
65
|
+
values.each do |value|
|
|
66
|
+
mark_origin value, origin, result
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/ttycoke/log.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'logger'
|
|
3
|
+
|
|
4
|
+
module TTYCoke
|
|
5
|
+
class TTYCokeLogFormat < ::Logger::Formatter
|
|
6
|
+
def call severity, time, program_name, message
|
|
7
|
+
case severity
|
|
8
|
+
when "DEBUG"
|
|
9
|
+
datetime = time.strftime("%Y-%m-%d %H:%M")
|
|
10
|
+
print_message = "DEBUG".yellow + "--- (#{datetime})\n#{String(message)}"
|
|
11
|
+
[print_message].join("\n") + "\n\n"
|
|
12
|
+
when "ERROR"
|
|
13
|
+
datetime = time.strftime("%Y-%m-%d %H:%M")
|
|
14
|
+
print_message = "ERROR".red + " --- (#{datetime})\n#{String(message)}"
|
|
15
|
+
[print_message].join("\n") + "\n\n"
|
|
16
|
+
else
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module Log
|
|
23
|
+
|
|
24
|
+
$TEST ||= nil
|
|
25
|
+
@@logger ||= Logger.new($stderr)
|
|
26
|
+
@@logger.formatter = TTYCokeLogFormat.new
|
|
27
|
+
@@logger.level = Logger::INFO
|
|
28
|
+
|
|
29
|
+
def log_debug cls, mth, cllr, file, line, ivrs={}, lvrs={}
|
|
30
|
+
# return unless $TEST
|
|
31
|
+
msg = "#{cls.class.name}##{mth}".magenta +
|
|
32
|
+
"\nCaller => " + "#{cllr[0][/`.*'/][1..-2]}".green +
|
|
33
|
+
"\n---Instance Vars---"
|
|
34
|
+
ivrs.each { |v| msg += "#{v}=".yellow + eval("#{v}") }
|
|
35
|
+
msg += "\n---local Vars---"
|
|
36
|
+
lvrs.each { |v| msg += "#{v}=".yellow + eval("#{v}") }
|
|
37
|
+
msg += "\n#{file}:#{line}"
|
|
38
|
+
@@logger.debug(msg)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def log_error cls, mthd, cllr, e, vars={}
|
|
42
|
+
# return unless $TEST
|
|
43
|
+
status_code = e.status_code if e.respond_to?(:status_code)
|
|
44
|
+
msg = "#{cls.class.name}##{mthd}".magenta +
|
|
45
|
+
"\nCaller => " + "#{cllr[0][/`.*'/][1..-2]}".green +
|
|
46
|
+
"\ne.inspect: " + "#{e.inspect}".green +
|
|
47
|
+
"\ne.message: " + "#{e.message}".green +
|
|
48
|
+
"\ne.status_code: " + "#{status_code}".green +
|
|
49
|
+
"\n---backtrace---\n" +
|
|
50
|
+
"#{e.backtrace.join("\n")}" +
|
|
51
|
+
"\n---Vars---"
|
|
52
|
+
vars.each { |name, value| msg += "\n#{name}: ".yellow + " #{value}" }
|
|
53
|
+
@@logger.error(msg)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def log_rescue cls, mthd, cllr, ex_type=nil, vars={}
|
|
57
|
+
begin
|
|
58
|
+
result = yield
|
|
59
|
+
status = $? || 0
|
|
60
|
+
rescue Exception => e
|
|
61
|
+
log_error(cls, mthd, cllr, e, vars.merge(status: status)) unless $TEST
|
|
62
|
+
if ex_type
|
|
63
|
+
raise ex_type.new(e)
|
|
64
|
+
else
|
|
65
|
+
raise TTYCoke::Errors::TTYCokeError.new(e)
|
|
66
|
+
end
|
|
67
|
+
exit e.status_code if e.respond_to?(:status_code)
|
|
68
|
+
end
|
|
69
|
+
result
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def log_check_child_exit_status
|
|
73
|
+
result = yield
|
|
74
|
+
status = $? || 0
|
|
75
|
+
unless [0,172].include?(status)
|
|
76
|
+
raise ArgumentError
|
|
77
|
+
log_error(self, __method__, caller, e,
|
|
78
|
+
{status: status}) unless $TEST
|
|
79
|
+
exit e.respond_to?(:status_code) ? e.status_code : Errno::ENOENT::Errno
|
|
80
|
+
end
|
|
81
|
+
result
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
module TTYCoke
|
|
3
|
+
class Parser
|
|
4
|
+
extend TTYCoke::Log
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def coke! prgm, line
|
|
8
|
+
program = raw_config_parser(prgm, line)
|
|
9
|
+
if program[:exist]
|
|
10
|
+
parse(program[:match])
|
|
11
|
+
else
|
|
12
|
+
return line
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def raw_config_parser prgm, line, program={}
|
|
19
|
+
if prgm.is_a?(Hash)
|
|
20
|
+
program.merge!(raw_config_to_hash(prgm, line))
|
|
21
|
+
elsif prgm.is_a?(Array)
|
|
22
|
+
prgm[0].each { |p| program.merge!(raw_config_to_hash(p[1], line)) }
|
|
23
|
+
else
|
|
24
|
+
program.merge!(exist: false)
|
|
25
|
+
end
|
|
26
|
+
program
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def raw_config_to_hash prgm, line, program={}
|
|
30
|
+
if mch = prgm.fetch('regex').match(line)
|
|
31
|
+
program.merge!({
|
|
32
|
+
exist: true,
|
|
33
|
+
match: mch
|
|
34
|
+
})
|
|
35
|
+
end
|
|
36
|
+
program
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def matchd_color mtch, current_match, color=''
|
|
40
|
+
mtch.names.each { |name|
|
|
41
|
+
color = name if mtch[name].to_s == current_match.to_s
|
|
42
|
+
}
|
|
43
|
+
color
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse mtch, coked_str=''
|
|
47
|
+
(mtch.size - 1).times do |i|
|
|
48
|
+
color = matchd_color(mtch, mtch[i+1].to_s)
|
|
49
|
+
if color.empty? # ANSI fking bug!
|
|
50
|
+
coked_str << mtch[i+1]
|
|
51
|
+
else
|
|
52
|
+
coked_str << mtch[i+1].send(color.to_sym)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
coked_str
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
module TTYCoke
|
|
4
|
+
class Platform
|
|
5
|
+
class << self
|
|
6
|
+
def tiger?
|
|
7
|
+
platform.include?("darwin8")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def leopard?
|
|
11
|
+
platform.include?("darwin9")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
[:darwin, :bsd, :freebsd, :linux].each do |type|
|
|
15
|
+
define_method("#{type}?") do
|
|
16
|
+
platform.include?(type.to_s)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def windows?
|
|
21
|
+
%W[mingw mswin].each do |text|
|
|
22
|
+
return true if platform.include?(text)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def platform
|
|
29
|
+
RbConfig::CONFIG["host_os"].downcase
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/ttycoke/run.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
module TTYCoke
|
|
3
|
+
class Run
|
|
4
|
+
def initialize argv={}
|
|
5
|
+
log_rescue(self, __method__, caller) {
|
|
6
|
+
@argv = argv
|
|
7
|
+
@prog = TTYCoke::Config.new.find_program(@argv.fetch(0))
|
|
8
|
+
}
|
|
9
|
+
start!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def start!
|
|
15
|
+
if @prog && $stdout.tty?
|
|
16
|
+
coke_it!
|
|
17
|
+
else
|
|
18
|
+
fallback!
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def coke_it!
|
|
23
|
+
trap_signal(%w(HUP INT QUIT)) #TODO: handle CLD
|
|
24
|
+
log_check_child_exit_status {
|
|
25
|
+
IO.popen(single_quote_argv(@argv)) { |process|
|
|
26
|
+
until process.eof?
|
|
27
|
+
puts Parser.coke!(@prog, process.gets)
|
|
28
|
+
end
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fallback!
|
|
34
|
+
log_check_child_exit_status { exec(single_quote_argv(@argv)) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def trap_signal signals
|
|
38
|
+
log_check_child_exit_status {
|
|
39
|
+
signals.each { |signal|
|
|
40
|
+
trap(signal) {
|
|
41
|
+
log_rescue(self, __method__, caller,
|
|
42
|
+
{signals: signals, signal: signal}) {
|
|
43
|
+
puts reset
|
|
44
|
+
}
|
|
45
|
+
log_rescue(self, __method__, caller,
|
|
46
|
+
{signals: signals, signal: signal}) {
|
|
47
|
+
Process.waitpid2(-1, Process::WNOHANG)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def single_quote_argv argv
|
|
55
|
+
argv.map! { |arg| arg.gsub(/'/, %q('"'"')) }
|
|
56
|
+
"'" << argv.join(%q(' ')) << "'"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/ttycoke.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'ttycoke/ansi'
|
|
3
|
+
include TTYCoke::ANSI
|
|
4
|
+
|
|
5
|
+
module TTYCoke
|
|
6
|
+
autoload :CLI, 'ttycoke/cli'
|
|
7
|
+
autoload :Log, 'ttycoke/log'
|
|
8
|
+
autoload :Run, 'ttycoke/run'
|
|
9
|
+
autoload :Parser, 'ttycoke/parser'
|
|
10
|
+
autoload :Import, 'ttycoke/import'
|
|
11
|
+
autoload :Config, 'ttycoke/config'
|
|
12
|
+
autoload :Errors, 'ttycoke/errors'
|
|
13
|
+
autoload :Version, 'ttycoke/version'
|
|
14
|
+
autoload :Platform, 'ttycoke/platform'
|
|
15
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
lsmod:
|
|
2
|
+
line: "i2c_algo_bit 4423 2 nouveau,i915"
|
|
3
|
+
exp: "\e[32mi2c_algo_bit\e[0m \e[34m4423\e[0m \e[33m2\e[0m\e[31m nouveau,i915\e[0m"
|
|
4
|
+
|
|
5
|
+
tail_tork_logs:
|
|
6
|
+
-
|
|
7
|
+
results:
|
|
8
|
+
line: "Finished tests in 0.038402s, 52.0809 tests/s, 78.1214 assertions/s."
|
|
9
|
+
exp: "\e[4mFinished tests in 0.038402s, 52.0809 tests/s, 78.1214 assertions/s.\e[0m"
|
|
10
|
+
stats:
|
|
11
|
+
line: "2 tests, 2 assertions, 0 failures, 0 errors, 0 skips"
|
|
12
|
+
exp: "\e[37m2 tests, \e[0m\e[32m2 assertions, \e[0m\e[35m0 failures, \e[0m\e[31m0 errors, \e[0m\e[33m0 skips\e[0m"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
tail_tork_logs:
|
|
2
|
+
-
|
|
3
|
+
results:
|
|
4
|
+
regex: !ruby/regexp
|
|
5
|
+
/^(?<underscore>Finished.*)/
|
|
6
|
+
error:
|
|
7
|
+
regex: !ruby/regexp
|
|
8
|
+
/(?<red>.+(Error|Failure):)/
|
|
9
|
+
expected:
|
|
10
|
+
regex: !ruby/regexp
|
|
11
|
+
/(?<blue>--- expected)/
|
|
12
|
+
expected-string:
|
|
13
|
+
regex: !ruby/regexp
|
|
14
|
+
/(?<blue>-\".+)/
|
|
15
|
+
actual:
|
|
16
|
+
regex: !ruby/regexp
|
|
17
|
+
/(?<red>\++ actual)/
|
|
18
|
+
actual-string:
|
|
19
|
+
regex: !ruby/regexp
|
|
20
|
+
/(?<red>\+\".+)/
|
|
21
|
+
at:
|
|
22
|
+
regex: !ruby/regexp
|
|
23
|
+
/(?<on_yellow>@.+)/
|
|
24
|
+
stats:
|
|
25
|
+
regex: !ruby/regexp
|
|
26
|
+
/(?<white>\d+ tests, )(?<green>\d+ assertions, )(?<magenta>\d+ failures, )(?<red>\d+ errors, )(?<yellow>\d+ skips)/
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
if RUBY_VERSION < "1.9"
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
else
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
require 'minitest/unit'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
$TEST = true
|
|
10
|
+
|
|
11
|
+
lib_dir = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
12
|
+
$LOAD_PATH.unshift lib_dir, File.dirname(__FILE__)
|
|
13
|
+
|
|
14
|
+
require 'ttycoke'
|