詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

いい感じに出力をインデントしてくれるモジュール #perl

[2013-11-20: Print::IndentedがCPANになくてしょんぼりしている。 とのことで shipit いたしました。 あざーす!]

過去のよく分からないコードの挙動を把握したいときには print (warn) するのが少なくともとりあえずの方法としては常套手段ですが、これがただ平坦に画面に現れるだけだと見づらい。例えば条件分岐やループの中にいる時は出力をインデントするようにすると動きが掴みやすくなるけれど、ただでさえ人間に単純労働を課す print デバッグにそこまで労力を使うのは辛い。そういう面倒さを減らそうという動機で、このモジュールは書かれました。

https://github.com/motemen/perl5-Print-Indented

一体何をするのかというと今書いた通りですが、print 時に、その print 関数の呼び出しコードのインデントに応じて、出力自身もインデントされるようになります。

つまり、

# a.pl
use strict;
use warnings;

warn 'blah starts';

for my $i (1..3) {
    warn "i: $i";
}

warn 'blah finished';

を普通に実行すると以下のようになりますが、

% perl a.pl      
blah starts at a.pl line 5.
i: 1 at a.pl line 8.
i: 2 at a.pl line 8.
i: 3 at a.pl line 8.
blah finished at a.pl line 11.

これが

% perl -MPrint::Indented a.pl
blah starts at a.pl line 5.
    i: 1 at a.pl line 8.
    i: 2 at a.pl line 8.
    i: 3 at a.pl line 8.
blah finished at a.pl line 11.

こうなる。

使い方

基本的には use Print::Indented するだけで、呼び出し元のパッケージから直接 STDOUT および STDERR に出力された文字列が、当該のソース行のインデントに応じてインデントされるようになります。例外として、main パッケージから import された時にはすべてのパッケージからの出力をインデントします。なので「とりあえず -MPrint::Indented する」、という使い方ができます。

ちなみに複数行の出力にも対応していて、

use strict;
use warnings;
use Data::Dumper;
use Print::Indented;

warn Dumper { a => 1 };

{
    warn Dumper { b => 2 };
}

$VAR1 = {
          'a' => 1
        };
    $VAR1 = {
              'b' => 2
            };

ってなります。

リポジトリ 

https://github.com/motemen/perl5-Print-Indented

どうぞご利用ください。

自動で cpanfile を記述する試み

[2013-10-31 追記]

tokuhirom ちなみに静的解析でいい場合は scan-prereqs-cpanfile がつかえますね。 http://b.hatena.ne.jp/tokuhirom/20131030#bookmark-167207818

おお、顧客が本当に必要だったものはこれですね! ありがとうございます!


適当に書きあげたスクリプトをそろそろ Carton 化するか〜と思っても cpanfile にいちいち書いてくのは面倒なので lib::xi 的なことをしながら cpanfile を組み立ててくれるコードを書いた。

このエントリの最後に貼ってあるファイルをカレントディレクトリに置いて次のようにスクリプトを実行する。

while ! carton exec -- perl -MCartonPacker foo.pl; do sleep 3; done

挙動は以下のようになってます。

  • require されたモジュールが見つからなかったら cpanfile に追記、carton install して exit 1 する(ので while で異常終了をみて再実行する)
  • carton install が失敗したらそのモジュール名を .cartonpackerignore というファイルに追記して cpanfile を戻す。このモジュールは以降 carton install を試行されなくなる。
  • モジュール名は一度 cpanm --info を通して正規化する。この過程で require されたモジュールと cpanfile に記述されたモジュール名が異なる場合も .cartonpackerignore に追記される。(HTML::TreeBuilder::XPath::Node とか)
  • なので、require を試みられるけど不要なモジュールは .cartonpackerignore にあらかじめ書いとくといい

この while を繰り返して最後にスクリプトが正常終了すると、cpanfile が出来上がっているはずです。あとは手で綺麗に仕上げてください。

雑なコードなんで雑な用途には有用だと思います。

CartonPacker.pm

To avoid "Wide character in print" warnings in Test::More

Today I happend to read perldoc of Test::More and learned:

The Solution

Put this line before use Test::More.

use open ':std', ':encoding(utf8)';

The Problem

use utf8;
use strict;
use warnings;
use Test::More 'no_plan';

pass 'L( ◔ω◔)┘三└( ◔ω◔)」';

subtest 'foo' => sub {
    pass 'L( ◔ω◔)┘三└( ◔ω◔)」';
};
Wide character in print at /Users/motemen/.plenv/versions/5.14.2/lib/perl5/5.14.2/Test/Builder.pm line 1759.
ok 1 - L( ◔ω◔)┘三└( ◔ω◔)」
Wide character in print at /Users/motemen/.plenv/versions/5.14.2/lib/perl5/5.14.2/Test/Builder.pm line 1759.
    ok 1 - L( ◔ω◔)┘三└( ◔ω◔)」
    1..1
ok 2 - foo
1..2

The Explanation

use open ':std', ':encoding(utf8)' adds encoding(utf8) Perl IO layer to both STDOUT and STDERR (and STDIN), which Test::Builder duplicates to its output handles.

The following code will do the same:

BEGIN {
  binmode STDOUT, ':encoding(utf8)';
  binmode STDERR, ':encoding(utf8)';
}
use Test::More;

Reference

はてなで一緒に働きませんか?