Perl スクリプトファイルの中に埋み可能なドキュメンテーションフォーマット。 ドキュメンテーションコメントに使われる。
仕事で書いた Perl モジュールの API 仕様を紙媒体にしなければならないので、今回は pod2latex を使ってみる。
pod2latex -modify -full -out hoge-utf8.tex lib
で lib ディレクトリ以下の *.pm の Pod が集められて hoge-utf8.tex になる。ソースを UTF-8 で書いているのでこれを lv などで EUC-JP に変換して、platex してdvipdfmx。
前に Javadoc から LaTeX に落とした時に比べて非常にすんなりと変換してくれる。 '=item foo([opt])' とかすると ] 文字が落ちたりする以外は特に今のところ目につく問題なし。
ま、いざとなったらスクリプトに手を入れて直せるだろうし、なかなかいいんじゃないでしょうか。
依存モジュールが多くなってきて、開発しているPerl モジュールの実行環境構築が面倒になってきた。
ActivePerl では PPM パッケージ化 + PPM リポジトリで芋蔓式インストールが可能である。 素のPerlだとCPANモジュールでネットワークインストールする事になる。 ここで一個づつインストールしていくのがかったるい。 ということでCPANにあるモジュールのように Bundle::* を作る事にした。
調べてみると簡単。
フォーマットは以下。
Module_Name [Version_String] [- optional text]
これだけ。Pod に書かせるあたり、とりあえずから始まった感じである。
CPAN上に置あるものはきちんと tarball 化してあるが、ローカルで使う分にはこの bundle Perl モジュールを @INC のどこかに置いておけばよい。
Bundle::MyModule を作ったとすると perl -MCPAN -e shell から 'install Bundle::MyModule' でOK。
カレントディレクトリの下に Bundle/MyModule.pm を置いて
perl -I . -MCPAN -e shell
として Bundle::MyModule をインストールしようとしたのだがうまくいかない。CPAN のシェル上の ! コマンドで @INC を出力してみると . が含まれていない。何故? PERL5LIBに設定しても同様。 試行錯誤したところ絶対パスで指定すればOKであった。
CPAN.pm 1.76_01 を読んでみた。
no lib "."; # we need to run chdir all over and we would get at wrong # libraries there
これだ。
一昨日インストールしたMing 0.3 beta 1 + Perl でいろいろ実験。 Ming 関連は資料が少ない。
SWF::setVersion(6) をしても吐き出されるバージョンは5。 SWF::Movie の Pod に書いてある
new SWF::MovieWithVersion(6)
はエラー。
結局 xs ファイルを見て、
SWF::Movie::newSWFMovieWithVersion(6);
なら呼べてバージョン6形式で吐けることを確認。
テキストフィールドの onChanged イベント処理のテスト。
SWF::TextField インスタンスに setName で名前を設定して ActionScript から onChanged イベントハンドラを設定するも、ウンともスンとも言わない。 設定した名前に文字列を代入すると表示が書き換えられるはする。 はて。
どうやらMacromedia Flash ではテキストフィールドにインスタンス名と変数名があるようだ。 setName で設定した名前はどっちだったのか? どうやら変数名っぽいのだが。
また手元の本によると onChanged はインスタンス名を指定するらしい。 Ming ではどうする?
いろいろいじったところ、SWF::Movie に add した後の返り値である SWF::DisplayItem オブジェクトに対して setName し、ActionScript でこの名前のインスタンスに対して onChanged にハンドラを与えたところうまく動くようになった。 ということはこれがインスタンス名なのか?
プロジェクトの後半で納品用ドキュメントの整備を始めるのだが、その時はたいがいもう切羽詰りはじめていて構成やら体裁やらマネジメントやらを工夫する余力が無かったりする。 ついつい(次回は改良しようと思っていつも思っている)前回のプロジェクトの手法を踏襲してしまいがちだ。 ともすれば劣化コピーになりかねない。
やはり、忙しくても日頃からの改善は重要である。
最近はアジェンダ・議事録・開発メモなどを、積極的に Wiki や Subversion で共有するようにし、その点では以前より改善してきている。
今後はさらに、出荷ドキュメントのレビュープロセスなどを確立し品質を高めていきたいところである。 現状でもチームメンバでのピアデスクチェックやパスアランドを非形式的に行っているのだが、「チェックの程度」やその後の「修正」および「修正の確認」については、まだなんとなくやったかなという具合。この辺りを工夫したい。
先月発売されていて気になっていた「開発の現場 Vol.003」に、何かヒントがあるかなと思って買ってみた。
パラパラと見た感じではテクニカルライティングの話はあまりなく、主にソフトウェア開発における中間成果物としてのドキュメントや開発者間ドキュメントをどうとりまとめていくかという話が中心のよう。 Wiki による開発資料のライトな共有など、うちのチームでも進めている話など。
「(最初から)完全なドキュメントを書こうとしない」というのはもっとも。 状況はほとんどの場合変わるし、最初の段階では未確定の部分も多い。 だからといって、いつまでたっても手元で温めていてもしょうがない。
技術的な話では Perl の Pod を活用しようという話。 Perl 以外の言語のコメント中に Pod 形式でドキュメントを書こうという提案や、Apache で動的に Pod ドキュメントを整形しようという話とか。
テキストフォーマットとしての Pod は =over / =item / =back によるリスト表現など、最近のフォーマットに比べてすごく読み易いわけではないが、たしかに他の言語のコメントに埋め込んでおいて処理するのは、標準の Pod 関連のモジュールでできるな。
自分も Pod でドキュメントを書くけれど、(Perl 以外は) 個人的には reStructuredText にしたいと考えている。 ただ Pod みたいに他のテキストの一部に埋め込んでその部分のみ処理する記法およびツールがが標準の reStructuredText / Docutils には見当らない。 実はどっかにあるのだろうか。
[ 読書ノート ]
WiKicker のソースコードを人に説明するのにプリントアウトして説明するのに、doxygen のようなツールが欲しいのだけれど Perl 用のものはないのかな。
というのが希望。1 だけなら結構いろいろなツールがあり、1 + 2 なら perltidy で実現できる。 しかし 3、4 までしてくれるツールが見つけられない。
とりあえず perltidy の Perl::Tidy と File::Find で再帰的にまとめて HTML に変換するスクリプトだけは書いて、一気に変換だけはできるようにしておいた。
インデックスの作成までは面倒なので未着手。
Perl プログラムでテキストファイル処理を 改行コード CR/LF/CRLF 全対応にしようと思ったが、書こうとするとこれが結構面倒臭いことに気がつく。
$/ に正規表現が設定できないため、<FILEHANDLE> で単純に3パターン対応ができない (LF と CRLF に対応とかならすぐできる)。
小さいファイルと仮定して良いなら全部読み込んで自前で行分割、大きいファイルならまじめにバッファリングして改行コードをスキャンして行処理するのが正攻法かな。 多少効率悪くなりそうだけれど。
Perl 5.8.8 の Pod::Html (1.0504) だと pod2html の中で $/ = "" と設定して、パラグラフ単位で読み込んでそのあと処理している。
最近の Perl であれば PerlIO::crlf、 PerlIO::eol あたりが使えそうである。
もちろん Perl 5.005_03 だと NG。
ちょっとしたものをのぞいて、Perl プログラムはアプリケーション部分も App 的 Perl モジュール(.pm)に入れて、実行するスクリプトファイル (.pl) では use して new して run するだけにしている。
#!/usr/bin/perl use warnings; use strict; use MyApp; exit MyApp->new->run;
@ARGV の処理は new から呼ばれているプライベートメソッドの中で local @ARGV してから、Getopt::Long::GetOptions あたりで解析処理をしている。
でテストスクリプトではこの MyApp を use_ok して local @ARGV = qw(引数組み合わせパターン...) した後に new を呼んだ結果を検査するようにしていた。
そんなところ今回「.pm の中で @ARGV いじるのやっぱり気持ち悪くない?」っていう意見をもらった。気持ちはわかる。ただ
MyApp->new(@ARGV)->run;
のように .pl 側で受け取ったものを解析すれば?」いいかというと、直接 @ARGV を使わない Getopt::Long::GetOptionsFromArray は 2.36 からしかなくて、古いバージョン の Getopt::Long でも動くようにすると結局 local @ARGV になってしまうのである。
また「@ARGV の解析は .pm の方には入れたくない」っていう意見もあったので、今回はそれらのコードは .pl 側に追い出すことにした。
そうすると次の課題は .pl にある @ARGV の解析処理のテストはどう書けばいいかなと。.pl ファイルを require したらスクリプトが走っちゃうし。試行錯誤していたらマスタリング Perl で紹介されている caller(0) を使う方法を教えてもらった。
スクリプトファイル(.pl) #!/usr/bin/perl package App; # スクリプトの実行開始エントリ sub main { # ここに実行したい処理呼び出しを書く。 } main() unless caller(0);
こうするとこのスクリプトファイルを直接実行した場合は main サブルーチンが実行され、他から require された場合は main は実行されないというようにすることができる。前者の場合は caller(0) は undef を返し、後者は 'main' (あるいその他のパッケージ名)が返されることを利用している。
これでテストファイルの中でこのファイルを require_ok できるようになるので、あとは各サブルーチンのテストを書けばよい。
それから Pod::Usage を使っているんだけれど、テストでは Pod::Usage::pod2usage で exit されてしまうと困るので exit が何もしないようにしておく。テストスクリプトでは以下のようにする。
BEGIN { *CORE::GLOBAL::exit = sub {}; require_ok('script/myscript.pl'); }
これで myscript.pl のテスト中に pod2usage が exit を呼んでもスルーさせられる。 ただし本番では exit させたい pod2usage がある場合は、その後に制御が流れていっても問題がおきないようにプログラムを書いておく必要があるので注意。
それから pod2usage で期待するメッセージが STDERR に吐かれているかをテストするには STDERR をオープンしなおせばよい。
{ my $message; local *STDERR; open STDERR, '>', \$message or die $!; # ここで pod2usage がメッセージを出すことが期待されるテストを実行。 like($message, qr/expected message/); }
以上、スクリプトのテストについての何点かのまとめ。
TLS と認証が必要な SMTP サーバ経由でメールを送信する以前書いたスクリプトをちょっと整理した。標準入力からメッセージ本文を受けとって送信できるように機能を切り出し。
メールでコンテンツを投稿できるサービスにメールを投げる時用。
標準入力で受け取った本文を Email::MIME でメールの形式に変換して、Email::Sender::Simple を使って送信。transpot として Email::Sender::Transport::SMTP::TLS を使う。なお、今 Email::Sender::Transport::SMTP::TLS の Pod ドキュメントをみたら今後は Email::Sender::Transport::SMTPS の方がお薦めと書いてあったのでその方が良いのかも。
#!/usr/bin/perl use warnings; use strict; use utf8; use open IN => ':utf8'; use open ':std'; use Email::MIME; use Email::Sender::Simple qw(sendmail); use Email::Sender::Transport::SMTP::TLS; eval { my $message; { local $/ = undef; $message = <>; } my $subject = "sendXXX: $message"; $subject = substr($subject, 0, 32 - 3) . '...' if length($subject) > 32; my $new_mail = Email::MIME->create( header_str => [ From => 'me@example.com', To => 'post@example.com', Cc => 'me@example.com', Subject => $subject, ], attributes => { content_type => 'text/plain', charset => 'UTF-8', encoding => '8bit', }, body_str => $message ); sendmail( $new_mail, { from => 'me@example.com', transport => Email::Sender::Transport::SMTP::TLS->new( { host => 'smtp@example.com', port => 587, username => 'myusername', password => 'mypassword' } ) } ); }; if ($@) { open my $file, '>>', '/home/me/var/log/sendXXX/error.log' or die $!; print $file $@, "\n"; close $file; exit 1; } exit 0;
[ Perl モジュール ]
Naney (なにい)です。株式会社ミクシィで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。