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