CSVライブラリの作成

標準ライブラリのCSVクラス見たいなのが作りたかったので練習に作ってみた。
まあ作った理由の半分は必要が有ったからだけど。


標準ライブラリでは自前で以下のような動作ができる。

#csv.rbからコピペ
CSV.open("path/to/file.csv", "wb") do |csv|
  csv << ["row", "of", "CSV", "data"]
  csv << ["another", "row"]
end


単純に考えると自前で作るとこんなのしか思い浮かばない。
これはダサいのでなんとかしたい。

csv = CSV.open("path/to/file.csv", "wb")
csv.write(["row", "of", "CSV", "data"])
csv.close


取り合えずcsv.rbをみたりネットで調べて作ってみたので公開。
テストはあまりしていないけど、取り合えず動いたのでまあ気にせずに。

#単純なCSV処理のみ
#改行対応はしない

class SimpleCSV
  def SimpleCSV.open(file, mode="r", sep=",", &block)
    case mode
    when "r"
      reader(file, sep, &block)
    when "w"
      writer(file, sep, &block)
    else
      raise "mode error"
    end
  end

  class <<self
    private
    def reader(file, sep, &block)
      if block
        begin
          f = File.open(file, "r")
          open_reader(f,sep,&block)
        ensure
          f.close if f
        end
      else
        File.read(file, mode)
      end
    end

    def writer(file, sep, &block)
      if block
        begin
          open_writer(file,sep,&block)
        end
      else
        CsvFile.open(file, "w", sep)
      end
    end

    def open_reader(f, sep, &block)
      f.read.split("?n").each do |line|
        yield(line.split(sep))
      end
    end

    def open_writer(file, sep, &block)
      CsvFile.open(file, "w", sep) do |f|
        yield(f)
      end
    end

    class CsvFile < File
      def open(path, mode="r", sep=",", perm=0666)
        @@sep = sep #インスタンス変数だったら<<で使用出来ないのでクラス変数で
        super(path, mode, perm)
      end

      def CsvFile.open(path, mode="r", sep=",", perm=0666)
        @@sep = sep
        super(path, mode, perm)
      end

      def <<(arg)
        tmp = []
        if arg.kind_of?(Array)
          tmp = arg.join(@@sep)
        else
          tmp << arg
        end
        super(tmp)
      end
    end
  end
end
#!/usr/bin/env ruby
require 'simplecsv'

#こんな感じでCSVクラスと同じような感じで使える
SimpleCSV.open("a", "w") do |f|
  f << ["aaa", "bbb", "cc"]
end