Daniel Bruder, M.A.
CIS, LMU, 06/2014
perlbrew
#!/usr/bin/env perl
(!)cpanm
Dist::Zilla
Moose hat darüber hinaus:
my $self = shift
mehr am Anfang einer Subroutine@_
usw.method foo(Str :$str, ArrayRef[Int] $values where { map { $_ > 0 } })
multi method foo(Str :$str)
Mixins / Roles
"horizontale Vererbung" / "monkey patching" / "duck typing"
Beispiel:
package AdminUser;
use Moose;
extends 'User';
with 'Role::Admin::Privileges';
Int
, Str
, Undef
, ArrayRef
, HashRef[ArrayRef[Str]]
, ...um genau zu sein:
Any
Item
Bool
Maybe[`a]
Undef
Defined
Value
Str
Num
Int
ClassName
RoleName
Ref
ScalarRef[`a]
ArrayRef[`a]
HashRef[`a]
CodeRef
RegexpRef
GlobRef
FileHandle
Object
$class->can('dance')
MooseX::*
Das Herzstück von Moose
has
eingeleitetBeispiel: Klasse Person
package Person;
use Moose;
has name => (
is => 'ro',
isa => 'Str'
);
Person->new
$person->name
$person->name('Newman')
package main;
my $p1 = Person->new;
my $p2 = Person->new(name => 'Foo');
$p1->name('Bar');
say $p1->name, " ", $p2->name;
package Person;
use DateTime;
use DateTime::Format::Natural;
use Moose;
use Moose::Util::TypeConstraints;
has name => (
is => 'rw',
isa => 'Str',
required => 1,
);
# ...
# ...
has birth_date => (
is => 'rw',
isa => 'DateTime',
);
# ...
# Moose doesn't know about non-Moose-based classes.
class_type 'DateTime';
my $en_parser = DateTime::Format::Natural->new(
lang => 'en',
time_zone => 'UTC',
);
coerce 'DateTime'
=> from 'Str'
=> via { $en_parser->parse_datetime($_) };
# ...
# Moose doesn't know about non-Moose-based classes: coerce it!
has birth_date => (
is => 'rw',
isa => 'DateTime',
coerce => 1,
);
# ...
# Delegate to DateTime->year() and expose birth_year in API!
has birth_date => (
is => 'rw',
isa => 'DateTime',
coerce => 1,
handles => { birth_year => 'year' },
);
# ...
enum 'ShirtSize' => qw( s m l xl xxl );
has shirt_size => (
is => 'rw',
isa => 'ShirtSize',
default => 'l',
);
package User;
use Email::Valid;
use Moose;
use Moose::Util::TypeConstraints;
extends 'Person';
has email_address => (
is => 'rw',
isa => 'Email',
required => 1,
);
subtype 'Email'
=> as 'Str'
=> where { Email::Valid->address($_) }
=> message { "$_ is not a valid email address" };
use Person;
my $person = Person->new(
name => 'Bob',
birth_date => 'Dec 24 1789'
);
say $person->name,
' was born in the year of ', $person->birth_year,
' and wears shirt size: ', $person->shirt_size;
use User;
my $user = User->new(
name => 'Anne',
email_address => 'jane@example.com',
shirt_size => 's'
);
say $user->name, "'s email address is: ", $user->email_address;
say $user->name, "'s shirt size is: ", $user->shirt_size;
$user->birth_date
is not defined yet...Str
DateTime
DateTime::Format::Natural
!$user->birth_date('tuesday last week');
say $user->name, "'s shirt birth date is: ", $user->birth_date; # UTC, FTW
package Person;
use strict;
use warnings;
use Carp qw( confess );
use DateTime;
use DateTime::Format::Natural;
sub new {
my $class = shift;
my %p = ref $_[0] ? %{ $_[0] } : @_;
exists $p{name}
or confess 'name is a required attribute';
$class->_validate_name( $p{name} );
exists $p{birth_date}
or confess 'birth_date is a required attribute';
$p{birth_date} = $class->_coerce_birth_date( $p{birth_date} );
$class->_validate_birth_date( $p{birth_date} );
$p{shirt_size} = 'l'
unless exists $p{shirt_size}:
$class->_validate_shirt_size( $p{shirt_size} );
return bless \%p, $class;
}
sub _validate_name {
shift;
my $name = shift;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
defined $name
or confess 'name must be a string';
}
{
my $en_parser = DateTime::Format::Natural->new(
lang => 'en',
time_zone => 'UTC',
);
sub _coerce_birth_date {
shift;
my $date = shift;
return $date unless defined $date && ! ref $date;
my $dt = $en_parser->parse_datetime($date);
return $dt ? $dt : undef;
}
}
sub _validate_birth_date {
shift;
my $birth_date = shift;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
$birth_date->isa('DateTime')
or confess 'birth_date must be a DateTime object';
}
sub _validate_shirt_size {
shift;
my $shirt_size = shift;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
defined $shirt_size
or confess 'shirt_size cannot be undef';
my %sizes = map { $_ => 1 } qw( s m l xl xxl );
$sizes{$shirt_size}
or confess "$shirt_size is not a valid shirt size (s, m, l, xl, xxl)";
}
sub name {
my $self = shift;
if (@_) {
$self->_validate_name( $_[0] );
$self->{name} = $_[0];
}
return $self->{name};
}
sub birth_date {
my $self = shift;
if (@_) {
my $date = $self->_coerce_birth_date( $_[0] );
$self->_validate_birth_date( $date );
$self->{birth_date} = $date;
}
return $self->{birth_date};
}
sub birth_year {
my $self = shift;
return $self->birth_date->year;
}
sub shirt_size {
my $self = shift;
if (@_) {
$self->_validate_shirt_size( $_[0] );
$self->{shirt_size} = $_[0];
}
return $self->{shirt_size};
}
multi method drink ( Coffee $beverage! does coerce, ... ) { ... }
package Role::Admin::Privileges {
use Moose::Role;
requires 'authenticate';
has _privileges => (
is => 'ro',
isa => enum(qw<sudoer user>),
lazy => 1
);
}
package AdminUser {
use Moose;
extends 'User';
with 'Role::Admin::Privileges';
method authenticate {
... # yadda yadda yadda
}
}
Code
use Moose;
has foo => (
is => 'rw',
isa => 'ArrayRef[Str]',
traits => [ 'Array' ],
handles => {
foo_push => 'push'
},
);
$self->foo_push(@values);
use MooseX::DeclareX;
class Baz with MooseX::Getopt {
has foo => (
is => 'rw',
isa => 'Str',
traits => [ 'Getopt' ],
cmd_aliases => ['f', 'foo'],
default => 'foo',
);
}
package main;
print Baz->new_with_options->foo;
__END__
$ perl Baz.pm
# foo
$ Perl Baz.pm --foo=quux
# quux
$ Perl Baz.pm -f bar
# bar
Method modifiers erlauben es, sub-Routinen / Methoden zu "hooken"
package Example;
use Moose;
sub foo {
print " foo\n";
}
before 'foo' => sub { print "about to call foo\n"; };
after 'foo' => sub { print "just called foo\n"; };
around 'foo' => sub {
my $orig = shift;
my $self = shift;
print " I'm around foo\n";
$self->$orig(@_);
print " I'm still around foo\n";
};
Ergebnis:
about to call foo
I'm around foo
foo
I'm still around foo
just called foo
use Moops;
role NamedThing {
has name => (is => "ro", isa => Str);
}
class Person with NamedThing;
class Company with NamedThing;
class Employee extends Person {
has job_title => (is => "rwp", isa => Str);
has employer => (is => "rwp", isa => InstanceOf["Company"]);
method change_job (Object $employer, Str $title) {
$self->_set_job_title($title);
$self->_set_employer($employer);
}
method promote (Str $title) {
$self->_set_job_title($title);
}
}
Mit lazy
lassen sich teure Berechungen auf den spätest-möglichen Zeitpunkt verschieben:
has size => (
is => 'ro',
lazy => 1,
builder => '_build_size',
);
method _build_size {
# heavy stuff...
}
Devel::Declare
-Magie verwendenen, können hier gemeine Effekte in Form von unnachvollziehbaren Fehlermeldungen u.ä. entstehen. Mit Vorsicht zu genießen (aber sehr genießbar...).Devel::Declare
und kann also als veritable als Alternative gesehen werden – ist aber noch nicht production ready.MooseX::*
: https://metacpan.org/search?q=moosex%3A%3A