Perlのための完全にモダンなオブジェクトフレームワーク
A complete modern object framework for Perl
package Person;
use strict;
use warnings;
sub new {
my ($class) = @_;
return bless {
name => '',
age => undef,
}, $class;
}
sub name {
my ($self, $name) = @_;
$self->{'name'} = $name if $name;
return $self->{'name'};
}
sub age {
my ($self, $age) = @_;
$self->{'age'} = $age if $age;
return $self->{'age'};
}
1;
package Person;
use Moose;
has name => (is => 'rw');
has age => (is => 'rw');
1;
use Moose;
use strict; use warnings;
@ISA = qw(Moose::Object) unless @ISA
has
はアトリビュートを定義する (declares attibutes)
is => 'rw'
→ 読み書き両用アクセサis => 'ro'
→ 読み込み専用アクセサwriter
, reader
new
は Moose::Object
から継承する
今度はアトリビュートの機能を説明していくよ Now we’re going to discuss more features of the attributes
package Person;
use Moose;
has name => (
is => 'rw',
isa => 'Str'
default => 'Bob'
);
has staff => (
is => 'ro',
isa => 'ArrayRef',
lazy => 1,
default => sub { [qw(Bob Alice Tim)] },
);
default と isa が追加されてます Adds default, isa
default
は
new
にパラメータがわたされなかったときに使われるlazy
は default
を遅延させる
$object->staff
が使われたときに呼ばれる (generates)new
の中ではなく (no param)デフォルトの話 discusses default
リファレンスでないと想定外の共有がむずかしくなる non refs make accidental sharing hard
isa
は型を規定する (specifies type)
Moose::Util::TypeConstraints
Any, Item, Bool, Undef, Defined, Value, Num, Int, Str, Ref, ScalarRef, ArrayRef, HashRef, CodeRef, RegexpRef, GlobRef, FileHandle, Object, and Role
has 'date' => (isa => 'DateTime'); # DWIM
isa, 型の制約 isa, type constraints
Item
⊃ Defined
⊃ Ref
⊃ Object
subtype 'Ref'
=> as 'Defined'
=> where { ref($_) };
subtype 'Object'
=> as 'Ref'
=> where { blessed($_) }
型の階層構造 type hierarchy
package Employee;
use Moose;
use Moose::Util::TypeConstraints;
extends qw(Person);
class_type 'Manager';
coerce 'Manager'
=> from 'Str'
=> via { Manager->new( name => $_) };
has manager => (
is => 'ro',
isa => 'Manager',
required => 1,
coerce => 1,
);
# 型制約のキーワードをインポート(import type constraint keywords)
use Moose::Util::TypeConstraints;
# オブジェクトのサブタイプであるマネージャーを定義(define Manager, a subtype of Object)
class_type "Manager";
# 変換を定義する(define the conversion)
coerce 'Manager'
=> from 'Str'
=> via { Manager->new( name => $_) };
# アトリビュートごとに有効にする(enable it per attribute)
has manager => (
…
coerce => 1,
);
例を細かく見ていくよ breakdown of the example
クラスの型はMooseのクラスすべてに自動的に用意されます class types are automatically created for all Moose classes
package Employee;
use Moose;
extends qw(Person);
has manager => (
is => 'ro',
isa => 'Manager',
handles => {
manager_name => 'name',
coworkers => 'staff',
}
);
Employee
のいくつかのメソッドを処理しますhandles
certain methods for Employee
$emp->coworkers
== $emp->manager->staff
has phone => (
...
handles => [qw(number extension)],
);
has phone => (
isa => "Phone"
handles => qr/$method_regex/,
);
Phone->meta->compute_all_applicable_methods
にフィルターをかけるPhone->meta->compute_all_applicable_methods
has phone => (
...
handles => "Dialing", # a role
);
package Company;
use Moose;
use MooseX::AttributeHelpers;
has employees => (
metaclass => 'Collection::Array',
isa => 'ArrayRef[Employees]',
is => 'rw',
provides => {
push => 'add_employee',
pop => 'remove_employee',
count => 'number_of_employees',
empty => 'any_employees',
}
);
before 'employees' => sub { warn 'calling employees' };
after 'employees' => sub { warn 'finished calling employees' };
@_
のコピーを得ます(Get a copy of @_
)
around 'employees' => sub {
my ($next, $self, @args) = @_;
...
my @return = $self->$next(@args);
...
return @return;
};
package Employee;
use Moose;
sub do_work {
my $self = shift;
$self->punch_in;
inner(); # call subclass here
$self->punch_out;
}
package Employee::Chef;
use Moose;
extends qw(Employee);
augment do_work => sub {
my $self = shift;
while ( @burgers ) {
$self->flip_burger(shift @burgers);
}
}
$chef->do_work; # punch in, flip burgers, punch out
has employees => (
is => 'rw',
isa => 'ArrayRef[Employee]',
);
has shopping_carts => (
is => 'rw',
isa => 'ArrayRef[ArrayRef[ShinyBead]]'
);
型システムの機能についてちょっと説明していくよ Going to go into features of the type system for a bit
パラメータ付きの型 Parametrized types
has language => (
is => 'rw',
isa => 'English | Welsh | Scots | Gaelic',
);
has member => (
is => 'rw',
isa => 'Employee | ArrayRef[Employee|Group]',
);
型の結合 Union types
package Foo;
use Moose;
use Moose::Util::TypeConstraints;
use Test::Deep qw(eq_deeply ...);
type 'SomethingTricky'
=> where {
eq_deeply( $_, ... );
};
has 'bar' => (
is => 'rw',
isa => 'SomethingTricky',
);
Test::Deppのカスタムバリデータ Test::Deep custom validator
CPANからどんなバリデータでも持ってこられる Can use any validation from the CPAN
use Moose::Util::TypeConstraints;
subtype 'ArrayRef[Employee]' => as 'ArrayRef';
coerce 'ArrayRef[Employee]'
=> from 'ArrayRef[Str]'
=> via { [ map { Employee->new( name => $_ ) } @$_ ] };
has staff => (
is => 'ro',
isa => 'ArrayRef[Employee]',
lazy => 1,
default => sub { [qw(Bob Alice Tim)] },
coerce => 1,
);
ArrayRef[Str] から ArrayRef[Employee] に強制変換 coerce parametrized ArrayRef[Employee] from ArrayRef[Str]
強制変換は ‘default’ の返り値にも適用されます coercions can be applied to ‘default’ return values
MooseX::Storage
- 柔軟なシリアライズ(Flexible serialization)MooseX::LogDispatch
- $self->logger->info("something happenned")
MooseX::Getopt
- コマンドライン引数の処理(@ARGV
aware constructor)MooseX::Param
- CGI.pm
の param()
メソッドみたいなの(param
method like CGI.pm
’s)MooseX::Clone
- 柔軟なclone
メソッド(Flexible clone
method)再利用可能な小さな動作の例 Some examples of small reusable behaviors
Paramは連携に便利 Param is good for interacting with e.g. CGI::Expand or similar modules
package Minion;
use Moose;
extends qw(Employee);
with qw(Salaried::Hourly);
package Boss;
use Moose;
extends qw(Employee);
with qw(Salaried::Monthly);
with
はクラスに Role を追加しますwith
adds roles into your class
Salaried::Hourly
がMinion
に追加されるSalaried::Hourly
was added to Minion
package Salaried;
use Moose::Role;
requires qw('paycheck_amount');
package Salaried::Hourly;
use Moose::Role;
with qw(Salaried);
has hourly_rate => (
isa => "Num",
is => "rw",
required => 1,
);
has logged_hours => (
isa => "Num",
is => "rw",
default => 0,
);
sub paycheck_amount {
my $self = shift;
$self->logged_hours * $self->hourly_rate;
}
Roleはアトリビュートとメソッドを持てる roles can have attributes and methods Roleはインターフェースだけでなく動作を提供するもの roles provide behavior, not just interface
喧嘩両成敗というのは優先順位がないということ。ふたつのRoleが同じものを定義しようとした場合はコンパイル時にエラーになる(直さないといけない) symmetric composition means no precedence - if two roles try to define the same thing you get a compile time error that needs to be resolved 多重継承の場合はだまって最初のクラスを使うもんだと想定してしまう multiple inheritence silently assumes you want the first class
Roleは多重継承と違ってコンパイル時にエラーを吐く roles cause errors at compile time, unlike multiple inheritence
Roleは簡単にエラーを修正する方法も用意している roles also provide easy ways to fix the errors
package First;
use Moose::Role;
sub dancing { ... }
package Second;
use Moose::Role
sub dancing { ... }
package Foo;
use Moose;
# KABOOM
with qw(
First
Second
);
衝突 conflicts
package Ent::Puppy;
use Moose;
with (
Tree => {
alias => {
bark => "tree_bark",
},
},
Dog => {
alias => {
bark => "bark_sound",
}
},
);
sub bark {
my $self = shift;
if ( $condition ) {
$self->tree_bark;
} else {
$self->bark_sound;
}
}
組み込むときにパラメータをつける Composition parameters 衝突を解決するのも簡単だし Easier conflict resolution よりきめ細かいコントロールができるようになる Finer grained control
Class::MOP
my $class = $obj->meta; # $objのメタクラス($obj's metaclass)
my $meta = MyApp->meta; # MyAppのメタクラス(MyApp's metaclass)
my $emo = $obj->meta->meta # メタメタ(even more meta)!
warn $obj->meta->name;
my $metaclass = $self->meta;
$metaclass->superclasses;
$metaclass->linearized_isa; # すべての先祖クラスを得ます(returns all ancestors)
$metaclass->has_method("foo");
$metaclass->compute_all_applicable_methods; # すべてのメソッド(継承されたものもふくめて)(returns all methods (inherited too))
$metaclass->has_attribute("bar");
# … lots more
simple introspection
Moose::Meta::Class->create( Bar =>
version => '0.01',
superclasses => [ 'Foo' ],
attributes => [
Moose::Meta::Attribute->new( bar => ... ),
Moose::Meta::Attribute->new( baz => ... ),
],
methods => {
calculate_bar => sub { ... },
construct_baz => sub { ... }
}
);
クラスはプログラム的につくることもできる Classes can be created programmatically
無名クラスも可 Anonymous classes also possible
has foo => ( is => "rw" );
__PACKAGE__->meta->add_attribute( foo => is => "rw" );
has employees => (
metaclass => 'Collection::Array',
...
);
Moose::Meta::Class
, Moose::Meta::Attribute,
Moose::Meta::Method
, Moose::Meta::Method::Accessor
Moose::Meta::Instance
, Moose::Meta::Role
, Moose::Meta::TypeConstraint
, …,$work
story)MooseX::Compile
がアルでよ(MooseX::Compile
is in the works)MooseX::GlobRef::Object
package Units::Bytes;
use Moose::Role;
use Moose::Autobox;
sub bytes { $_[0] }
sub kilobytes { $_[0] * 1024 }
sub megabytes { $_[0] * 1024->kilobytes }
sub gigabytes { $_[0] * 1024->megabytes }
sub terabytes { $_[0] * 1024->gigabytes }
Moose::Autobox->mixin_additional_role(SCALAR => 'Units::Bytes');
use Units::Bytes;
use Moose::Autobox;
is(5->bytes, 5, '... got 5 bytes');
is(5->kilobytes, 5120, '... got 5 kilobytes');
is(2->megabytes, 2097152, '... got 2 megabytes');
is(1->gigabytes, 1073741824, '... got 1 gigabyte');
is(2->terabytes, 2199023255552, '... got 2 terabyte');
oose.pm
があるでよoose.pm
perl -Moose -e'has foo => (is=>q[rw]); Class->new(foo=>1)'
package Counter;
use MooseX::POE;
has count => (
isa => 'Int',
is => 'rw',
);
sub START {
my ($self) = @_;
$self->yield('increment');
}
event increment => sub {
my ($self) = @_;
warn "Count is now " . $self->count;
$self->count( $self->count + 1 );
$self->yield('increment') unless $self->count > 3;
};
Counter->new( count => 0 );
POE::Kernel->run();
event
がオブジェクトのステートを宣言しますevent
declares object statesSlides written by:
Slides deleted by:
Slides translated by: