SlideShare a Scribd company logo
CPAN の依存モジュール
をもう少し正しく検出し
たい
Kenichi Ishigaki
(@charsbar)
July 3, 2016
YAP(achimon)C::Asia
Hachioji 2016mid
Perl QA Hackathon
• April 21-24 @ Rugby, UK
http://act.qa-hackathon.org/qa2016/sponsors.html
• PAUSE
• CPANTS
https://www.flickr.com/photos/jj_perl/26591371285/in/album-72157667398548526/
PAUSE
• CPAN モジュールの登録・管
理
• 簡単な事前審査や静的解析
も
• 昨年から進めていた Plack へ
の移行は完了
• さらに近代化を進めたい
CPANTS
• 第三者目線のテスト (<-> CPAN::Testers)
• 体裁が適切かどうか ( 静的解析 )
• 2012 年に管理を引き継ぎ
• いろいろ問題が出てきていたので全面
的にリファクタリング
• 更新間隔が 1 日から 15 分に
prereq_matches_use
• 必要な依存モジュールが適切に宣言さ
れているか
( インストールには成功するんだけど場合に
よっては使うときに Can't locate FooBar.pm in
@INC エラーが出るようなモジュールを検出 )
• マシンを買い換えたとか Perl のバー
ジョンを上げたらコケるようになった
というときに真っ先にチェックしたい
項目のひとつ
• 静的インストールの検証にも
prereq_matches_use
package Archive::Any::Lite;
use strict;
use warnings;
use File::Spec;
...
package Archive::Any::Lite::Tar;
use Archive::Tar;
...
package Archive::Any::Lite::Zip;
use Archive::Zip …
requires:
Archive::Tar: '1.76'
Archive::Zip: '0'
File::Spec: '0'
File::Temp: '0.19'
IO::Uncompress::Bunzip2: '0'
prereq_matches_use
古くからあるコアモジュールは省略可能
package Archive::Any::Lite;
use strict;
use warnings;
use File::Spec;
...
package Archive::Any::Lite::Tar;
use Archive::Tar;
...
package Archive::Any::Lite::Zip;
use Archive::Zip …
requires:
Archive::Tar: '1.76'
Archive::Zip: '0'
File::Spec: '0'
File::Temp: '0.19'
IO::Uncompress::Bunzip2: '0'
prereq_matches_use
現状では 2 割くらいに問題がある
•META の更新漏れ
•新しいコアモジュールの指定漏れ
•残念ながら CPANTS 側に問題があること
も
Module::ExtractUse
• Thomas Klausner さん作
(2003 年~ 2008 年、 2012 年~ )
• 不要なものを除去したあとセミコロン
単位で行を分割
• use などのキーワードを見つけたら再帰
下降パーサ (Parse::RecDescent) で解析
たいていの場合は
これで十分
Moose の仲間には未対応
ちょうど開発中断していた頃に活発
化
•Moose (2006 年~ )
•Mouse (2008 年~ )
•Moo (2010 年~ )
ヒアドキュメントの
中身を見てしまう
my $template = <<'END';
use Module;
...
END
環境依存のモジュールを
無視してくれない
use Test::More;
BEGIN {
if ($^O ne 'MSWin32') {
plan skip_all => 'for Win32 only';
}
}
use Win32::Module;
...
ヒアドキュメントや plan
skip_all の後も除去?
CPANTS 的には取り過ぎるよりは取れ
ない方が害は少ない
( 表に出てこない高速化モジュールな
どがあるので、 META 側の情報が多
い分には問題ない )
META の自動生成などに
は使いづらくなる
BEGIN {
if ($^O ne 'MSWin32') {
plan skip_all => 'for Win32 only';
}
}
# Win32 環境では入れてほしい
use Win32::Module;
どこまで正確さを
求めるかも悩まし
い
厳密に言うなら BEGIN
の有無もチェック必要
BEGIN {
if ($^O ne 'MSWin32') {
plan skip_all => 'for Win32 only';
}
}
use Win32::Module;
本気で解析するなら、
セミコロン分割から直すべ
き
BEGIN {
use Test::More;
if ($^O ne 'MSWin32') {
plan skip_all => 'for Win32 only';
}
}
use Win32::Module;
ハッカソンの枠で
は収まりそうにな
い
他にもいろいろな課題が残っていたので
…ついつい後回しに
プロファイルを取ってみ
た
ファイルまわりの問題が出てくることを期待し
ていた
Pod::Checker
分析部分だけ取り出してみ
る
Module::ExtractUse
Parse::Local
Distribuiton
Pod::Checker
分析部分だけ取り出してみ
る
Module::ExtractUse
Parse::Local
Distribution
Parse::RecDescent Pod::Strip
Pod::Checker
Module::ExtractUse
Parse::Local
Distribution
Parse::RecDescent Pod::Strip
どう見ても次のターゲット
…は
高速化だけで
お茶を濁す手も
• Pod::Strip を外す
• 毎回パーサを生成する必要はないはず
• 再帰下降パーサを別のものに切り替え
る (Parse::RecDescent は AUTOLOAD を使って
いるので遅くなりがち )
• これだけでも新しいテストの検証などは楽に
なる
見てしまったからに
は
仕方ない
いまやらなかったら、
次いつ直せるの?
代わりになりそうな
ものを探すことにし
た
当然ですよね
ふたつの異なる要件
サーバ側
•Module::ExtractUse と同程度の速さはほしい
•fork() などに対応していて欲しい
•Perl のバージョンは気にしない
ユーザ側
•速さは気にしない
•Test::Kwalitee 対応するなら Perl 5.8.1 が必須
(Lancaster Consensus)
•現状テストには DB が必要ということになって
いるので、サイト専用のテストに移行する手も
Perl::PrereqScanner
• Ricardo Signes さん作 (2010 年~ )
• Dist::Zilla から派生
• PPI ベース (2001 年~ )
• Moose 、 if 、 VERSION メソッドなどに
も対応
• リポジトリには追加プラグインの PR も
Perl::PrereqScanner
• 事実上の評価基準 : これでできること
ができないとツライ
• 遅すぎて大量のバッチ処理には向かな
い
( 当初はこれしかなかったから移行を断
念 )
Perl::PrereqScanner::Lite
• moznion さん作 (2013 年~ )
• WEB+DB PRESS Vol.81/82
(Perl Hackers Hub 第 27/28 回 )
• Compiler::Lexer ベース (goccy さん作 )
• 非常に高速 (P::PS の 20 ~ 30 倍 )
• Moose と VERSION メソッドに対応
• App::scan_prereqs_cpanfile などで利用
Perl::PrereqScanner::Lite
I have to redesign and
reimplement this
module, and I have
some plans. (GH#13)
Perl::PrereqScanner::Lite
• Compiler::Lexer が不安定
( 大量処理時に SEGV)
• 拡張性の問題
( トークンをそのまま処理している )
• 今年に入ってから FAIL が多発 (GH#15)
( おそらく version モジュールの変更によるも
の )
Perl::PrereqScanner::
NotQuiteLite
• 拙作 (2013 年~ )
• Compiler::Lexer ベース
• 生トークンは隠蔽
• Perl::PrereqScanner と Module::ExtractUse
のテストを通るように改造
• Plack::Builder にも対応
Perl::PrereqScanner::
NotQuiteLite
• 個人的なモジュール管理には重宝
• これも大量処理時には不安定
• Compiler::Lexer …を差し替えたい
Perl::Lexer
• tokuhirom さん作 (2013 年~ )
• Perl 本体の解析器を利用
• ( 理屈としては ) これ以上正しいものは
ない
• 同期が取れなくなる心配もない
Perl::Lexer
THIS LIBRARY IS WRITTEN
FOR RESEARCHING PERL5
LEXER API. THIS MODULE
USES PERL5 INTERNAL API.
DO NOT USE THIS.
Perl::Lexer
• 繰り返し使えるようには ( まだ ) なって
いない
• Perl 5.18.1 → 5.10.0
• Devel::Declare の手法が参考にできるか
も
Text::Balanced
• Damian Conway さん作 (1997 年~ )
• Perl 5.7.3 から標準添付
• 文字列、括弧、正規表現などを ( 正規表
現で ) 取り出せる
• Parse::RecDescent の中でも使われている
Text::Balanced
• 開発は停滞
( 実質的には 2006 年で止まっている )
• 最近の Perl についていけていない箇所
も
(Perl 5.14 で導入された修飾子など )
• 括弧の対応がとれていないものに弱い
• 補助的なたたき台としては使えそう
Perl::Tokenizer
• スライドを書いている最中に発見
• Daniel Șuteu さん作 (2015 年~ )
• 正規表現ベース
• 内部で状態管理
• 非常に高精度
• …書き始める前に見つけていたら
Perl::Tokenizer
• Perl 5.18
• 解釈の部分は自前で実装必要
• 効率面でもベンチマークで確認必要
すぐに代用できる
…ものはなさそう
というわけで、
作ることにした
作成方針
• トークンは(基本的には)正規表現で
抽出
• 正規表現などの塊の抽出はひとまず
Text::Balanced のコードを流用
• スコープの情報などは適宜状態変数に
格納していく
頑張りすぎない
• 欲しいのは依存モジュールの解析器
• モジュール名とバージョンを取れれば
いい
• 変な書き方まで検出できる必要はない
print "@{[require Module]}";
s/YA8C/use Module/e;
困ったときは perl
のソースを見る
• perlop などはあくまで人間向けの説明
• 正しい仕様を確認したいときは perly.y
や toke.c 、 keywords.h などを見る
• …あまり読めていませんが
最初の目標
• まずは CPAN 全体のトークナイズに挑戦
• 中期的には Perl::PrereqScanner::NotQuiteLite の
置き換え
( ここまでできれば、 BEGIN の処理などの追
加はそれほど苦労せずにできるはず )
基本的な構造
while (...) {
# 字句解析
if ($code =~ m/G($re_whitespace)/gc) { ...; next; }
elsif ($code =~ m/G($re_keyword)/gc) { ... next; }
...
elsif ($code =~ m/G($re_anything_else)/gc) { die; }
last;
} continue {
# 構文解析
}
正規表現を補う道具
my $pos = pos($code);
my $c1 = substr($code, $pos, 1);
my $c2 = substr($code, $pos, 2);
if ($c1 eq '@') {
if ($code =~ m/G($re_array)/gc) { ... next; }
else { pos($code) = $pos + 1; ... next; }
}
先読みに失敗したら
戻せるように
# 内部で先読みしていく
if (match_regexp($code)) {
... next; # 正規表現の抽出に成功
} else {
# 解釈に失敗したら保存してある位置まで
戻す
pos($code) = $pos;
}
トークンの収集は
最小限に
• 関係ない行のトークンは集めても無駄
• オブジェクトにするのはもっと無駄
• 登録済みのキーワードを見つけたらトークン
収集開始
• メソッドの呼び出し元だけは事前に保存して
おく
Module->VERSION(1.50)
• ブロックのスタックや条件文の存在は別途保
名前空間や数字は
扱いやすい形に
• use などの引数を解釈するときに同じよ
うな処理は書きたくない
• 一度の正規表現で読み切れるならそれ
にこしたことはない
• 必要ない字句までまとめる必要はない
テスト
• きれいなコードをパースできるのは当
然
• 仕様を片手にあれこれテストコードを
考えるより、何が出てくるかわからな
い現実のコードでテストする方が確実
( 頑張りすぎない )
WorePAN
my $worepan = WorePAN->new(
use_minicpan => 1,
)->walk(sub {
my $dir = shift;
$dir->recurse(callback => sub {
my $file = shift;
return unless $file =~ /.pm$/;
scan($file);
});
});
# 最新版をテストし続けるのには便利
先に解凍しておく
• 毎回解凍するのは時間の無駄
• WorePAN では問題があるファイルを
消せない
• もともと壊れているモジュール
• Perl6 モジュール
エラーが出たファイ
ルだけを詳細に分析
• テスト環境にあわせてデバッグモード
を切り替える
• 問題を解決できたらテストコードに追
加
• 解析できないモジュールは対象から消
したり、例外に追加したり
悩まされた
コードたち
• アポストロフィ
• Unicode
• 一見キーワード
• 無名サブルーチン
• プロトタイプ
• print $fh
• // と /
• /#/x
• qr))
• eval ''.''
1. アポストロ
フィ
• 文字列をくくるだけではありません
• Perl 4 時代の名前空間の区切り文字とし
てもおなじみ
&jcode'convert($value, 'sjis');
• Acme::Don't (Damian Conway さん作 )
名前空間を一度で
…取ろうとしたら
m/G(w+(?:(?:'|::)w+)*)/gc
キーワードはあらかじ
…め避けておく必要が
if (ref($hashOrInternalId) eq'HASH') {
PHRED/WebService-NetSuite-0.04/lib/WebService/NetSuite.pm
ヒアドキュメント
…と
our $SIGNATURE_GRAMMAR = << '#'END';
...
#'END
GAAL/Perl6-Signature-0.04/lib/Perl6/Signature.pm
…特殊変数と
my $list = shift @'_;
my $sep = @$list <= 1 ? '' : do {
...
SPROUT/CSS-DOM-0.16/lib/CSS/DOM/PropertyParser.pm
サブルーチン名にまで
…
sub O'o { [ shift,oO( @_ ) ]->[!$[] }
TYEMQ/Acme-ESP-1.002007/ESP.pm
2. Unicode
package Acme::Lambda;
use 5.008;
use warnings;
use strict;
use utf8;
...
*λ = &lambda;
NELHAGE/Acme-Lambda-0.03/lib/Acme/Lambda.pm
2. Unicode
use utf8;
package Acme::_;
{
$Acme::_::VERSION = '0.006';
}
BEGIN {
$Acme::_::AUTHORITY = 'cpan:ETHER';
}
# ABSTRACT: send warnings with _
ETHER/Acme-LookOfDisapproval-0.006/lib/Acme/o_o.pm
2. Unicode
• マルチバイト文字が泣き別れしないよ
うに
• use utf8; を見つけたら全体を decode
• 解析前には BOM のチェックも
3. 一見キーワー
ド
sub thx ($) {
my ($str) = @_;
$INLINE->use if $INLINE;
SATOH/Text-Xatena-0.18/t/lib/Text/Xatena/Test.pm
3. 一見キーワー
ドpush @{$data->{stack}}, {
in => (caller(1))[3] || '-',
package => (caller(1))[0] || '-',
sub => (caller(2))[3] || '-',
filename => (caller(1))[1] || '-',
line => (caller(1))[2] || '-',
};
STEVEB/Devel-Trace-Subs-0.22/lib/Devel/Trace/Subs.pm
3. 一見キーワー
ド
{ } の中に空白がない場合は特別扱い
$reg->{ y } = ( $reg->{ y } - 1 ) & 0xff;
BRICAS/Games-NES-Emulator-0.03/lib/CPU/Emulator/6502/Op/DEY.pm
4. 無名サブルーチン
enable_if sub { fingerprinted(@_) },
'Header', set => ['Expires' => MAX_DATE];
INGY/Cog-0.11/lib/Cog/Runner.pm
map {} ... なども同じ理由で地雷原
5. プロトタイプとアトリ
ビュートPerl には $) …という特殊変数があるのですが
return (
....
$>, # uid
$), # gid
...
);
MSISK/Net-Nmsg-0.15/lib/Net/Nmsg/Layer.pm
5. プロトタイプとアトリ
ビュート
プロトタイプなどは ( ) の組を最優先
sub run_test ($) {
AGENT/Makefile-DOM-0.008/t/Shell.pm
6. ファイルハンドル? 変
数?
print $dest_fh <<__EOJS__;
...
__EOJS__
DAMI/Alien-GvaScript-1.44/GvaScript_Builder.pm
6. ファイルハンドル? 変
数?
print $dest_fh << 1;
...
1
print $dest_fh << 1;
どうやって区別?
7. // と /
srand(((time/$$)^($>*time))/(time/(time^$$)));
foreach (1..$length){
$letter = pack("c", rand(128));
redo unless $letter =~ /[a-zA-Z]/; # I just don't
like w, okay?
$word .= $letter;
}
JONG/Bioinf_V2.0/Bioinf.pm
7. // と /
合わせ技
正規表現は変数の直後には来ませんが
…
print $fh /$var/ ? 1 : 0;
print $fh // $var;
print $var{foo} / 1;
map {...} /$var/ ? 1 : 0;
8. コメントと正規
表現
m{# trying to cheat with cpants game ;)
use strict;
use warnings;
}x;
MONS/AnyEvent-SMTP-0.10/lib/AnyEvent/SMTP/Client.pm
8. コメントと正規
表現入れ子がおかしくなる場合
$rest =~ s{
b # start at word boundary
( # begin $1 {
$urls : # need resource and a colon
[$any] +? # followed by on or more
...
) # end $1 }
...
}{<A HREF="$1">$1</A>}igox;
AUTRIJUS/Pod-HtmlHelp-1.1/WinHtml.pm
8. コメントと正規表
現次の文字がデリミタになるんじゃなかったの?
unless ($$this =~ s
# {use (warnings|strict)...)K}
{use warnings...)K}
{$begin_block}s)
{
die("Could not add a begin block.n");
}
WINTRU/Carrot-1.1.309/lib/Carrot/Modularity/Package/Source_Code.pm
8. コメントと正規表
現どちらのコメント?
$regexp=qr{
....
# (?{
# my $pos=pos;
# my $prev=substr($str, $pos-10, 10);
# my $post=substr($str, $pos, 10);
# print "emitted at position ",pos,...;
# })
}xs;
OPI/HTML-YaTmpl-1.8/lib/HTML/YaTmpl/_parse.pm
8. コメントと正規表
現
単一行だけど x の有無で意味が変わる場
合
if ($token =~ m/[ # matches [ /x) {
DYLUNIO/Gwybodaeth-0.02/lib/Gwybodaeth/Parsers/N3.pm
9. …括弧の数が
if(my $base_elem = $doc->look_down(_tag
=> 'base', target => qr)(?:)))){
$name = $base_elem->attr('target');
}
SPROUT/WWW-Scripter-0.031/lib/WWW/Scripter.pm
9. …括弧の数が
かっこをひとつ変えるとエラーに
Unmatched ) in regex; marked by <-- HERE in
m/(?:)) <-- HERE
if(my $base_elem = $doc->look_down(_tag
=> 'base', target => qr((?:)))){
10. eval: 文字列の中身
は不完全なコードかも
$c->{constraint} = eval 'sub { no strict
qw/refs/; return defined &{"match_'.
$c->{constraint}.'"}(@_)}';
MARKSTOS/Data-FormValidator-4.66/lib/Data/FormValidator/Results.pm
3 ヶ月ほどの試行錯誤
でトークナイズは
ほぼできるように
• 例外として登録したモジュール 6 個
• 109 個のテスト
• 偶然解析できているものや意味的に間
違っているものが残っている可能性は
あり
古い perl の問題
• なぜか $c1 に 2 文字入ることがあ
る
( 初期の 5.8 系列 )
• 取る順序をひっくり返すと直る
my $c1 = substr($code, $pos, 1);
my $c2 = substr($code, $pos, 2);
古い perl の問題
巨大で複雑な文字列を正規表現にかける
と落ちることがある
(5.8 系列すべて )
TOKUHIROM/lib/Amon2/Setup/Asset/Bootstrap.pm
現状と今後の予定
リポジトリ ( 予定
地 )https://github.com/charsbar/Perl-
PrereqScanner-NotQuiteLite
ご清聴ありがと
うございました

More Related Content

PPTX
CMSとPerlで遊ぼう
PPT
2017年夏のPerl
PPT
2017年春のPerl
PPT
How to debug a perl script using gdb
PPTX
php7's ast
PPTX
PHP AST 徹底解説(補遺)
KEY
モダンmod_perl入門 #yapcasia
PDF
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題
CMSとPerlで遊ぼう
2017年夏のPerl
2017年春のPerl
How to debug a perl script using gdb
php7's ast
PHP AST 徹底解説(補遺)
モダンmod_perl入門 #yapcasia
『How to build a High Performance PSGI/Plack Server』のその後と ISUCON3を受けての話題

What's hot (20)

PDF
Everyday Life with clojure.spec
PDF
最近の PHP の話
PDF
Ruby 2.5
PDF
VarnishではじめるESI
PDF
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
PPTX
PHP AST 徹底解説
PDF
OPcache の最適化器の今
PPTX
PHP と SAPI と ZendEngine3 と
PDF
about Perl5.10
PDF
PHPの今とこれから2021
PDF
PDF
Zabbix API
PPT
サーバー実装いろいろ
PPTX
php-src の歩き方
PDF
omoon.org の裏側 〜FuelPHP の task 活用例〜
PDF
15分でCakePHPを始める方法(Nseg 2013-11-09 )
PDF
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
PDF
PHP5.5新機能「ジェネレータ」初心者入門
PDF
PHPの今とこれから 2013
PDF
Varnish 4.0 Release Party in Tokyo発表資料
Everyday Life with clojure.spec
最近の PHP の話
Ruby 2.5
VarnishではじめるESI
Norikraで作るPHPの例外検知システム YAPC::Asia Tokyo 2015 LT
PHP AST 徹底解説
OPcache の最適化器の今
PHP と SAPI と ZendEngine3 と
about Perl5.10
PHPの今とこれから2021
Zabbix API
サーバー実装いろいろ
php-src の歩き方
omoon.org の裏側 〜FuelPHP の task 活用例〜
15分でCakePHPを始める方法(Nseg 2013-11-09 )
日本 GNU AWK ユーザー会チラシ - OSC2012 Tokyo/Fall
PHP5.5新機能「ジェネレータ」初心者入門
PHPの今とこれから 2013
Varnish 4.0 Release Party in Tokyo発表資料
Ad

Similar to CPANの依存モジュールをもう少し正しく検出したい (20)

KEY
EC-CUBE + PHPUnit で 実践テスト駆動開発
PDF
おまえらこのライブラリ使ってないの? m9 (2013-07)
PDF
関西Php勉強会のlimeの話
PDF
MoteMote Compiler Plugin
PDF
Capistrano in practice - WebCareer
PDF
Ruby Postgres 2009
PDF
あんなテスト、こんなテスト(this and that about testing)
PDF
私とOSS活動とPerl
PDF
恋に落ちるデプロイツール
PDF
qmake入門
PDF
How to run P4 BMv2
PDF
Apacheの展望とmod_perlの超絶技巧 #yapcasia
PDF
behatエクステンションの作り方
PDF
Niigata.pm #1
PPTX
Php in ruby
PDF
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
PDF
20170127 JAWS HPC-UG#8
PDF
Haikara
PDF
Write good parser in perl
PDF
EC-CUBE + PHPUnit で 実践テスト駆動開発
おまえらこのライブラリ使ってないの? m9 (2013-07)
関西Php勉強会のlimeの話
MoteMote Compiler Plugin
Capistrano in practice - WebCareer
Ruby Postgres 2009
あんなテスト、こんなテスト(this and that about testing)
私とOSS活動とPerl
恋に落ちるデプロイツール
qmake入門
How to run P4 BMv2
Apacheの展望とmod_perlの超絶技巧 #yapcasia
behatエクステンションの作り方
Niigata.pm #1
Php in ruby
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
20170127 JAWS HPC-UG#8
Haikara
Write good parser in perl
Ad

More from charsbar (20)

PPT
Common boolean class_for_perl5
PPT
2018年夏のPerl5
PDF
萬國之津梁
PDF
Better detection of what modules are used by some Perl 5 code
PPT
Json(::PP) is a-changing
PPT
2016年のPerl (Long version)
PPT
JSON, JSON::PP, and more
PPT
perl language update
PDF
2013年のCPANモジュール作成事情
PDF
What you need to remember when you upload to CPAN
PPT
On UnQLite
PPT
typemap in Perl/XS
PDF
Analyze CPAN, Analyze Community
PDF
Annual Report 2012
PDF
DBD::SQLite
PDF
CPANTS: Kwalitative website and its tools
PPT
CPANTS 2012
PPT
Revisiting ppm
PDF
Mojolicious::Liteを使ってみよう
PPT
変数、リファレンス
Common boolean class_for_perl5
2018年夏のPerl5
萬國之津梁
Better detection of what modules are used by some Perl 5 code
Json(::PP) is a-changing
2016年のPerl (Long version)
JSON, JSON::PP, and more
perl language update
2013年のCPANモジュール作成事情
What you need to remember when you upload to CPAN
On UnQLite
typemap in Perl/XS
Analyze CPAN, Analyze Community
Annual Report 2012
DBD::SQLite
CPANTS: Kwalitative website and its tools
CPANTS 2012
Revisiting ppm
Mojolicious::Liteを使ってみよう
変数、リファレンス

CPANの依存モジュールをもう少し正しく検出したい