PureDocのエスケープ機能
有用なユーティリティコード
- TOP
- Old Archives
- PureDocのエスケープ機能
ずっと機能しない、と言ってきたPureDoc組み込みHTMLエスケープ機能だが、修正されて動くようになった。
問題のコードはこんな感じ。
STANDARD_ESCAPE_EX = /<(?![a-z/])|(?<![/"a-z])>|&(?![a-z0-9]+;|#[x0-9]+;)/ FORCE_ESCAPE_EX = /[<>"&]/ ESCAPE_INVOKE = ->(m, ptn) { return "" if m.nil? m.gsubz(ptn) do case m when "<" "<" when ">" ">" when '"' """ when "&" "&" end end }
修正版はこうなった
STANDARD_ESCAPE_EX = /<(?:(?:/[a-z]+|[a-z]+(?: +[a-z]+="[^"]*"+)*)>)?|(?:<(?:/[a-z]+|[a-z]+(?: +[a-z]+="[^"]*"+)*))?>|&(?![a-z0-9]+;|#[x0-9]+;)/ FORCE_ESCAPE_EX = /[<>"&]/ ESCAPE_INVOKE = ->(m, ptn) { return "" if m.nil? m.gsub(ptn) do case $& when "<" "<" when ">" ">" when '"' """ when "&" "&" else $& end end }
動かない原因は、case
でマッチする文字列が、正規表現のマッチ文字列ではなく元文字列になっていた、という単純なもので、気付くまでにデバッグに時間がかかったが、気付いてしまえばあっという間だった。
ところが、よく見るとSTANDARD_ESCAPE_EX
がまるごと変わっている。これは、「<
及び>
がタグを構成する一部か」を判定する部分が甘かったからなのだが、ちょっとハマったのが、るりまには書かれていない「後読みアサーションに量指定子は使えない」という仕様だった。どうしても量指定子を使いたかったので、選択(?
)でタグを構成する場合はタグ全体がマッチするようにした。このようにすると、case
文で判定しているのは文字が含まれているかではなく、文字が===
でマッチするかであるため(標準では文字列の同一性を見る)、タグ全体がマッチした場合はcase
文でのマッチに失敗する。マッチに失敗した場合はelse
でマッチ文字列全体を返すためString#grub
の内容が唯一のcase
文である時それにマッチしない文字列は変更されない。結果的に「タグを構成しているとみられる文字列は変更しない」となる。
なお、#esc
メソッドはこのように「タグ、もしくは文字参照と見られる文字列はエスケープしない」が、これは全ての要素メソッドが自動的に呼ぶためで、ユーザーが明示的にエスケープしたい場合は#esc!
メソッドを呼ぶことで対応できる。これは、このような判定は一切行わず、全ての当該シンボルをエスケープする。
なお、PureDocは特殊文字入力にHTML/XMLの文字参照を使うことになっている。そのため文字参照のエスケープを回避しているのだが、HTML以外への編訳においては当然文字参照のアンエスケープまたは変換が必要となる。