たのしいRuby 第22章 アクセスログの解析2 Hatena-Log.rb 0.02

昨日作ったHatena-Log.rbを書き直してみました。

バグ修正

パイプを通しても大丈夫!

% ruby hatena-log.rb 001-2007-04.csv | head
2859    77.4%   Windows
400     10.8%   Linux
255     6.9%    Mac
88      2.4%    mobile
27      0.7%    BSD
...(略

原因は・・・ファイルを閉じてなかった・・・・。

内部的な事

仕様変更
  • OS比率を前面に押し出す感じに。
例外処理を追加
  • ファイルが開けない時でもエラーメッセージがでるように。
CSVライブラリの真似をしてcreateメソッドを作った
yieldを使ってみた
  • 出力テキストをクラス外から指定し、面倒な内部処理をクラスにお任せする事が出来るようになった!

Rubyの特徴を全面的に出してみました。

Hatena-Log.rb 0.02

#!/usr/bin/ruby

# Hatena-Log.rb 0.0.2
# Usage  : ruby hatena-log.rb [000-0000-00.csv]"

class Log
  attr_reader :ua
  def initialize(time, ip, from, ua, lang, display, color, to)
    @time = time
    @ip = ip
    @from = from
    @ua = ua
    @lang = lang
    @display = display
    @color = color
    @to = to
  end
end

class HatenaLog
  def initialize
    @logs = Array.new
  end

  # HatenaLog::create(csv_file) -> HatenaLog
  def HatenaLog::create(csv_file)
    require "csv"
    logs = HatenaLog.new

    CSV::Reader.create(csv_file).each do |line|
      logs << Log.new(*line)
    end
    return logs
  end

  def <<(log)
    @logs << log
  end
  def length
    @logs.length
  end

  # dump_os -> os_list
  # dump_os {|os, count| block} -> os_list
  def dump_os
    os_table = Hash.new(0)

    @logs.each do |log|
      case log.ua
      when /Windows|Win32|gooRSSreader/i
        os = "Windows"
      when /Mac/i
        os = "Mac"
      when /Linux/i
        os = "Linux"
      when /FreeBSD|NetBSD|OpenBSD/i
        os = "BSD"
      when /SunOS/i
        os = "Unix"
      when /DoCoMo|KDDI|Vodafone|WILLCOM/i
        os = "mobile"
      when /w3m/i
        os = "w3m"
      when /BOT/i
        os = "BOT"
      else
        os = log.ua
      end
      os_table[os] += 1
    end

    os_list = os_table.sort do |a, b|
      b[1] <=> a[1]
    end

    if block_given?
      os_list.each do |os, count|
        yield(os, count)
      end
    end
    return os_list
  end
end

# main
unless ARGV[0]
  STDERR.puts "Usage: ruby log.rb [000-0000-00.csv]"
  exit 1
end

begin
  File.open(ARGV[0]) do |file|
    logs = HatenaLog::create(file)

    length = logs.length
    logs.dump_os do |os, count|
      ratio = count.to_f * 100 / length
      printf("%s\t%.1f%%\t%s\n", count, ratio, os)
    end
  end
rescue => ex
  STDERR.puts ex.message
  exit 1
end

main部分が減りました。

OS判別は随時追加していく予定です。