テキストファイルをはてなダイアリーにインポート。

MacBookのハードディスクが壊れて数ヶ月分の日記が手の届かないところに行ってしまった。
失ってしまったものをいつまで思ってもどうしようもない。前に進まないといけない。今後、こういうことを無くすためにはてなサブアカウントを利用してはてなダイアリーに書くことにした。
まずは生き残っているファイルをインポートするために必要なファイルを作成する処理をRubyで書くことに。実装する上での要件はこんな感じ。

  • 日記は1日1ファイル
  • ファイル名はyyyy-mm-dd.txtが基本
    • 場合によっては曜日が入る
    • テキストファイル
    • 年→月でディレクトリが作られている
  • はてなダイアリーにインポートするためのファイル形式はXML
<?xml version="1.0" encoding="UTF-8"?>
<diary>
  <day date="2006-04-08" title="">
    <body>
      本文
    </body>
  </day>
</diary>

というわけで、久しぶりのRubyを楽しみつつサクサクと書く。
途中、REXMLの使い方がわからなかったのでこちらを参考にさせてもらい完成。
さぁ、れっつインポート、したけど読み込まれない。
何故にと思いあれやこれやと出力されたファイルをいじってインポートさせ動作を確認。どうやらAttributeの値を''ではなく""で囲んでやらないと読み込んでくれないようだと判明。
REXMLでどうやって''を""にすればいいのだ、面倒だし複雑なフォーマットではないので、ベタに文字列として出力してやろうかと思ったけど、それはちょっと格好悪いとゴーストが囁くので、Google様にお尋ねしたところ、バッチリな回答がこちらにあったので、コピペして実行、インポートをしたところ無事に読み込まれた。
完成したスクリプトはこんな感じ。

require 'kconv'
require 'rexml/document'

$kcode = 'u'

def list_diary(dirname)
  return unless FileTest.exist?(dirname) or FileTest.directory?(dirname)

  Dir.foreach(dirname) do |item|
    next if item == '.' or item == '..'

    full_path = File.join(dirname, item)
    
    if /(^\d{4}-\d{2}-\d{2})(.*)(.txt)/ =~ item
      body = nil
      File.open(full_path) do |file|
        body = file.read
      end

      # 重複チェック
      $diaries.each do |item|
        if item.has_key?($1)
          $duplicate << $1
          break
        end
      end
      next if $duplicate.include?($1)

      diary = Hash[$1, body]
      $diaries << diary
    else
      next unless FileTest.directory?(full_path)
      list_diary(full_path)
    end

  end
end

if ARGV.size < 1 or not FileTest.directory?(ARGV[0])
  $stderr.puts("ディレクトリを指定しろ。".tosjis)
  exit
end

$diaries = []                   # 日記を格納
$duplicate = []                 # 重複している日を格納

# テキストファイルを読み込み配列に格納
list_diary(ARGV[0].chomp)

# 日付が重複していたら該当の日付を出力して処理を終了
unless $duplicate.empty?
  $duplicate.each {|item| puts item}
  exit
end

# XMLファイル作成
# attributeの値が''で囲まれてしまうとうまく取り込めないようなので
# ""で囲むように設定
REXML::Attribute.class_eval( %q^
		def to_string
			%Q[#@expanded_name="#{to_s().gsub(/"/, '&quot;')}"]
		end
	^ )

entries = REXML::Element.new("diary")

$diaries.each do |diary|
  day = REXML::Element.new("day")
  diary.each do |date, body|
    day.add_attribute("date", date.toutf8)
    day.add_attribute("title")
    day.add_element("body").add_text(body.toutf8)
  end
  entries.add_element(day)
end

File.open('import.xml', 'w') do |file|
  file.puts(REXML::XMLDecl.new("1.0", "UTF-8"))
  file.puts(entries)
end