# Beyond Perspective Solutions
# http://www.bpsinc.jp/

require "rubygems"
require "msgpack"

class RailsLog

  KEYS = [:controller_action, :format, :ip, :datetime, :http_method, :params, :time_total, :time_view, :time_db, :http_response, :request_uri]

  attr_accessor :file

  def initialize(file)
    @file = file
  end

  def each
    data = nil
    File.foreach(@file) do |line|
      if line =~ /^Processing ([A-Za-z0-9_]+#[a-zA-Z0-9_]+) to ([a-z]+) \(for (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) at (\d{4}-\d{2}-\d{2} \d\d:\d\d:\d\d)\) \[([A-Z]+)\]$/ then
        yield data if data

        data = {
          :controller_action => $1,
          :format => $2,
          :ip => $3,
          :datetime => parse_date_time($4),
          :http_method => $5
        }
      elsif line =~ /^\s*Parameters: ([^\n]+)$/ then
        data[:params] = eval($1)
      elsif line =~ /^\s*Completed in (\d+)ms \(View: (\d+), DB: (\d+)\) \|([^\[]+)\[([^\]]+)\]$/ then
        data[:time_total] = $1.to_i
        data[:time_view] = $2.to_i
        data[:time_db] = $3.to_i
        data[:http_response] = $4.strip
        data[:request_uri] = $5
      end
    end

    yield data if data
  end

  def bin_each
    File.open(@file, "rb") do |fh|
      unpacker = MessagePack::Unpacker.new(fh)
      begin
        unpacker.each do |obj|
          data = {
            :controller_action => obj[0],
            :format => obj[1],
            :ip => obj[2],
            :datetime => Time.at(obj[3]),
            :http_method => obj[4],
            :params => obj[5],
            :time_total => obj[6],
            :time_view => obj[7],
            :time_db => obj[8],
            :http_response => obj[9],
            :request_uri => obj[10]
          }
          yield data
        end
      rescue EOFError
      end
    end
  end

  def convert_to_bin(dest = @file + ".bin")
    File.open(dest, "wb") do |fh|
      each do |row|
        r = row.values_at(*KEYS)
        r[3] = r[3].to_i
        fh.write r.to_msgpack
      end
    end
  end

  private

  def parse_date_time(s)
    y, m, d, ho, mi, se = s.split(/[ \-\:]/).map{|x| x.to_i }
    Time.local(y, m, d, ho, mi, se)
  end

end

class RailsLogTraverser

  def initialize(files = [])
    @logs = files
  end

  def <<(railslog)
    @logs << railslog
  end

  alias add <<

  def each
    @logs.each do |rlog|
      rlog.each do |row|
        yield row
      end
    end
  end

  def bin_each
    @logs.each do |rlog|
      rlog.bin_each do |row|
        yield row
      end
    end
  end

end


