Chienomi

PureBuilder Simplyのアップデート (ReST完全対応)

有用なユーティリティコード

  • TOP
  • Old Archives
  • PureBuilder Simplyのアップデート (ReST完全対応)

3ヶ月ぶりとなったPureBuilder Simplyのアップデート。

今回はMimir Yokohamaのウェブサイトの新連載(そもそも連載開始にお金かかるので時期未定)でReSTructured Textを使うため、ReSTに完全対応した。

完全対応のポイントは、従来きちんと対応できていなかったdocinfo(Bibliographic Elements)に対応するようにした。

ただし、これは「対応した」という言い方が適切なのかどうかわからない。 ひとつは、自力でdocinfoを解釈する部分が間違っていたのと、仕様の理解自体正しくなかったので適正にした。

File.open([dir, filename].join("/")) do |f|
    l = f.gets
-        if l =~ /:[A-Za-z]+: .*/ #docinfo
-          docinfo_lines = [l.chomp]
+        if l =~ /:([A-Za-z]+): (.*)/ #docinfo
+          frontmatter = { $1 => [$2.chomp] }
+          last_key = $1

        # Read docinfo
        while(l = f.gets)
            break if l =~ /^\s*$/ # End of docinfo
-            if l =~ /^\s+- / && (docinfo_lines.last.kind_of?(Array) || docinfo_lines.last =~ /^:.*?: +-/) # List items
-              if docinfo_lines.last.kind_of?(String)
-                docinfo_lines.last =~ /^:(.*?): +- *(.*)/
-                docinfo_lines[-1] = [ [$1, $2] ]
-              end
-              docinfo_lines.last[1].push(l.sub(/^\s+- +/).chomp)
-            elsif l =~ /^\s+/ # Continuous line
-              docinfo_lines.last << " " + $'.chomp
-            elsif l =~ /^:.*?: +.*/
-              docinfo_lines.push l.chomp
+            if l =~ /^\s+/ # Continuous line
+              docinfo_lines.last.push($'.chomp)
+            elsif l =~ /:([A-Za-z]+): (.*)/
+              frontmatter[$1] = [$2.chomp]
+              last_key = $1
            end
        end

-          # Convert Hash.
-          frontmatter = {}
-          docinfo_lines.each do |i|
-            if i.kind_of?(Array) #list
-              # Array element
-              frontmatter[i[0]] = i[1]
-            elsif i =~ /^:author: .*[,;]/ #author
-              # It work only pandoc style author (not Authors.)
-              author = i.sub(/:author: /, "")
-              if author.include?(";")
-                author = author.split(/ *; */)
-              elsif author.include?(",")
-                author = author.split(/ *, */)
-              end
+          # Treat docinfo lines
+          frontmatter.each do |k,v|
+            v = v.join(" ")
+            if((k == "author" || k == "authors") && v.include?(";")) # Multiple authors.
+              v = v.split(/\s*;\s*/)

-              frontmatter["author"] = author
-            elsif i =~ /^:(.*?): +(\d{4}-\d{2}-\d{2}[T ]\d{2}[0-9: T+-]*)$/ #datetime
-              key = $1
-              time = DateTime.parse($2)
-              frontmatter[key] = time
-            elsif i =~ /^:(.*?): +(\d{4}-\d{2}-\d{2}) *$/ #date
-              key = $1
-              time = Date.parse($2)
-              frontmatter[key] = time
-            elsif i =~ /^:(.*?): +/
-              key = $1
-              value = $'
-              frontmatter[key] = value
+            elsif k == "date" # Date?
+              # Datetime?
+              if v =~ /[0-2][0-9]:[0-6][0-9]/
+                v = DateTime.parse(v)
+              else
+                v = Date.parse(v)
+              end
+            else # Simple String.
+              nil # keep v
            end
+
+            frontmatter[k] = v
        end

    elsif l && l.chomp == ".." #YAML
        # Load ReST YAML that document begins comment and block is yaml.
+          @extra_meta_format = true # ReST + YAML is not supported by Pandoc.
        lines = []

        while(l = f.gets)

考え方自体に大きな変更があったため、変更行数も多い。 ただし、22165f2よりも8f6e453のほうが以前のバージョンとの違いは少なくなっている。

ここでひとつポイントだ。ReSTの仕様上、authorsa,b,cと書かれた場合は3人の著者になる。a,b,c;と書かれた場合は1人の著者になる。 だが、Pandocは;でのみ分割するので、この仕様に従っている。このため非常にシンプルな仕様だ。

基本的にdocinfoの場合、authorsdateのみが特別扱いされる。 Pandoc的にもそのような仕様になっており、authorsauthorの代わりに書ける点も正式な仕様に従っている。 PureBuilderもこれにならって、それ以外のフィールドについては特別扱いしない。 また、Pandocは仕様にない項目を入れてもエラーにしないため、この点も合わせてある。

この上で、メタデータの解釈はPandocに委ねることにした。 従来は-Mオプションを付加して上書きしていたのだが、解釈の違いからバグにもなったし、Pandocのほうが優秀なのでこのような挙動は取りやめた。

ただし、PureBuilder的にはこれではちょっと困る。 サイトの内容に関する情報をメタデータに記述する風習があるため、メタデータに書ける内容が決められてしまうのは困るのだ。

そこで従来サポートされていた「ReSTでも先頭をコメントにした場合はそのあとYAMLメタデータを書くものとする」という仕様も維持している。 この場合、Pandocは解釈できないため、@extra_meta_formatというインスタンス変数を追加し、これが真の場合のみ-Mオプションでのメタデータを使うことにした。

こうしてReSTも完全対応できることとなった。 asciidocも結構人気があるらしいのだけど、Pandocが入力にasciidocをサポートしていないのでサポートすることはできない。 Textileをサポートしてほしい人がいれば対応は考えなくもないけれど、私が知る限りPureBuilderでTextileを使いたい人はいないはずだ。1


  1. 比較的複雑な構造を書けることがPureBuilderの利点であり、また簡易な記述をしたい人にとってもMarkdownが困ることはないはずだからだ。Textileにはコメント機能があるため、サポートすること自体は不可能ではない。↩︎