2008-06-29
Catamooseのinterview
Catalyst |
![]()
http://jjnapiorkowski.vox.com/library/post/catamoose.html
moose版catalystについての、いいinterviewですね。折角なので要点だけまとめておきます。
要点は、以下の5点くらい。
特にMoose RoleをPluginにという点とBread::BoardでPlugin(Component含む)郡のwiringにという2点が興味深いなと思いました。
Moose Roleについて
Moose Roles are a good fit for plugins, and will hopefully clarify some of the problems we used to have with people creating Catalyst plugins when that functionality was better off created as a base controller or model.
わかることは、
- MooseのRoleをPluginとして使うのが適切だ
- ControllerやModelとして作ったものがよいものについても、MooseのRoleになる可能性があるということ(世間一般でPluginといわれているものではないですが、見え方としてはPluginになるっていうことでしょう)
Bread::Boardについて
Matt Trout clarified the possible use of Bread::Board in a future version of Catalyst: “Bread::Board is going to replace the custom code in setup_components, it's -not- a replacement for the plugin system. Although we may extend Bread::Board to compose class+N roles classes for extensions, sort of like a more large-scale MooseX::Object::Pluggable.”
large-scale MooseX::Object::Pluggableというのは的確な表現だなぁと。複雑なオブジェクトの依存関係を解決してwiringしてやる役割をBread::Boardでもたしてやるって話なわけで、DIコンテナの現実的なユースケースだろうなぁと。
# どこまでがcatamooseの最初のリリースで入るんでしょうね。Moose RoleをPluginにってとこまで入ってくれるといいなぁ。
XIRCDでのMooseX::POEの使い方
Moose |
![]()
MooseX::POEの使い方の例として、XIRCDを少し見てみました。
http://coderepos.org/share/browser/lang/perl/XIRCD/trunk/
以下、メモです
- Component
- MooseX::POEを使う
- new時にPOCO::XXXをspawan
- POE系の冗長な記述を簡易に書く仕組み
- Sub::Exporterでpostなどなどをexportする使い方はとても綺麗
- get_argsも同様に短く書けていい
- Moose系
- MooseX::Daemonize, MooseX::POE, MooseX::POE::Aliased
ポイントは、以下の3点かなと思いました。
- MooseX::POEを使うことでPOE::Sessionうんぬんの記述を無くす事
- ComponentでPOEの冗長な記述を簡易に記述できるように関数をexportすること
- MooseX::Daemonizeでdaemonの記述が簡易になっている点
これだけコードがシンプルにできると、POEのコードも少し書いてみようかなぁなどと思えてきます。
まだ、MooseX::POEは全然触ってないのですが、ぱっと見た感じでは先ほどのやつをMoose化するのは、さほど難しいところはなさそうです。MooseX::POEのコードが読み終わったら移植してみます。
CPANモジュール作成の手順
今年はCPANモジュールを何かリリースできればなぁと思っているのですが、CPANモジュール作成の手順を理解していないので少しまとめてみました。
- ひな形作成
- 開発時
- 依存モジュール追加
- Makefile.PLのrequires, build_requires拡張
- Test::Dependenciesでチェック
- http://subtech.g.hatena.ne.jp/hirose31/20080612/1213271089
- モジュールに付属するスクリプトの追加
- share ファイルや、スクリプトのCONFファイル等があれば
- install_share 'conf';
- 依存モジュール追加
- テスト
- リリース前
- リリース
色々と手順は抜けているような気がするので、このステップは絶対にないといけない!とかあったほうがいい!というステップがあれば、是非教えてください。
# id:dayflowerさんにテストが抜けてるよ!というツッコミをもらったので追記してみました。間違いがあればツッコミおねがいします!
# id:dankogaiさんの指摘から、prove -bvについて追記しました。blib
See also:
- Perlのmakeフェーズの挙動
- CPANTSのチェックリスト
- CPANize
Shipitでgithubにリリース
steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist git.tagpattern = release-%v git.push_to = origin
git.tagpatternでtagの付け方を指定、git.pushtoでpushする先を指定といった感じです。これで、tag付けして、commitしてくれます。
初めてshipitを触ってみたのですがこれはリリース手順を簡単に自動化できていいですね。ShipitのStepの拡張も簡単にできそうなので、リリースツールの拡張はShipitベースでやるのがよさそうです。
See also:
MooseX::AttributeHelpersでArrayRef、HashRefのアクセッサを簡単に作成する
Moose |
![]()
Moose標準だと、ArrayRefのattributeを作ってそれをラップするメソッドを書く事になりますが、これは面倒です。そんなときに、使うのがMooseX::AttributeHelpersです。
attributeを拡張してくれます。これで、arrayrefにaliasのようなものを追加できるようになります。以下は、urlsのaccessorにurlをaddするメソッドを提供する例です。
providesに書いたadd_urlというメソッドがarray refのpushに対応します。HashRefなども同様の形で簡単に追加できるので、array, hashへのラッパメソッドを書いている人は、これを使ってみるといいかと思います。
ArrayRefの場合
#!/usr/bin/env perl use Data::Dumper; { package Crawler; use Moose; use MooseX::AttributeHelpers; has 'urls' => ( metaclass => 'Collection::Array', is => 'ro', isa => 'ArrayRef[Str]', default => sub { [] }, provides => { 'push' => 'add_url', } ); } my $crawler = Crawler->new; $crawler->add_url('http://www.google.co.jp'); $crawler->add_url('http://www.yahoo.co.jp'); warn Dumper $crawler->urls;
実行結果
$VAR1 = [
'http://www.google.co.jp',
'http://www.yahoo.co.jp'
];
HashRefの場合
#!/usr/bin/env perl
use Data::Dumper;
use Perl6::Say;
{
package Stuff;
use Moose;
use MooseX::AttributeHelpers;
has 'options' => (
metaclass => 'Collection::Hash',
is => 'ro',
isa => 'HashRef[Str]',
default => sub { {} },
provides => {
'set' => 'set_option',
'get' => 'get_option',
'empty' => 'has_options',
'count' => 'num_options',
'delete' => 'delete_option',
}
);
}
my $stuff = Stuff->new;
$stuff->set_option('option1' => 'value1');
$stuff->set_option('option2' => 'value2');
say 'option1 value: ' . $stuff->get_option('option1');
say Dumper $stuff->options;
$stuff->delete_option('option1');
say Dumper $stuff->options;
say 'option nums: ' . $stuff->num_options;
say 'has option?: ' . $stuff->has_options('option2');
実行結果
option1 value: value1
$VAR1 = {
'option1' => 'value1',
'option2' => 'value2'
};
$VAR1 = {
'option2' => 'value2'
};
option nums: 1
has option?: 1
POEでIRC bot
Yokohama.pmで、kanさんの発表を聞いて、MooseX::POEはなかなか良さそうだと思い、少し使ってみようと思っていたのですが、そもそもPOEのことを全然わかってないので、POE::Component::IRCを使って簡単なbotを作ってみました。
http://github.com/dann/yaircbot/tree/master
bot用のコマンドは、Shell::Amazon::S3で使ったCommand拡張の仕組みを使ったので、Commandは簡単に拡張できるようになっています。YAIRCBot::Command以下にファイルを置けば、Commandが追加できるようになっています。
さて、POEを触ってわかってことですが、
- Componentはeventをhookするだけで実装できるようになっている。定型処理はcomponent側で実装されている
- 引数はPOE::Sugar::Argsで。生で扱うと引数がとても汚くなる
- $poe->kernel->postなどと全体的に処理が長くなる
- デバッグは、以下のようにしてKernelのデバッグを出しておくと、少しやりやすい
sub POE::Kernel::ASSERT_DEFAULT () {1} sub POE::Kernel::TRACE_DEFAULT () {1}
- POE::Componenet::IRCをspwanしてから、POE::Kernel->runというのが定型処理
大体処理の流れはわかったので、次はMooseX::POEを使って、Mooooooooooooose化して遊んでみようかなと思ってます。今はお遊びスクリプトですが、最終的にはIRC botを簡単に作る仕組みをMooseベースで作ってみたいかなと。
起動スクリプト
#!/usr/bin/env perl use strict; use warnings; use FindBin::libs; use YAIRCBot; my $nick = 'commandbot'; my $server = 'localhost'; my $port = 6667; my $bot = YAIRCBot->new( nick => $nick, server => $server, port => $port ); $bot->run; __END__
main class
package YAIRCBot; use Moose; use POE::Component::IRC; use POE::Session; use POE::Kernel; use YAIRCBot::Component::IRCClient; use YAIRCBot::CommandDispatcher; our $VERSION = '0.01'; has 'nick' => ( isa => 'Str', is => 'rw', ); has 'server' => ( isa => 'Str', is => 'rw', ); has 'port' => ( isa => 'Int', is => 'rw', ); sub run { my $self = shift; POE::Component::IRC->spawn( alias => 'bot', nick => $self->nick, server => $self->server, port => $self->port, ); POE::Session->create( inline_states => { _start => \&YAIRCBot::Component::IRCClient::irc_start, irc_001 => \&YAIRCBot::Component::IRCClient::irc_001, irc_public => \&YAIRCBot::Component::IRCClient::irc_public, irc_error => \&YAIRCBot::Component::IRCClient::irc_reconnect, } ); POE::Kernel->sig( INT => sub { POE::Kernel->stop } ); POE::Kernel->run; } 1; __END__
Component
package YAIRCBot::Component::IRCClient; use strict; use POE qw( Sugar::Args Component::IRC ); use YAIRCBot::CommandDispatcher; use Perl6::Say; #sub POE::Kernel::ASSERT_DEFAULT () {1} #sub POE::Kernel::TRACE_DEFAULT () {0} sub parse_input { my ($line) = @_; my @tokens = split( /\s/, $line ); return unless @tokens >= 1; my $command = shift @tokens; return ( $command, \@tokens ); } sub execute_command { my ( $command, $args ) = @_; my $dispatcher = YAIRCBot::CommandDispatcher->new; my $result = eval { $dispatcher->dispatch( $command, $args ); }; return $result; } sub irc_start { my $poe = sweet_args; $poe->kernel->post( bot => register => 'all' ); $poe->kernel->post( bot => connect => {} ); } sub irc_001 { my $poe = sweet_args; $poe->kernel->post( $poe->sender => join => '#test' ); } sub irc_public { my $poe = sweet_args; my $who = $poe->args->[0]; my $where = $poe->args->[1]; my $what = $poe->args->[2]; my $nick = ( split /!/, $who )[0]; my $channel_name = $where->[0]; my $self = $_[0]->[OBJECT]; if ( $what =~ /\!(.*)/ ) { my $command_line = $1; my ( $command, $tokens ) = parse_input($command_line); my $result = execute_command( $command, $tokens ); $poe->kernel->post( $poe->sender => notice => $channel_name => $result ); } } sub irc_connected { say 'irc_connected'; } sub irc_error { my $p = sweet_args; my ($error) = @{ $p->args }; say 'irc_error:' . $error; } sub irc_reconnect { say 'irc_reconnect'; } 1;
先生! テストまわりについて書いてありません :)
あと make disttest とか distclean とか realclean とか。
+ prove -bv t/hoge.t # でないとXSに対応しない