Chienomi

ネストされた構造のためのPureDocのTOC展開

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

ネストTOC機能

文書からTOCを作る上で、やはり構造的にネストしたいことはあると思う。 最もポピュラーなのは、ulをネストさせることだろう。

だが、難しいのは、例えば最初にh4が来て、次にh2が来て、などということがありうるのだ。そして、h3は存在しないかもしれない。

間の全てのレベルが存在することにするのか。順に礼儀正しく登場すると仮定していいのか。

結局だが、汎用性のある仕様として次のようにした。

  • 最低レベルはオフセットかまたは実際に使われた最も大きいヘッダーに基づく(数え方としてはmin)
  • レベルの変遷に応じて変遷分proc4openproc4closeを呼ぶ。例えば->(l, ol) { "<ul>" }のように書く。
  • 当該レベルまではopen/closeした後はproc4eachを呼ぶ。

    def nest_expand(proc4open, proc4close, proc4each, offset=nil) result = [] mi = self.min { |i| i.level } or return nil mi = mi.level

     if ! offset.respond_to?(:to_int) || offset > ( mi - 1 )
             offset = mi - 1
     end
    
     cur = offset
    
     self.each do |i|
         if i.level > cur
             (i.level - cur ).times {|n| result << proc4open.call( ( i.level - (i.level - cur - 1 - n) ), ( i.level - offset - (i.level - cur - 1 - n) )) }
         elsif i.level < cur
             result << (cur - i.level).times {|n| proc4close.call( (cur - n), ( cur - n - offset ) ) }
         end
    
         result << proc4each.call(i.level, (i.level - offset), i.title)
    
         cur = i.level
     end
    
     result.join

    end

eRubyでは内部のメソッドがputsすればいいような言い方をされることが多いが、それは先に出力されてしまっていたので、置換できるようにするために一旦配列に格納した。

テンプレート側の記述量が多く、また直感的でないというデメリットはあるが、なんとかうまく処理できた。

instance_evalと定数

しかし、むしろ苦戦したのは、ProfileでTOCを含めることだった。

Profileは基本的にそれ自体がPureDocを拡張したRubyコードである。

文章としてヘッダーを含めているわけでもないので、TOCを作るためのとっかかりがないのだ。

そこで結局は

  • テンプレート側でテーブル手前にリンクを貼る
  • profileであとから各カテゴリをヘッダとして登録する

という方法を取ったのだが、意外な理由でうまくいなかった。 というのは、

instance_evalで評価した場合、そのコンテキストが認識する定数にアクセスできない」

のだ。PureDocはソースをObject#instance_evalを使って解析するため、この問題にひっかかっってヘッダーの登録ができなかった。

そこで、PureDocに登録用のメソッドを追加することとなった。

簡単に書いているが、profileは整頓されていない部分が多く、結構大変だった。