Class: ScripTTY::Util::Transcript::Reader

Inherits:
Object
  • Object
show all
Defined in:
lib/scriptty/util/transcript/reader.rb

Overview

Reader for transcript files

Example

File.open("transcript", "r") do |file|
  reader = ScripTTY::Util::Transcript::Reader.new
  file.each_line do |line|
    timestamp, type, args = reader.parse_line(line)
    # ... do stuff here ...
  end
end

Constant Summary collapse

OP_TYPES =
{
  "Copen" => :client_open,  # client connection opened
  "Sopen" => :server_open,  # server connection opened
  "C" => :from_client,  # bytes from client
  "S" => :from_server,  # bytes from server
  "*" => :info,   # informational message
  "Sx" => :server_close,  # server closed connection
  "Cx" => :client_close,  # server closed connection
  "Sp" => :server_parsed,  # parsed escape sequence from server
  "Cp" => :client_parsed,  # parsed escape sequence from client
  "EXC" => :exception_head,    # Exception header - exception class & message
  "EX+" => :exception_backtrace,  # Exception backtrace - single line of a backtrace
}

Instance Method Summary collapse

Constructor Details

#initialize(io = nil) ⇒ Reader

Returns a new instance of Reader.



52
53
54
55
# File 'lib/scriptty/util/transcript/reader.rb', line 52

def initialize(io=nil)
  @current_line = 0
  @io = io
end

Instance Method Details

#closeObject



65
66
67
# File 'lib/scriptty/util/transcript/reader.rb', line 65

def close
  @io.close if @io
end

#next_entryObject

Raises:

  • (TypeError)


57
58
59
60
61
62
63
# File 'lib/scriptty/util/transcript/reader.rb', line 57

def next_entry
  raise TypeError.new("no I/O object associated with this reader") unless @io
  return nil if @io.eof?
  line = @io.readline
  return nil unless line
  parse_line(line)
end

#parse_line(line) ⇒ Object

Raises:

  • (ArgumentError)


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/scriptty/util/transcript/reader.rb', line 69

def parse_line(line)
  @current_line += 1
  unless line =~ /^\[([\d]+(?:\.[\d]+)?)\] (\S+)((?: "(?:[\x20-\x21\x23-\x5b\x5d-\x7e]|\\[0-3][0-7][0-7])*")*)$/
    raise ArgumentError.new("line #{@current_line}: Unable to parse basic structure")
  end
  timestamp, op, raw_args = [$1, $2, $3]
  timestamp = timestamp.to_f
  args = []
  s = StringScanner.new(raw_args.strip)
  until s.eos?
    m = s.scan(/ +/) # skip whitespace between args
    next if m
    m = s.scan /"[^"]*"/
    raise ArgumentError.new("line #{@current_line}: Unable to parse arguments") unless m
    arg = m[1..-2].gsub(/\\[0-7][0-7][0-7]/) { |m| [m[1..-1].to_i(8)].pack("C*") }    # strip quotes and unescape string
    args << arg
  end
  type = OP_TYPES[op]
  raise ArgumentError.new("line #{@current_line}: Unrecognized opcode #{op}") unless type
  if [:client_open, :server_open].include?(type)
    raise ArgumentError.new("line #{@current_line}: Bad port #{args[1].inspect}") unless args[1] =~ /\A(\d+)\Z/m
    args[1] = args[1].to_i
  end
  [timestamp, type, args]
end