RubyでHTML入力支援
発端
HTMLを作成する必要が生じて昔の知識を思い出しながらやっているのだけれど、どうも思考と入力がちぐはぐになってしまう。なにが思考を妨げているかというと、1.大量のカッコとクオートを使うので、シフトキーを多用する、2.閉じのタグが画面をごちゃごちゃにしてしまう、の2点。
ただ、HTMLのDOM構造から離れるわけにはいかないので、ひとまずはインデントでタグのネストを表し、それを変換するようなスクリプトを書けばいいのでは? という考えに到達した。
つまり、
table tr td あ td い tr td う td え
を(上の例ではスペースでインデントしてます)
<table> <tr> <td>あ</td> <td>い</td> </tr> <tr> <td>う</td> <td>え</td> </tr> </table>
に変換してくれるような。
構想、仕様の策定
作りながら固まっていった最終的な仕様は、
- #で始まる行・・・コメント
- !で始まる行・・・その行をそのまま出力(直前までの閉じてないタグを全て閉じる)
- *で始まる行・・・その行をそのまま出力(ネストに影響しない)
- 1行の要素 各要素は基本的に半角スペースで区切る
- ネストはタブ(\t)の個数で表す
- タブのあとの最初の文字列はタグ名
- id #で始まる文字列
- クラス名 .で始まる文字列
- 一般的なタグの属性は、プロパティ=値
- タグに含まれる文字列 もう一度タブ(\t)で区切り、その後に記述
実装
見事な一本スクリプト。後悔はしてない。それなりに役に立っている。
実装を念頭において仕様を決めたのでかなり楽だった。
if ARGV[0]==nil puts "error: no input file" exit(0) end close_tags = Array.new output_filename = ARGV[1]==nil ? ARGV[0]+"_out.html" : ARGV[1] input = File.open(ARGV[0]) output = File.open(output_filename, "w") input.each_line{|line| if line[0].chr=="!" close_tags.reverse_each{|elem| output.puts elem['tag'] close_tags.pop } output.puts line.sub(/^!/, "") next elsif line[0].chr=="*" output.puts line.sub(/^\*/, "") next elsif line[0].chr=="#" || line.length==1 next end output_html = "<" class_names = "" id = "" line.sub!(/^[ ]+/, "")#タブ(\t)を検索している nest = $&==nil ? 0 : $&.length close_tags.reverse_each{|elem| break if nest>elem['nest'] output.puts elem['tag'] close_tags.pop } ar = line.split("\t") ar[1] = "" if ar[1]==nil output_html += ar[0].split(" ")[0]#半角スペースで分割している ar[0].split(" ").each{|e| if e=~/^#/ id = e.sub(/^#/,"") output_html += " id=\""+id+"\"" elsif e=~/^\./ class_names += " "+e.sub(/\./, "") elsif e=~/=/ params = e.split("=") params[1] = params[1]==nil ? "" : params[1] output_html += " "+params[0]+"=\""+params[1]+"\"" end } output_html += " class=\""+class_names.strip+"\"" if class_names.length > 0 output.print output_html += ">"+ar[1].strip close_tags << {'nest'=>nest, 'tag'=>"</"+ar[0].split(" ")[0]+">\n"} } close_tags.reverse_each{|elem| output.puts elem['tag'] close_tags.pop } input.close output.close
特徴
- 文字列の置換を一切行わないので、タグを書くのも自由。
- imgやbrなどを考慮していない。
- ただし、行頭の*を使うことでそのままタグを出力できるのでうまくやってください。
実行例
!<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> html xmlns=http://www.w3.org/1999/xhtml head *<meta content="text/html" charset="utf-8" http-equiv="Content-Type"/> title タイトル body a #examplelink onclick=func1(); リンク table border=1 .exampleclass tr td これ td は例 td です div .class1 .class2 #id0 b 太字にした div .class 1 <i>斜体にした</i>
出力
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><meta content="text/html" charset="utf-8" http-equiv="Content-Type"/> <title>タイトル</title> </head> <body><a id="examplelink" onclick="func1();">リンク</a> <table border="1" class="exampleclass"><tr><td>これ</td> <td>は例</td> <td>です</td> </tr> </table> <div id="id0" class="class1 class2"><b>太字にした</b> </div> <div class="class"><i>斜体にした</i></div> </body> </html>
出力されたHTMLの整形がひどい。ただ、余計なスペースや改行がタグの中に入らないように一応気を遣っている。
補足
bodyなどの広域なタグもこの記法で書くと、それに含まれるタグのネストが深くなってしまう(タブ(\t)を何回も打たないといけない)ので、bodyやheadなど、あまりいじらなさそうなところは行頭「!」でそのまま出力したらいいかと。
2011/07/20追記
Zen-Codingというのがあることを最近知った。