もう少し高度なカスタムメタクラス
これまで紹介してきた使い方を踏まえたうえで、より現実的に使えそうなメタレイヤのしくみを考えてみましょう。
オブジェクト定義から自動的にHTMLフォームへ
Webアプリケーションを書く場合、データをオブジェクトとして保持して、その内容をHTMLフォームに表示することがよくあります。
リスト15 では、任意のオブジェクトをHTMLフォームとして表示可能にする簡単なメタクラス・メタアトリビュートを作成しています。オブジェクトをHTMLとして表示するには、アトリビュート一つ一つをHTMLフォームの一部として表現できるようにする必要があります(ここでは話をシンプルにするため、すべてのアトリビュートはテキストないしパスワードフィールドとして表示されると仮定します) 。
リスト15 HTMLフォームのフィールドを表現するためのメタアトリビュート
package MyWAF::Attribute::Field;
use Moose;
use Moose::Util::TypeConstraints;
extends 'Moose::Meta::Attribute'; #
has field_type => ( #
is => 'ro',
isa => enum([ 'text', 'password' ]),
default => 'text',
);
has maxlength => ( #
is => 'ro',
isa => 'Int',
predicate => 'has_maxlength',
);
sub as_form_field { #
my $self = shift;
my $html = qq[<input];
$html .= qq[ name="] . $self->name . qq["];
$html .= qq[ required] if $self->is_required;
$html .= qq[ type="] . $self->field_type . qq["];
if ( $self->has_maxlength ) {
$html .= qq[ maxlength="] .
$self->maxlength . qq["];
}
$html .= qq{>\n};
return $html;
}
まずリスト15(1) でメタアトリビュートのサブクラスを作成し、2つのアトリビュートを定義します。リスト15(2) のfield_type
はこのアトリビュートをHTMLで表現する際に使えるフォーム種別(textかpassword) 、リスト15(3) のmaxlength
はフィールドのmaxlength
値を指定します。
リスト15(4) でこのアトリビュートを実際にHTMLで表現する際の変換メソッドas_form_field()
を定義します。 field_type
やmaxlength
、そしてMoose::Meta::Attributeにもともと定義されているis_required()
を参照しながらHTMLの一部を作成します。
Fieldアトリビュートを使う
このアトリビュートはMooseのhas()
のmetaclass
引数に指定するだけで使用できます(リスト16 ) 。リスト16(1) でメタクラスの名前をmetaclass引数に渡します。
リスト16 Fieldメタアトリビュートの使い方
package User;
use Moose;
has name => (
metaclass => 'MyWAF::Attribute::Field', #
is => 'ro',
isa => 'Str',
required => 1,
maxlength => 10,
);
has password => (
metaclass => 'MyWAF::Attribute::Field',
is => 'ro',
isa => 'Str',
required => 1,
field_type => 'password',
);
sub generate_form { #
my $self = shift;
my $html = '';
for my $attribute ($self->meta->get_all_attributes) {
$html .= $attribute->as_form_field();
}
print $html;
}
User->generate_form();
【出力】
<input name="name" required type="text" maxlength="10">
<input name="password" required type="password">
残念ながらこれだけだとHTMLの出力のところまでフックできていませんので、リスト16(2) のようなメソッドを追加する必要があります。もちろんこのメソッドも自動的に提供するようにメタクラスを作成することもできます。このようなメタクラスを含めたもっと現実的なサンプルとしては、CPANにあるHTML::FormHandlerなどを参照してください。
p5-mop─未来のMOP
これまでClass::MOPおよびMooseを前提として説明をしてきましたが、これらは外部依存とするにはかなり大きなツールです。そこで最近、Mooseの作者であるStevan Little さんとJesse Luehrs(DOY)さんが、Perl本体に組込みMOPを追加するp5-mop という提案を準備中です。p5-mopは「正しい」実装を求めてまだ設計段階ですので、利用可能なバーションがいつリリースされるかはわかりません。でも、これまでの進歩は期待を持てるものとなっています。
Perlの基本的なオブジェクト指向機能は17年も前にPerl 4からの互換性を保つという足かせを持ちながら生まれました。それ以降、ほかの言語、特にRubyはオブジェクト指向を相当進化させました。p5-mopで、Perlはまたそれらの言語と肩を並べられるようになります。
p5-mopが完成した暁には、CPANに登録されているさまざまなオブジェクト関連のモジュール類はそれぞれ別途に進化するのではなく、共通の技術の上に開発できるようになります。たとえばコマンドラインオプションをパースしてオブジェクトのアトリビュートに代入するモジュールは現在、Moose(MooseX::Getopt), Mouse(MouseX::Getopt), Moo(MooX::Getopt)とそれぞれのオブジェクトシステムごとに存在します。でもアトリビュートプロトコルが組込みで実装されれば、MOPレイヤでコマンドラインオプションと該当オブジェクトのアトリビュートを結び付けることができ、各オブジェクトシステムはそれを参照するだけでつなぎ込みが可能になります。
また現在のCPANモジュールは、さまざまなトリックを使って回避策を実装するしかない点がありますが、この問題をp5-mopは解決してくれる予定です。たとえばMooseでは「クラスを凍結」しないと若干のスピードロスが発生するため__PACKAGE__->meta->make_immutable()
を呼び出すことが推奨されています。当然ですがこのような明示的な呼び出しは忘れがちですので、自動的にフックが呼ばれるべきです。p5-mopでは、このような処理を行うためのフックも用意される予定です。
もしp5-mopに興味を持たれた場合は、ぜひフィードバックをしてください。
まとめ
MOPの使い方とMooseを拡張するいくつかのモジュールを解説しました。オブジェクト指向プログラミングに対するこのような考え方は年々重要性を増しています。p5-mopの開発もそうですし、さらに『The Art of the Metaobject Protocol』( 注1 )という教科書は、オブジェクト指向プログラミングを提唱したAlan Kay氏によって絶賛されています。
さあ! 次回の執筆者はcho45さんで、テーマは「最新Perl使いこなし!」です。お楽しみに!
[1] Gregor Kiczales , Jim des Rivieres , Daniel G. Bobrow: The Art of the Metaobject Protocol (The MIT Press, 1991)