=> via { $en_parser->parse_datetime($_) };
has birth_date => (
- is => 'rw',
- isa => 'DateTime',
+ is => 'rw',
+ isa => 'DateTime',
+ coerce => 1,
+ handles => { birth_year => 'year' },
);
subtype 'ShirtSize'
exists $p{birth_date}
or confess 'birth_date is a required attribute';
- my $date = $p{birth_date};
- $class->_coerce_birth_date( \$date );
- $class->_validate_birth_date( $date );
+ $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} );
- my $self = map { $_ => $p{$_} } qw( name shirt_size );
- $self->{birth_date} = $date;
-
- return bless $self, $class;
+ return bless \%p, $class;
}
sub _validate_name {
shift;
my $date = shift;
- return unless defined $date && ! ref $date;
+ return $date unless defined $date && ! ref $date;
my $dt = $en_parser->parse_datetime($date);
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
- $birth_date->isa('DateTime') )
+ $birth_date->isa('DateTime')
or confess 'birth_date must be a DateTime object';
}
my $self = shift;
if (@_) {
- my $date = shift;
-
- $self->_coerce_birth_date( $date );
+ 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;
Wow, that was a mouthful! One thing to note is just how much space the
data validation code consumes. As a result, it's pretty common for
-Perl 5 programmers to just not bother, which results in much more
-fragile code.
+Perl 5 programmers to just not bother. Unfortunately, not validating
+arguments leads to surprises down the line ("why is birth_date an
+email address?").
-Did you spot the bug?
+Also, did you spot the (intentional) bug?
It's in the C<_validate_birth_date()> method. We should check that
-that value in C<$birth_date> is actually defined and object before we
-go and call C<isa()> on it! Leaving out those checks means our data
+the value in C<$birth_date> is actually defined and an object before
+we go and call C<isa()> on it! Leaving out those checks means our data
validation code could actually cause our program to die. Oops.
-There's one bit of code in there worth explaining, which is the
-handling of the birth date for coercion. In both the constructor and
-accessor, we first take a copy of the birth date before passing it to
-the coercion routine. This is to avoid changing the value as it was
-passed to those methods, which could cause problems for the caller.
-
-Also note that if we add a superclass to Person we'll have to change
-the constructor to account for that.
+Note that if we add a superclass to Person we'll have to change the
+constructor to account for that.
(As an aside, getting all the little details of what Moose does for
-you just right in this code was not easy, which just emphasizes the
-point, that Moose saves you a lot of work!)
+you just right in this example was really not easy, which emphasizes
+the point of the example. Moose saves you a lot of work!)
Now let's see User:
Of course, there are CPAN modules that do some of what Moose does,
like C<Class::Accessor>, C<Class::Meta>, and so on. But none of them
put together all of Moose's features along with a layer of declarative
-sugar.
+sugar, nor are these other modules designed for extensibility in the
+same way as Moose. With Moose, it's easy to write a MooseX module to
+replace or extend a piece of built-in functionality.
+
+Moose is a complete OO package in and of itself, and is part of a rich
+ecosystem of extensions. It also has an enthusiastic community of
+users, and is being actively maintained and developed.
=head1 AUTHOR
=head1 COPYRIGHT AND LICENSE
-Copyright 2008 by Infinity Interactive, Inc.
+Copyright 2009 by Infinity Interactive, Inc.
L<http://www.iinteractive.com>