timmy 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/timmy.rb +1 -0
- data/lib/timmy/command_streamer.rb +58 -0
- data/lib/timmy/config_loader.rb +4 -0
- data/lib/timmy/logger.rb +56 -20
- data/lib/timmy/option_parser.rb +21 -4
- data/lib/timmy/runner.rb +24 -8
- data/lib/timmy/targeted_timer_manager.rb +6 -2
- data/lib/timmy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dffd420c4f6701cdf73b18488939d50783a51a9
|
4
|
+
data.tar.gz: 57c3f635a25771abe102b10ae5739c0ccbd0ff2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 882a7604e4d4d90e293a88411c4fa9c8b2a85119bfaaf74ccb1ef49bfe17270cd9855e46aaf1a0bd52b706ad482f3934844d8a434732b2d0f421f8906283b64c
|
7
|
+
data.tar.gz: ea0f80623f0ef1173c4bf8e03399322e70ca0b4010e547e23193775eed20fc232fddefd03fea1a52763ab67831be1ba31a4a03b2a244bee2e3f0f308159a39b7
|
data/lib/timmy.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Timmy
|
5
|
+
class CommandStreamer
|
6
|
+
class << self
|
7
|
+
def stream(command, &block)
|
8
|
+
self.new(command).stream(&block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(command)
|
13
|
+
@command = command
|
14
|
+
@queue = Queue.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def stream(&block)
|
18
|
+
Open3.popen3(*@command) do |stdin, stdout, stderr, wait_thr|
|
19
|
+
start_readers(stdout: stdout, stderr: stderr)
|
20
|
+
pop_lines_from_active_readers(block)
|
21
|
+
join_readers()
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def start_readers(streams)
|
28
|
+
@readers = streams.map do |type, stream|
|
29
|
+
thread = Thread.new do
|
30
|
+
until (line = stream.gets).nil? do
|
31
|
+
@queue << [type, line]
|
32
|
+
end
|
33
|
+
@queue << [type, nil]
|
34
|
+
end
|
35
|
+
|
36
|
+
[type, thread]
|
37
|
+
end.to_h
|
38
|
+
|
39
|
+
@active_readers = @readers.keys
|
40
|
+
end
|
41
|
+
|
42
|
+
def pop_lines_from_active_readers(delegate)
|
43
|
+
while @active_readers.any? do
|
44
|
+
type, line = @queue.pop
|
45
|
+
|
46
|
+
if line
|
47
|
+
delegate.call(type, line)
|
48
|
+
else
|
49
|
+
@active_readers.delete(type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def join_readers
|
55
|
+
@readers.values.each(&:join)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/timmy/config_loader.rb
CHANGED
data/lib/timmy/logger.rb
CHANGED
@@ -5,6 +5,10 @@ module Timmy
|
|
5
5
|
@output_dir = File.expand_path(dir)
|
6
6
|
end
|
7
7
|
|
8
|
+
def set_quiet(quiet)
|
9
|
+
@quiet = quiet
|
10
|
+
end
|
11
|
+
|
8
12
|
def set_precision(precision)
|
9
13
|
@precision = precision
|
10
14
|
end
|
@@ -13,38 +17,61 @@ module Timmy
|
|
13
17
|
@profile = profile
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
20
|
+
def match_replay_header(line)
|
21
|
+
line.match(/^TIMMY-SESSION:v1:(?<s>\d+\.\d{9})$/)
|
22
|
+
end
|
23
|
+
|
24
|
+
def match_replay_line(line)
|
25
|
+
line.match(/^(?<s>\d+(\.\d+)?)(?<t>e)? (?<content>.*)/)
|
26
|
+
end
|
27
|
+
|
28
|
+
def put_output(output, error = false)
|
17
29
|
duration = MasterTimer.get
|
18
|
-
formatted_duration = format_duration(duration)
|
19
30
|
|
20
|
-
|
31
|
+
if @quiet
|
32
|
+
puts output
|
33
|
+
else
|
34
|
+
formatted_duration = format_duration(duration)
|
35
|
+
formatted_duration = red(formatted_duration) if error
|
36
|
+
puts "\e[0m" + feint(formatted_duration) + " " + output
|
37
|
+
end
|
38
|
+
$stdout.flush
|
21
39
|
|
22
40
|
@output ||= ''
|
23
|
-
@output += sprintf("%.9f %s\n", duration, output)
|
41
|
+
@output += sprintf("%.9f%s %s\n", duration, error ? 'e' : '', output)
|
24
42
|
end
|
25
43
|
|
26
44
|
def put_eof
|
27
|
-
put_output(feint("EOF"))
|
45
|
+
put_output(feint("EOF")) unless @quiet
|
28
46
|
end
|
29
47
|
|
30
48
|
def put_timer(timer)
|
31
|
-
|
49
|
+
do_put_timer(timer) unless @quiet
|
32
50
|
end
|
33
51
|
|
34
52
|
def finalize
|
53
|
+
save
|
54
|
+
put_profile if profile?
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def save
|
35
60
|
suffix = "#{MasterTimer.start.to_i}+#{MasterTimer.get.to_i}"
|
36
61
|
filename = File.join(output_dir, "timmy-#{suffix}.log")
|
37
|
-
header = sprintf("TIMMY-SESSION:v1:%.9f\n", MasterTimer.start)
|
38
62
|
|
39
|
-
File.
|
63
|
+
return if File.exists?(filename)
|
40
64
|
|
65
|
+
header = sprintf("TIMMY-SESSION:v1:%.9f\n", MasterTimer.start)
|
66
|
+
File.write(filename, header + @output)
|
41
67
|
puts feint("Log written to #{filename}")
|
42
68
|
puts
|
43
|
-
|
44
|
-
put_profile if profile?
|
45
69
|
end
|
46
70
|
|
47
|
-
|
71
|
+
def do_put_timer(timer)
|
72
|
+
puts format_timer(timer)
|
73
|
+
$stdout.flush
|
74
|
+
end
|
48
75
|
|
49
76
|
def put_profile
|
50
77
|
slowest_timers = TargetedTimerManager
|
@@ -64,9 +91,14 @@ module Timmy
|
|
64
91
|
end
|
65
92
|
|
66
93
|
def format_timer(timer)
|
67
|
-
string =
|
68
|
-
|
69
|
-
|
94
|
+
string = (
|
95
|
+
bold(format_duration(timer.duration)) +
|
96
|
+
" " +
|
97
|
+
bold(green(format_id(timer.definition.id)))
|
98
|
+
)
|
99
|
+
|
100
|
+
string += " " + green("(" + timer.group + ")") if timer.group
|
101
|
+
string += " #{timer.label}" if timer.label
|
70
102
|
|
71
103
|
string
|
72
104
|
end
|
@@ -80,16 +112,20 @@ module Timmy
|
|
80
112
|
sprintf(format, duration / 60, duration % 60)
|
81
113
|
end
|
82
114
|
|
83
|
-
def green(string)
|
84
|
-
"\e[0m\e[32m#{string}\e[0m"
|
85
|
-
end
|
86
|
-
|
87
115
|
def bold(string)
|
88
|
-
"\e[
|
116
|
+
"\e[1m#{string}\e[0m"
|
89
117
|
end
|
90
118
|
|
91
119
|
def feint(string)
|
92
|
-
"\e[
|
120
|
+
"\e[2m#{string}\e[0m"
|
121
|
+
end
|
122
|
+
|
123
|
+
def green(string)
|
124
|
+
"\e[32m#{string}\e[0m"
|
125
|
+
end
|
126
|
+
|
127
|
+
def red(string)
|
128
|
+
"\e[31m#{string}\e[0m"
|
93
129
|
end
|
94
130
|
|
95
131
|
def output_dir
|
data/lib/timmy/option_parser.rb
CHANGED
@@ -2,6 +2,14 @@ module Timmy
|
|
2
2
|
class OptionParser
|
3
3
|
class << self
|
4
4
|
def parse
|
5
|
+
if command_start_at = ARGV.find_index { |arg| arg == '--' }
|
6
|
+
command = ARGV.slice!(command_start_at, ARGV.length - command_start_at)[1..-1]
|
7
|
+
elsif ARGV[0] && !ARGV[0].start_with?('-')
|
8
|
+
command = ARGV.slice!(0, ARGV.length)
|
9
|
+
else
|
10
|
+
command = nil
|
11
|
+
end
|
12
|
+
|
5
13
|
opts = {}
|
6
14
|
|
7
15
|
::OptionParser.new do |parser|
|
@@ -10,19 +18,25 @@ module Timmy
|
|
10
18
|
|
11
19
|
\e[33mUsage:\e[0m
|
12
20
|
|
13
|
-
Pipe output from
|
21
|
+
Pipe output from command:
|
22
|
+
|
23
|
+
\e[36mCOMMAND | timmy [OPTIONS]\e[0m
|
14
24
|
|
15
|
-
|
25
|
+
Pass command as argument (records STDERR, gives more precise results):
|
26
|
+
|
27
|
+
\e[36mtimmy [OPTIONS --] COMMAND\e[0m
|
16
28
|
|
17
29
|
Replay previous session:
|
18
30
|
|
19
|
-
\e[36mcat
|
31
|
+
\e[36mcat LOGFILE | timmy [OPTIONS]\e[0m
|
20
32
|
EOS
|
21
33
|
|
22
34
|
parser.separator ""
|
23
35
|
parser.separator "\e[33mOptions:\e[0m"
|
24
36
|
parser.separator ""
|
25
37
|
|
38
|
+
parser.on("-q", "--quiet",
|
39
|
+
"Don't print times and targeted timers (default: false)")
|
26
40
|
parser.on("-p", "--precision NUM", Integer,
|
27
41
|
"Set precision used when printing time (default: 0)")
|
28
42
|
parser.on("-r", "--[no-]profile",
|
@@ -33,7 +47,10 @@ EOS
|
|
33
47
|
parser.separator ""
|
34
48
|
end.parse!(into: opts)
|
35
49
|
|
36
|
-
opts.map { |key, value| [key.to_s.gsub('-', '_').to_sym, value] }.to_h
|
50
|
+
opts = opts.map { |key, value| [key.to_s.gsub('-', '_').to_sym, value] }.to_h
|
51
|
+
opts[:command] = command if command
|
52
|
+
|
53
|
+
opts
|
37
54
|
end
|
38
55
|
end
|
39
56
|
end
|
data/lib/timmy/runner.rb
CHANGED
@@ -10,10 +10,17 @@ module Timmy
|
|
10
10
|
|
11
11
|
options = OptionParser.parse
|
12
12
|
replay_speed = options[:replay_speed] || @replay_speed
|
13
|
+
Logger.set_quiet(options[:quiet]) if options.key?(:quiet)
|
13
14
|
Logger.set_precision(options[:precision]) if options.key?(:precision)
|
14
15
|
Logger.set_profile(options[:profile]) if options.key?(:profile)
|
15
16
|
|
16
|
-
self.new(replay_speed: replay_speed)
|
17
|
+
instance = self.new(replay_speed: replay_speed)
|
18
|
+
|
19
|
+
if command = options[:command]
|
20
|
+
instance.stream_command(command)
|
21
|
+
else
|
22
|
+
instance.consume_stdin()
|
23
|
+
end
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
@@ -22,6 +29,14 @@ module Timmy
|
|
22
29
|
@last_replay_time = 0
|
23
30
|
end
|
24
31
|
|
32
|
+
def stream_command(command)
|
33
|
+
around_run_lines do
|
34
|
+
CommandStreamer.stream(command) do |type, line|
|
35
|
+
run_line(line.rstrip, type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
25
40
|
def consume_stdin
|
26
41
|
around_run_lines do
|
27
42
|
STDIN.each_line do |line|
|
@@ -33,8 +48,6 @@ module Timmy
|
|
33
48
|
run_line(line.rstrip)
|
34
49
|
end
|
35
50
|
end
|
36
|
-
|
37
|
-
Logger.put_eof unless @replay_mode
|
38
51
|
end
|
39
52
|
end
|
40
53
|
|
@@ -42,7 +55,7 @@ module Timmy
|
|
42
55
|
|
43
56
|
def init_replay_mode(line)
|
44
57
|
if @replay_mode == nil
|
45
|
-
if (match =
|
58
|
+
if (match = Logger.match_replay_header(line))
|
46
59
|
MasterTimer.start(match[:s].to_f)
|
47
60
|
@replay_mode = true
|
48
61
|
else
|
@@ -56,13 +69,16 @@ module Timmy
|
|
56
69
|
|
57
70
|
yield
|
58
71
|
|
72
|
+
Logger.put_eof unless @replay_mode
|
59
73
|
TargetedTimerManager.stop_all
|
60
74
|
Logger.finalize
|
61
75
|
end
|
62
76
|
|
63
77
|
def replay_line(line)
|
64
|
-
line_match =
|
78
|
+
line_match = Logger.match_replay_line(line)
|
79
|
+
line_content = line_match[:content]
|
65
80
|
line_time = line_match[:s].to_f
|
81
|
+
line_type = (line_match.send(:[], :t) rescue nil) == 'e' ? :stderr : :stdout
|
66
82
|
|
67
83
|
duration = line_time - @last_replay_time
|
68
84
|
sleep duration / @replay_speed if @replay_speed
|
@@ -70,12 +86,12 @@ module Timmy
|
|
70
86
|
|
71
87
|
MasterTimer.set(line_time)
|
72
88
|
|
73
|
-
run_line(
|
89
|
+
run_line(line_content, line_type)
|
74
90
|
end
|
75
91
|
|
76
|
-
def run_line(line)
|
92
|
+
def run_line(line, type = :stdout)
|
77
93
|
TargetedTimerManager.start_for_line(line)
|
78
|
-
Logger.put_output(line)
|
94
|
+
Logger.put_output(line, type == :stderr)
|
79
95
|
TargetedTimerManager.stop_for_line(line)
|
80
96
|
end
|
81
97
|
end
|
@@ -3,7 +3,7 @@ module Timmy
|
|
3
3
|
class << self
|
4
4
|
def start_for_line(line)
|
5
5
|
TargetedTimerDefinition.all.each do |definition|
|
6
|
-
if match = line
|
6
|
+
if match = match_line(line, definition.start_regex)
|
7
7
|
label = get_capture(match, :label)
|
8
8
|
group = get_capture(match, :group)
|
9
9
|
|
@@ -16,7 +16,7 @@ module Timmy
|
|
16
16
|
def stop_for_line(line)
|
17
17
|
started.each do |timer|
|
18
18
|
if (stop_regex = timer.definition.stop_regex) &&
|
19
|
-
(match = line
|
19
|
+
(match = match_line(line, stop_regex)) &&
|
20
20
|
get_capture(match, :group) == timer.group
|
21
21
|
stop(timer)
|
22
22
|
end
|
@@ -53,6 +53,10 @@ module Timmy
|
|
53
53
|
|
54
54
|
private
|
55
55
|
|
56
|
+
def match_line(line, regex)
|
57
|
+
line.gsub(/(\x1b\[[0-9;]*m)/, '').match(regex)
|
58
|
+
end
|
59
|
+
|
56
60
|
def get_capture(match, name)
|
57
61
|
match[name]
|
58
62
|
rescue IndexError
|
data/lib/timmy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timmy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karol Słuszniak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,6 +62,7 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- bin/timmy
|
64
64
|
- lib/timmy.rb
|
65
|
+
- lib/timmy/command_streamer.rb
|
65
66
|
- lib/timmy/config_loader.rb
|
66
67
|
- lib/timmy/logger.rb
|
67
68
|
- lib/timmy/master_timer.rb
|