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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22d313c4caa316badf7f3aa3a2dd286ca7409290
4
- data.tar.gz: 79ed29b958c3b909f6ee30710d04029eb6349f70
3
+ metadata.gz: 1dffd420c4f6701cdf73b18488939d50783a51a9
4
+ data.tar.gz: 57c3f635a25771abe102b10ae5739c0ccbd0ff2c
5
5
  SHA512:
6
- metadata.gz: 86b21b72ed5bda63389081cf14a7bf185cc316ff85b564700c9df0d3470285726268f71e882ca0001994aa2da4dcc9dc513f5a6a7e5565a6307626429ae64852
7
- data.tar.gz: c2a2ce42c84555784dbc00318e8a44a4da8082c4b85595e6b583ea5f845048f3a61c60c73b62a444348159e02b3f2a78a8450ccdb942ad746ea5e6c472c5e4ac
6
+ metadata.gz: 882a7604e4d4d90e293a88411c4fa9c8b2a85119bfaaf74ccb1ef49bfe17270cd9855e46aaf1a0bd52b706ad482f3934844d8a434732b2d0f421f8906283b64c
7
+ data.tar.gz: ea0f80623f0ef1173c4bf8e03399322e70ca0b4010e547e23193775eed20fc232fddefd03fea1a52763ab67831be1ba31a4a03b2a244bee2e3f0f308159a39b7
@@ -1,3 +1,4 @@
1
+ require "timmy/command_streamer"
1
2
  require "timmy/config_loader"
2
3
  require "timmy/logger"
3
4
  require "timmy/master_timer"
@@ -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
@@ -19,6 +19,10 @@ module Timmy
19
19
  TargetedTimerDefinition.delete(id)
20
20
  end
21
21
 
22
+ def set_quiet(quiet)
23
+ Logger.set_quiet(quiet)
24
+ end
25
+
22
26
  def set_precision(precision)
23
27
  Logger.set_precision(precision)
24
28
  end
@@ -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 put_output(output)
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
- puts feint(formatted_duration) + " " + output
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
- puts format_timer(timer)
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.write(filename, header + @output)
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
- private
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 = "#{bold(format_duration(timer.duration))} #{format_id(timer.definition.id)}"
68
- string += " (#{timer.group})" if timer.group
69
- string += ": #{green(timer.label)}" if timer.label
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[0m\e[1m#{string}\e[0m"
116
+ "\e[1m#{string}\e[0m"
89
117
  end
90
118
 
91
119
  def feint(string)
92
- "\e[0m\e[2m#{string}\e[0m"
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
@@ -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 arbitrary command:
21
+ Pipe output from command:
22
+
23
+ \e[36mCOMMAND | timmy [OPTIONS]\e[0m
14
24
 
15
- \e[36m[COMMAND] | timmy [OPTIONS]\e[0m
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 [LOGFILE] | timmy [OPTIONS]\e[0m
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
@@ -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).consume_stdin
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 = line.match(/^TIMMY-SESSION:v1:(?<s>\d+\.\d{9})$/))
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 = line.match(/^(?<s>\d+(\.\d+)?) (?<content>.*)/)
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(line_match[:content])
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.match(definition.start_regex)
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.match(stop_regex)) &&
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
@@ -1,3 +1,3 @@
1
1
  module Timmy
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
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.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-10-30 00:00:00.000000000 Z
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