2008年10月14日(火) 11:22

はじめてのPerlモジュール開発メモ

ネットで資料を漁り、独学で覚えたのでこれで本当にあっているのか不安がいっぱい。
誰かのツッコミを期待してまとめを書いておきます。

ねらい

Perlモジュールを作成して配布する手順をひと通り辿る。
過程で、もろもろ便利なカスタマイズはたくさんありますが、今回はなるべく寄り道しない方向で。


参考サイト


開発環境

Vine Linux4.2 + Perl5.8.6


作成モジュール

WWW::Hatena::IconURL(一応、完成品は coderepos にあげました。)


はてなid を渡すと、アイコンの画像URLを返すモジュールを作成する。
(モジュールの有用性は無視してください。あくまで具体性を持ったサンプルという位置づけですので)


モジュールいろいろ

perlモジュールといっても、いろいろあります(勝手分類)。

  • 自分だけが利用する内輪モジュール
  • 配布できることを念頭に置いた一般モジュール
  • 広く利用されるCPANモジュール

やはり、配布をしたり、CPANに上げたりするとなると、テストやpodに気を使わなければならないであろう割合が多くなるかと思います。たくさんの人に使ってもらうならなおさら。だけど、いきなりそうしたモジュールを作成するのはハードルが高くて失敗の元。
しかし、もともとモジュールは汎用性を高めるために作成するもので、内輪のオレオレモジュールを動かしても面白みが薄い。どうせなら配布も出来るレベルにしたい。誰かに使ってもらえれば何らかのフィードバックが得られることもあるだろうし。
(会社でモジュール書いてるなら内輪モジュールでも意見がもらえるかもしれませんが、オイラのような孤独なコーダーはネットにコードを放つしか術がない)


モジュール雛形作成ツールいろいろ

Perlモジュールはイチから作成しなくても雛形作成ツール(モジュール)がたくさん作られています。

  • h2xs
    たぶん基本。しかし、今となっては古いのかも。
     
  • Module::Starter
    module-starterというコマンドでモジュール作成。
     
  • Module::Starter::PBP
    Module::StarterのPBP作法版。Build.PL(Module::Build) とか perlcritic.tとか
     
  • pmsetup
    そうとう便利そう。でもまだ使ったことない。。。
     
  • Module::Setup
    pmsetupをyappoさんがモジュール化したもの
     

ガシガシモジュールを書くようになると、きっと誰もが pmsetup に行き着くのだろうけど、たぶん最初は h2xs か Module::Starter(::PBP) ではじめた方が良いのではないかと思います。基本を学ぶという点において。
h2xs はすごくシンプルな雛形が出来上がって一式そろえる手間は少ないですが、雛形のモダンさで Module::Starter::PBP に軍配が上がるのかと思います。


というわけで、今回は Module::Starter::PBP を使ってモジュールをこさえていきます。


関連モジュールのインストール

では、モジュール作成で必要になるCPANモジュールのインストールから。

# cpan -i Module::Starter
# cpan -i Module::Starter::PBP


以下のモジュールも入れておきます。

# cpan -i Module::Build
# cpan -i Test::Pod
# cpan -i Test::Pod::Coverage
# cpan -i PPI
# cpan -i Test::Perl::Critic
(うちは cpan -if PPI じゃないとダメだった。)


Module::Starter::PBPセットアップ

以下のように、太字部分を入力して Module::Starter::PBP のセットアップをする。

perl -MModule::Starter::PBP=setup
Creating ~/.module-starter/PBP...done.
Creating ~/.module-starter/PBP/t...done.
Creating ~/.module-starter/config...
Please enter your full name: Your Name
Please enter an email address: hoge@example.com
Writing ~/.module-starter/config...
done.
Installing templates...
        ~/.module-starter/PBP/Build.PL...done
        ~/.module-starter/PBP/Makefile.PL...done
        ~/.module-starter/PBP/README...done
        ~/.module-starter/PBP/Changes...done
        ~/.module-starter/PBP/Module.pm...done
        ~/.module-starter/PBP/t/pod-coverage.t...done
        ~/.module-starter/PBP/t/pod.t...done
        ~/.module-starter/PBP/t/perlcritic.t...done
Installation complete.
#


上記のセットアップで、~/.module-starter/PBP/ 以下に雛形の元となるファイルが書き出されるので、2点ほど(小さな)修正を加えます。


Module.pm の修正

まず、~/.module-starter/PBP/Module.pm のバージョン記述に our を追加しておきます。

use version; $VERSION = qv('0.0.3');
  ↓↓↓
use version; our $VERSION = qv('0.0.3');


Makefile.PL の修正

そして、~/.module-starter/PBP/Makefile.PL を以下の内容に修正します。

use lib qw(lib);

use Module::Build::Compat;

Module::Build::Compat->run_build_pl(args => \@ARGV);
Module::Build::Compat->write_makefile(build_class => 'Module::Build');


モジュール雛形作成

雛形の元となるファイル修正が終わったら、モジュール作成の準備完了。


雛形はカレントディレクトリ以下に書き出されるので、モジュールを開発するディレクトリに予め移動して、以下のように module-starter コマンドを入力します。

# module-starter --module=WWW::Hatena::IconURL


以下のようなファイルとディレクトリ群が作成されます。

WWW-Hatena-IconURL/Build.PL
WWW-Hatena-IconURL/Changes
WWW-Hatena-IconURL/MANIFEST
WWW-Hatena-IconURL/Makefile.PL
WWW-Hatena-IconURL/README


WWW-Hatena-IconURL/lib/WWW/Hatena/IconURL.pm


WWW-Hatena-IconURL/t/00.load.t
WWW-Hatena-IconURL/t/perlcritic.t
WWW-Hatena-IconURL/t/pod-coverage.t
WWW-Hatena-IconURL/t/pod.t

この段階で、中身は何もありませんが、一応動く WWW::Hatena::IconURL モジュールが出来上がっています。


Build

コードはまだ何も書いていませんが、モジュールの体裁は整っているので、すかさず Build してみます。

# cd WWW-Hatena-IconURL
# perl Build.PL
# perl Build
# perl Build test

Build が無事終われば、あとは開発に集中するだけです。
もし Build のどこかで失敗したら、これまでの手順を見直してやり直してみてください。
モジュールが足りない(Module::Build、Test::Pod、Test::Pod::Coverage、PPI、Test::Perl::Criticなど)か、雛形のテンプレートの修正で失敗しているのだと思います。


テスト

Build test まで成功したら、テストを書き、そのテストをクリアできるようにコーディングしていきます。テストファーストとかテスト駆動開発とかいうアレです。テスト重要。
よっぽど、モジュール雛形作成ツールの進化はテストの進化でもあるような気がするほどで、雛形時点で作成されるテストの内容(数)がいかにテストが重要かということを物語っていると思います。


さて、しかしながら、テストの書き方についてここで書ききるのは無理なので、以下の参考ページを挙げておきます。


WWW::Hatena::IconURL は、idを渡してコンストラクタを生成する「new」、idを確認する「id」、そしてメインのアイコン画像URLを生成する「icon」「icon_s」というメソッドを実装します。


そのためのテストが以下。


[/t/01.basic.t]

use Test::More tests => 3;
use WWW::Hatena::IconURL;

my $id2url = WWW::Hatena::IconURL->new("genkivogue");

# can get id?
is($id2url->id,
    'genkivogue', "id");

# can get icon URL?
is($id2url->icon,
    'http://www.hatena.ne.jp/users/ge/genkivogue/profile.gif', "icon");
is($id2url->icon_s,
    'http://www.hatena.ne.jp/users/ge/genkivogue/profile_s.gif', "icon_s");


01.basic.t はもちろん、t ディレクトリに作成しておきます。


テストコードができたら、せっかくなので、実行してみます。

# perl Build test
t/00.load.........ok 1/1# Testing WWW::Hatena::IconURL 0.0.3
t/00.load.........ok
t/01.basic........Can't locate object method "new" via package "WWW::Hatena::IconURL" at t/01.basic.t line 5.
# Looks like your test died before it could output anything.
t/01.basic........dubious
        Test returned status 255 (wstat 65280, 0xff00)
DIED. FAILED tests 1-3
        Failed 3/3 tests, 0.00% okay
t/perlcritic......ok
t/pod-coverage....ok
t/pod.............ok
Failed Test  Stat Wstat Total Fail  List of Failed
-------------------------------------------------------------------------------
t/01.basic.t  255 65280     3    6  1-3
Failed 1/5 test scripts. 3/7 subtests failed.
Files=5, Tests=7,  1 wallclock secs ( 1.59 cusr +  0.12 csys =  1.71 CPU)
Failed 1/5 test programs. 3/7 subtests failed.
#


下線部の通り、追加したテスト 01.basic.t が失敗しています。
エラーメッセージは「Can't locate object method "new" via package "WWW::Hatena::IconURL" at t/01.basic.t line 5.」とあり、WWW::Hatena::IconURL に new メソッドがないということです。


そりゃ失敗します。new メソッドはまだ書いていませんから(もちろん、他のメソッドも)。


モジュールのコーディング

というわけで、テストをクリアするべくコードを書きます。
今回はモジュールを作り上げる過程がメインなので、コードの詳細には触れません。
ソースはこちらで参照してください。


ポイントは以下のような点です。

  • PBP準拠のコーディング(use strict;など)
  • PODを書く(メソッドは説明ナシでも最低限列挙する必要がある(_ではじまるインターナルメソッドは除く))


モジュールの規模が大きくなれば他にもいろいろと配慮するべき点は増えますが、今回はこの程度です。
コーディングできたら再びテストを実行してクリアできるまで繰り返します。


テストにOKした場合。

# perl Build test
Copying lib/WWW/Hatena/IconURL.pm -> blib/lib/WWW/Hatena/IconURL.pm
t/00.load.........# Testing WWW::Hatena::IconURL 1.0.0
t/00.load.........ok
t/01.basic........ok
t/perlcritic......ok
t/pod-coverage....ok
t/pod.............ok
All tests successful.
Files=5, Tests=7,  3 wallclock secs ( 2.33 cusr +  0.12 csys =  2.45 CPU)
#


その他のファイルの編集

README、Changes を編集します。
コードの動作には関係ないところですが、それなりに。
まあ、雛形そのまんまの場合もよく見かけますが。
中身についてはたぶん説明不要だと思うので、割愛。


Build.PL の編集

コードができたら、Build.PL の requires に使用モジュール(依存モジュール)を追加します。
うっかり忘れると、モジュールが未インストールな環境でテストがこけます(ちゃんと書いておけばインストールを促してくれます)。


    requires => {
        'Test::More' => 0,
        'version'    => 0,
    },
 ↓↓↓
    requires => {
        'Test::More' => 0,
        'version'    => 0,
        'URI'       => 0,
    },


MANIFEST の編集

MANIFESTファイルは、配布アーカイブに含めるファイルの一覧です。
すぐに MANIFEST.SKIP → perl Build manifest を利用するようになると思いますが、今回は手動で編集します。


キモは、開発時にしか必要のないテストをはずしたり、 Build(make)してできるディレクトリとその中身やファイルをコピーしないという点です(Build(make)すると/blib/ができたりいろいろコピー不要なものが生成されているので)。
必要なファイルのみ列挙します。


今回の MANIFEST

Build.PL
Changes
MANIFEST
Makefile.PL
README
lib/WWW/Hatena/IconURL.pm
t/00.load.t
t/01.basic.t


インストール

インストールしてみます。


Build test まで終わっているとして、

perl Build install


makeなら

# perl Makefile.PL
# make
# make test
# make install


Perlのmakeフェーズの挙動 も読むと役に立つと思います。


配布アーカイブの作成

# perl Build dist


上記コマンドで、
モジュール配布用アーカイブ WWW-Hatena-IconURL-1.0.0.tar.gz が作成されます。


# perl Build disttest


Build disttest(make disttest)で、動作確認。
問題が起きなければ、以上で、おつかれさまです。


さいごに

ここまでの手順で、ゼロから配布モジュールを作成できるようになったと思います。
モジュールの作成は .pm を作ればそれでいいのかと思っていたら大間違いで、自分以外の誰かや、例えばモジュールを書いてしばらく時間が過ぎ、モジュールの中身を忘却した自分に向けて、一定の作法にのっとったモジュールを形にしておくというのは大事だなあと思うこのごろ(pod があればたいてい問題ないのかもだけど)。


というわけで、
32歳IT業界経験なし無職が頑張ってここまでやってみたメモでした(´ν`)
CPAN Author への道は遠い。。。

このエントリーに最初のコメントを書いてみませんか?

名前  クッキーに保存 
エントリーナビ
はじめてのPerlモジュール開発メモ」は、
Perl, Perlモジュール カテゴリーの記事です。


関連エントリー
ポケベル世代の数字暗号化モジュール Acme::CryptPB
ピアノのコード表をテキスト出力するText::Chord::Piano書いた
ピアノのコード表を出力するGD::Chord::Piano
Perlの標準モジュール確認君
CPANメモ

トラックバック
このエントリーへトラックバックするURL (言及リンクのないトラックバックは無効です)