timmy 0.4.0 → 0.5.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.
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