=encoding utf8 =pod =head1 題名 Moose::Cookbook::Meta::Recipe2 - メタアトリビュート、ラベル付きのアトリビュート =head1 概要 package MyApp::Meta::Attribute::Labeled; use Moose; extends 'Moose::Meta::Attribute'; has label => ( is => 'rw', isa => 'Str', predicate => 'has_label', ); package Moose::Meta::Attribute::Custom::Labeled; sub register_implementation {'MyApp::Meta::Attribute::Labeled'} package MyApp::Website; use Moose; has url => ( metaclass => 'Labeled', is => 'rw', isa => 'Str', label => "The site's URL", ); has name => ( is => 'rw', isa => 'Str', ); sub dump { my $self = shift; my $dump = ''; my %attributes = %{ $self->meta->get_attribute_map }; for my $name ( sort keys %attributes ) { my $attribute = $attributes{$name}; if ( $attribute->isa('MyApp::Meta::Attribute::Labeled') && $attribute->has_label ) { $dump .= $attribute->label; } else { $dump .= $name; } my $reader = $attribute->get_read_method; $dump .= ": " . $self->$reader . "\n"; } return $dump; } package main; my $app = MyApp::Website->new( url => "http://google.com", name => "Google" ); =head1 要約 このレシピからは、不思議なメタプログラミングの世界に深入りしていきます。読者の中には「そんなのはいかれまくった変態開発者だけの世界だ」と冷笑する方もいるかもしれませんが、断じてそんなことはありません! それなりにいかれた開発者であれば、メタを利用すれば多大な恩恵を受けられます。 ここでの目標は、それぞれのアトリビュートに人が読んでわかるような「ラベル」を追加することです。このようなラベルがあれば、エンドユーザにデータを見せるときに利用できます。このレシピでは、Cアトリビュートに「このサイトのURL」というラベルをつけ、そのラベルの使い方を紹介する簡単なメソッドを作ります。 =head1 メタアトリビュートオブジェクト Mooseベースのオブジェクトのアトリビュートは、実際にはすべてオブジェクトであり、メソッドやアトリビュートを持っています。具体的な例を見てみましょう。 has 'x' => ( isa => 'Int', is => 'ro' ); has 'y' => ( isa => 'Int', is => 'rw' ); 内部的には、CのメタクラスはLを2つ持っています。メタクラスからメタアトリビュートを取り出す方法はいくつかありますが、そのひとつにCというメソッドがあります。このメソッドはメタクラスオブジェクトから呼べます。 Cメソッドはアトリビュート名とオブジェクトがマッピングされたハッシュリファレンスを返します。今回の場合、Cはこのようなハッシュリファレンスを返すはずです。 { x => $attr_object_for_x, y => $attr_object_for_y, } また、Cを使うと単独のLを取り出すこともできます。メタアトリビュートオブジェクトを取り出したら、このようなメソッドを呼ぶことができます。 print $point->meta->get_attribute('x')->type_constraint; => Int アトリビュートにラベルを追加するには、2つの手順を踏む必要があります。まず、アトリビュートのラベルを保存できる新しいアトリビュートメタクラスが必要です。また、そのアトリビュートメタクラスを利用するアトリビュートを作成する必要があります。 =head1 レシピのおさらい まずは新しいアトリビュートメタクラスから作っていきます。 package MyApp::Meta::Attribute::Labeled; use Moose; extends 'Moose::Meta::Attribute'; Mooseのメタクラスもほかのものをサブクラス化するのと同じやり方でサブクラス化できます。 has label => ( is => 'rw', isa => 'Str', predicate => 'has_label', ); これもまた標準的なMooseのコードです。 続いて、Mooseを使って私たちのメタクラスを登録する必要があります。 package Moose::Meta::Attribute::Custom::Labeled; sub register_implementation { 'MyApp::Meta::Attribute::Labeled' } ここではちょっとした魔法を使って、新しいメタクラスを参照するときに「Labeled」という短い名前を使えるようにしています。 これでアトリビュートメタクラスはおしまいです。 今度はアトリビュートメタクラスを使ってみましょう。 package MyApp::Website; use Moose; use MyApp::Meta::Attribute::Labeled; ほかのPerlのクラスと同様に、メタクラスを使うにはロードする必要があります。 いよいよアトリビュートにメタクラスを適用します。 has url => ( metaclass => 'Labeled', is => 'rw', isa => 'Str', label => "The site's URL", ); これは一見ふつうのアトリビュート宣言ですが、2つ異なっているところがあります。CパラメータとC