#!/usr/bin/env ruby =begin = rweave.rb RDによる((<文芸的プログラミング|URL:http://www.csl.sony.co.jp/person/masui/OpenPOBox/wtangle/wtangle.html>))を支援するツール。 == 概要 文芸的プログラミング。 これは、プログラムと同じファイルにドキュメントを書くことで常に整合性の取れたプログラムとドキュメントを作成する手法。 同じファイル、それもプログラムの該当部分に近いところにドキュメントを記述するために、プログラムと同時にドキュメントも((*手軽に*))修正することが可能。 ((<増井さんのアプローチ|URL:http://www.csl.sony.co.jp/person/masui/OpenPOBox/wtangle/wtangle.html>))では一つのHTMLにプログラムとドキュメントを書いているが、ここではRDを使うことにする。 もともとRDはRubyスクリプトに埋め込むために作られたので、RubyスクリプトにRDを埋め込むだけでも文芸的プログラミングと言うこともできるが、ここではもう一つ踏み込んだアプローチを取る。 HTMLにプログラムとドキュメントを書く場合は「プログラムを含んだ((*ドキュメント*))」である。 対してRubyスクリプトにRDを埋め込む場合は「ドキュメントを含んだ((*プログラム*))」となる。 前者の場合、ドキュメントそのものをウェブにアップでき、かつ一つのファイルからすべてのソースファイルを取り出すことができるという利点がある反面、プログラムを書き換えるときはそのプログラムを含んだHTMLを書き換えないといけないので、書き換えるたびにHTMLからプログラムを取り出す手間がある。 後者の場合はそのまま実行できるが、ソースが複数ある場合ドキュメントを完成させるためには複数のソースファイルが必要になってしまう。 ただ、Rubyのようなインタプリタ言語の場合はすぐに実行できた方が嬉しいし、せっかくRDという書きやすいドキュメント体系があるのだから俺はこっちを選択する。 それにソースファイルが一個の小さなプログラムの場合はその欠点はなくなる。 ドキュメントソースすなわちプログラムのファイルが複数ある場合、ドキュメントを結合する順序に気を配らないといけないが、結合する順序を((<テンプレート>))に記述すればよい。 == インストール すみません、いろんなツールに依存してます(^^;; === 必要なもの * GNU make (ほぼすべてのUnixには標準でinstallされているだろう)((-Cygwinのmakeはいずこに?-)) * (()) * (()) * (()) === あればいいもの * (()) * (()) === インストール方法 #(1) RDtool, rd2html-ext, ERbをインストールする。 # それぞれの説明を読んでインストールしよう。 (2) GNU make。 * RDtoolを自分でmakeした人はすでに入っているだろう。 #(3) rd.makを ~/include に置く。 (4) sendcmd.rbを LOAD_PATH の通ったディレクトリに置く。 == 使い方 === 基本的な使い方 使い方は次の2ステップになる。 (1) 出力するRDの((<テンプレート>))を生成する。 これは最初の一回だけやればよい。 * 方法1:index.templateを編集する (1) $ rweave.rb (2) 生成されたindex.templateで$src_filesを編集する。 * 方法2:ソースファイルを指定して起動 (1) $ rweave.rb init src... (2) HTMLを生成する。 * $ rweave.rb === テンプレート テンプレートは(()) RDとなっている。 いじるとすれば「#### config」部分の $src_files 変数くらいだろう。 その変数はRD(が含まれたファイル)を結合する順にファイルを並べている配列である。 そこをいじることでRDを結合する順番を変更することができるし、新たにRDを加えることもできる。 「#### rd_part」以下はHTMLをどのように出力するかを書いている。 === カスタマイズ setupメソッドに記述してある変数は ~/.rweave に書くことで好きなように設定できる。 HTML出力形式を変えたり、Makefileをいじったりするときには ~/.rweave に書けばいい。 見ての通り、setupメソッドの最後に ~/.rweave が読み込まれる。 =end require 'sendcmd' require 'erb/erbl' class RWeave < SendCmd def setup @dot_rweave = "~/.rweave" @makefile = "rweave.mak" @rd_mak = "~/include/rd.mak" @files_list = "files.list" @rd_template_filename = "index.template" @rd_filename = @rd_template_filename.sub(/template$/, 'rd' ) @html_filename = @rd_template_filename.sub(/template$/, 'html' ) ################ RD @rd_template = '" #### config <% $src_files = %w[ #{ARGV.join %Q[\n]} ] create_makefile; makerds %> #### rd_part <%= rd_part( $src_files ) %> #### source " "=begin == ソースコード <% $src_files.each do |src| %> * ((<<%=src %>|URL:<%=src %>>)) <% end %> #### result <% unless result_files.empty? %> == 実行結果 <% result_files.each_with_index do |res,i| %> <% if result_files.length > 1 %> == 結果その<%= i+1 %> <% end %> <%= make_verbatim( res ) %> <% end # each%> <% end # unless%> " "=end\n"' ################ makefile @makefile_template = " RWEAVE = ruby <%= $0 %> RD2 = rd2 RDVisitor = rd/rd2html-lib.rb RD2HTMLOPT = -r $(RDVisitor) --with-part=html:include --out-code=jis RD2HTML = $(RD2) $(RD2OPT) $(RD2HTMLOPT) src = <%= $src_files.join ' ' %> rds = <%= rds.join ' ' %> results = <%= Dir['result.*'].join ' ' %> <%= @html_filename %>: <%= @rd_filename %> <%= @rd_filename %>: $(rds) $(results) <%= @rd_template_filename %> $(RWEAVE) makerd rdpart: $(rds) %.rdw: % $(RWEAVE) rb2rd $< > $@ %.html: %.rd $(RD2HTML) $< > $@ || rm -f $@ #include <%= @rd_mak %> rweaveclean: rm -f $(rds) #{@makefile} rweaverealclean: rweaveclean rm -f #{@rd_template_filename} " open( File::expand_path @dot_rweave ) {|f| eval f.read } rescue nil end =begin == サブコマンド : rb2rd スクリプトからRD部分を切り出して結合する。 =end def rb2rd( inf, outf ) inrd = false while inf.gets case $_ when /^=begin/ inrd = true outf.print when /^=end/ inrd = false outf.print else outf.print if inrd end end end def cmd_rb2rd( file ) open( file ) do |inf| rb2rd( inf, $> ) end end =begin : rb2rb スクリプトからRubyスクリプトのみを抜き出す。 =end def rb2rb( inf, outf ) inrb = true while inf.gets case $_ when /^=begin/ inrb = false when /^=end/ inrb = true else outf.print if inrb end end end def cmd_rb2rb( file ) open( file ) do |inf| rb2rb( inf, $> ) end end def create_makefile #template rds = $src_files.collect{|x| x+".rdw" } open( @makefile, "w" ) do |mak| mak.print ERbLight::new( @makefile_template ).result( binding ) end $stderr.puts "created #{@makefile}." end =begin : template : init テンプレート(eRuby RD)を出力する。 =end def cmd_template( *src_files ) open( @rd_template_filename, "w" ) do |outf| outf.print eval @rd_template end $stderr.puts "created #{@rd_template_filename}." end alias :cmd_init :cmd_template def rd_part( src_files ) rds = $src_files.collect{|x| x+".rdw" } rds.collect {|rdw| open( rdw ) {|f| f.read } }.join end def result_files Dir[ 'result.*' ].sort{|a,b| x=a.sub(/^result\./,'').to_i y=b.sub(/^result\./,'').to_i x <=> y } end def template open( @rd_template_filename ) do |f| ERbLight::new( f.read ).result( binding ) end end def xsystem( str ) $stderr.puts str system str end def makerds xsystem "make -k -f #{@makefile} rdpart" end def make_verbatim( file ) File::readlines( File::expand_path file ).collect{|line| " #{line}"} end =begin : make : makerd スクリプトよりRDとHTMLを生成する =end def cmd_make open( @rd_filename, 'w' ) do |rd| rd.print template end xsystem "make -k -f #{@makefile}" end alias :cmd_makerd :cmd_make =begin : clean テンプレート以外の生成したファイルをすべて削除する。 =end def cmd_clean xsystem "make -k -f #{@makefile} rweaveclean" end =begin : realclean テンプレートも含め生成したファイルをすべて削除する。 =end def cmd_realclean xsystem "make -k -f #{@makefile} rweaverealclean" end =begin : default(引数なしで起動したとき) * テンプレートが存在しないときは、テンプレートを作成。 * 存在するときはRDとHTMLを生成。 =end def cmd_default unless File::exist?( @rd_template_filename ) cmd_template $stderr.puts "!!Edit #{@rd_template_filename}!!" else cmd_make end end end RWeave.new =begin == 既知のバグ * スクリプト上で複数行にわたる文字列やヒアドキュメント内に 「=begin」や「=end」があるとそれぞれRDの開始、終了と誤認識してしまう。 解決するにはRubyスクリプトを字句解析しないといけない。 =end