hw-howm.rb 0.1.0

微妙に修正。
はてダラファイルにhowmファイル名を"><!--hoge.howm--><"のように埋め込んでたけど、これがよろしくないらしい。RSSにしたとき"<"がゴミとして入ってきてしまう。
たぶんはてな側のバグだとおもうけど、よくよく考えるとpタグ停止記法を使う理由もないのでファイル名埋め込みには"<!-- -->"を使うことにする。


以下本体

#!/usr/bin/ruby -w
#
# hw-howm.rb -- howmメモをhw.plで扱える形式にする.
#
require "optparse"
require "fileutils"
require "find"
require "pathname"

begin
  opt = OptionParser.new
  OPTS = {}
  opt.banner= "usage: #$0 [options] src"
  opt.on_tail("-h", "--help", "show this message") {puts opt; exit 0}
  opt.on("-t", "--timestamp=FILE", "use FILE as timestamp (default: none)") {|v| OPTS[:t] = v} # touch file path
  opt.on("-g", "--group=GROUP", "assume group name"){|v| OPTS[:g] = v}
  opt.on("-o", "--outputdir=DIR", "output file in DIR (default: .)"){ |v| OPTS[:o] = v }
  opt.version = "0.1.0"
  opt.release = nil
  opt.parse!(ARGV)
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
  puts $!
  puts opt
  exit 1
end

if ARGV.size == 0
  puts opt
  exit 1
end

updir = OPTS.key?(:o) ? OPTS[:o] : '.'
unless FileTest.directory? updir
  puts "invalid directory: #{dir}"
  puts opt
  exit 1
end

touch_file = ""
if OPTS.key?(:t) && FileTest.file?(OPTS[:t])
  touch_file = OPTS[:t]
end
TOUCH_MTIME = (touch_file == "") ? Time.at(0) : File.mtime(touch_file)

ARG_GROUP = OPTS.key?(:g) ? OPTS[:g] : ""

IN_NOOP = :IN_NOOP
IN_BODY = :IN_BODY
IN_DIARY = :IN_DIARY

# howmファイルの読み込み
def readhowm (path, def_date)

  status = IN_NOOP
  howm_content = Hash.new("")

  date = ""
  path.each_line do |line|    
    case status
    when IN_NOOP
      if line =~ /^= .*\{hw(:(.*))?(?=\})/
        date = def_date
        group = ""
        if !$2.nil? 
          $2.split(',').each do |str|
            str.strip!
            if str =~ /(\d{4}-\d{2}-\d{2})/
              date = $1
            elsif str =~ /g=(.*)/
              group = $1.strip
            end
          end
        end
        howm_content[date] += ""
        status = IN_BODY if ARG_GROUP == group
      end

    when IN_BODY
      if line =~ /^=( |$)/
        status = IN_NOOP
        redo
      end

      if line =~ /^%%%/
        status = IN_DIARY
        next
      end

    when IN_DIARY
      if line =~ /^=( |$)/
        status = IN_NOOP
        redo
      end

      # EOF対策。改行で終らせる。
      line += "\n" if line[-1] != ?\n
      howm_content[date] += line
    end
  end

  return howm_content
end

def scan_files(array, &block)
  array.each do |p|
    path = Pathname.new(p)
    path.find {|path| block.call(path)}
  end
end

# メインの処理
scan_files(ARGV) do |path|
  next unless path.readable?()

  # touchファイルより新しいか
  next if TOUCH_MTIME > path.mtime()

  # *.howmファイルのみ対象
  next unless /\.howm$/ =~ path.to_s

  filename = path.basename().to_s

  # ファイル名の先頭がYYYY-MM-DDならその日付を日記日付のデフォルトとして使用
  if /^(\d{4}-\d{2}-\d{2}).*\.howm$/ =~ filename
    def_date = $1
  else
    def_date = path.mtime().strftime("%Y-%m-%d")
  end

  # howmファイルの内容をハッシュに読みこみ
  howm_content = readhowm(path, def_date)

  howm_content.keys.each do |date|
    # はてダラファイル記事格納用ハッシュ
    hatena_content = Hash.new("")

    # はてダラファイルの先頭部分はhatena_content["0"]に格納
    key = "0"
    # はてダラファイルの内容をハッシュに格納
    uppath = "#{updir}/#{date}.txt"
    if FileTest.file?(uppath)
      File.foreach(uppath) do |line|
        if /^<!--(\d{4}-\d{2}-\d{2}.*\.howm)-->$/ =~ line
          key = $1
          next
        end
        hatena_content[key] += line
      end
    elsif FileTest.exist?(uppath)
      puts "#{uppath} is not file."
      next
    end

    # はてダラファイルもなく内容が空の場合はファイル出力しない
    next if !hatena_content.key?("0") and howm_content[date] == ""

    # はてダラファイルがなかったとき
    # とりあえずファイルの先頭は空行にしておく
    hatena_content["0"] = "\n" unless hatena_content.key?("0")

    if !hatena_content.key?(filename) or hatena_content[filename] != howm_content[date]
      hatena_content[filename] = howm_content[date]
      # はてダラファイルへ書き込み
      File.open(uppath, "w+") do |io|
        io << hatena_content["0"]
        hatena_content.keys.sort.each do |key|
          if key != "0" && hatena_content[key] != ""
            io << "<!--#{key}-->\n"
            io << hatena_content[key]
          end
        end
      end
    end
  end

end

# touch
if OPTS.key?(:t)
    FileUtils.touch(OPTS[:t])
end

exit 0

マイナーバージョン番号が上ってますがあんまり意味はないです。