Redo method modifiers exercise to use before/after/around instead of augment/inner
Dave Rolsky [Thu, 3 Feb 2011 21:41:07 +0000 (15:41 -0600)]
moose-class/exercises/answers/04-method-modifiers/Document.pm [deleted file]
moose-class/exercises/answers/04-method-modifiers/Person.pm [new file with mode: 0644]
moose-class/exercises/answers/04-method-modifiers/Report.pm [deleted file]
moose-class/exercises/answers/04-method-modifiers/TPSReport.pm [deleted file]
moose-class/exercises/t/04-method-modifiers.t
moose-class/exercises/t/lib/MooseClass/Tests.pm

diff --git a/moose-class/exercises/answers/04-method-modifiers/Document.pm b/moose-class/exercises/answers/04-method-modifiers/Document.pm
deleted file mode 100644 (file)
index c44d165..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package Document;
-
-use Moose;
-
-has [ qw( title author ) ] => ( is => 'ro' );
-
-sub output {
-    my $self = shift;
-
-    my $t = $self->title;
-    my $a = $self->author;
-
-    my $content = inner();
-
-    return <<"EOF";
-$t
-
-$content
-
-Written by $a
-EOF
-}
-
-no Moose;
-
-__PACKAGE__->meta->make_immutable;
-
-1;
diff --git a/moose-class/exercises/answers/04-method-modifiers/Person.pm b/moose-class/exercises/answers/04-method-modifiers/Person.pm
new file mode 100644 (file)
index 0000000..8c519e2
--- /dev/null
@@ -0,0 +1,70 @@
+package Person;
+
+use Moose;
+
+has title => (
+    is        => 'rw',
+    predicate => 'has_title',
+    clearer   => 'clear_title',
+);
+
+has first_name => (
+    is       => 'rw',
+    required => 1,
+);
+
+has last_name => (
+    is       => 'rw',
+    required => 1,
+);
+
+sub BUILDARGS {
+    my $class = shift;
+
+    if ( @_ == 1 && 'ARRAY' eq ref $_[0] ) {
+        return {
+            first_name => $_[0]->[0],
+            last_name  => $_[0]->[1],
+        };
+    }
+    else {
+        return $class->SUPER::BUILDARGS(@_);
+    }
+}
+
+our @CALL;
+
+before full_name => sub {
+    push @CALL, 'calling full_name';
+};
+
+after full_name => sub {
+    push @CALL, 'called full_name';
+};
+
+sub full_name {
+    my $self = shift;
+
+    my $title = join q{ }, $self->first_name, $self->last_name;
+    $title .= q[ (] . $self->title . q[)]
+        if $self->has_title;
+
+    return $title;
+}
+
+around full_name => sub {
+    my $orig = shift;
+    my $self = shift;
+
+    return $self->$orig unless $self->last_name eq 'Wall';
+
+    return q{*} . $self->$orig . q{*};
+};
+
+sub as_string { $_[0]->full_name }
+
+no Moose;
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/moose-class/exercises/answers/04-method-modifiers/Report.pm b/moose-class/exercises/answers/04-method-modifiers/Report.pm
deleted file mode 100644 (file)
index 723ab75..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-package Report;
-
-use Moose;
-
-extends 'Document';
-
-has 'summary' => ( is => 'ro' );
-
-augment output => sub {
-    my $self = shift;
-
-    my $content = inner();
-
-    my $s = $self->summary;
-
-    return <<"EOF";
-$s
-
-$content
-EOF
-};
-
-no Moose;
-
-__PACKAGE__->meta->make_immutable;
-
-1;
diff --git a/moose-class/exercises/answers/04-method-modifiers/TPSReport.pm b/moose-class/exercises/answers/04-method-modifiers/TPSReport.pm
deleted file mode 100644 (file)
index c88a79a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-package TPSReport;
-
-use Moose;
-
-extends 'Report';
-
-has [ qw( t p s ) ] => ( is => 'ro' );
-
-augment output => sub {
-    my $self = shift;
-
-    return join q{}, map { "$_: " . $self->$_ . "\n" } qw( t p s );
-};
-
-no Moose;
-
-__PACKAGE__->meta->make_immutable;
-
-1;
index f681f72..f31420b 100644 (file)
@@ -1,43 +1,23 @@
 # Your tasks ...
 #
-# First, you will create a set of three new classes to make use of the augment
-# method modifier. The class hierarchy will look like this:
+# You will go back to the Person class and add several method modifiers.
 #
-#   Document
-#      |
-#   Report
-#      |
-#   TPSReport
+# First, add before and after modifiers to the full_name() method. The
+# modifiers will be using for debugging. These modifiers will record debugging
+# info by setting a package global, @Person::CALL;
 #
-# The Document class should have two read-only attributes: "title" and
-# "author".
+# The before modifier should push the string "calling full_name" onto
+# @Person::CALL. The after modifier should push "called full_name" onto this
+# array.
 #
-# The Report class should have one read-only attribute: "summary".
+# You do not need to declare this global, but you can if you like.
 #
-# Finally, the TPSReport class should have three read-only attributes: "t",
-# "p", and "s".
+# Finally, create an around modifier for full_name. This modifier should call
+# the real full_name method.
 #
-# The goal is to produce a report that looks this:
-#
-# $title
-#
-# $summary
-#
-# t: $t
-# p: $p
-# s: $s
-#
-# Written by $author
-#
-# This report should be returned as a string from the Document->output method.
-#
-# Don't worry too much about how many newlines separate each item (as long as
-# it's at least one). The test does a little massaging to make this more
-# forgiving.
-#
-# Use augment method modifiers in Report and TPSReport to "inject" the
-# relevant content, while Document will output the $title and $author.
-
+# However, if the person object's last name is "Wall" (as in Larry Wall), your
+# modifier should wrap the full name in asterisks (*), one before the name and
+# one after, and then return that new version of the name.
 use strict;
 use warnings;
 
index 9fc3a8f..d5a86bf 100644 (file)
@@ -121,43 +121,66 @@ sub tests03 {
 }
 
 sub tests04 {
-    has_meta('Document');
-    has_ro_attr( 'Document',  $_ ) for qw( title author );
-
-    has_meta('Report');
-    has_ro_attr( 'Report',    'summary' );
-
-    has_meta('TPSReport');
-    has_ro_attr( 'TPSReport', $_ ) for qw( t p s );
-
-    has_method( 'Document', 'output' );
-    has_augmented_method( 'Report',    'output' );
-    has_augmented_method( 'TPSReport', 'output' );
-
-    my $tps = TPSReport->new(
-        title   => 'That TPS Report',
-        author  => 'Peter Gibbons (for Bill Lumberg)',
-        summary => 'I celebrate his whole collection!',
-        t       => 'PC Load Letter',
-        p       => 'Swingline',
-        s       => 'flair!',
-    );
-
-    my $output = $tps->output;
-    $output =~ s/\n\n+/\n/g;
-
-    is( $output, <<'EOF', 'output returns expected report' );
-That TPS Report
-I celebrate his whole collection!
-t: PC Load Letter
-p: Swingline
-s: flair!
-Written by Peter Gibbons (for Bill Lumberg)
-EOF
-
-    no_droppings('Document');
-    no_droppings('Report');
-    no_droppings('TPSReport');
+    has_meta('Person');
+
+    ok( Person->can('full_name'), 'Person has a full_name() method' )
+        or BAIL_OUT(
+        'Person does not have a full_name() method. Cannot continue testing.'
+        );
+
+    my $meth = Person->meta()->get_method('full_name');
+    ok(
+        $meth && $meth->isa('Class::MOP::Method::Wrapped'),
+        'method modifiers have been applied to the Person->full_name method'
+    );
+
+    is(
+        scalar $meth->before_modifiers,
+        1,
+        'Person->full_name has a single before modifier'
+    );
+
+    is(
+        scalar $meth->after_modifiers,
+        1,
+        'Person->full_name has a single after modifier'
+    );
+
+    my $person = Person->new(
+        first_name => 'Bilbo',
+        last_name  => 'Baggins',
+    );
+
+    is_deeply(
+        \@Person::CALL,
+        [],
+        'Person::CALL global is empty before calling full_name'
+    );
+
+    $person->full_name();
+
+    is_deeply(
+        \@Person::CALL,
+        [ 'calling full_name', 'called full_name' ],
+        'Person::CALL global contains before and after strings'
+    );
+
+    is(
+        scalar $meth->around_modifiers,
+        1,
+        'Person->full_name has a single around modifier'
+    );
+
+    my $larry = Person->new(
+        first_name => 'Larry',
+        last_name  => 'Wall',
+    );
+
+    is(
+        $larry->full_name,
+        '*Larry Wall*',
+        'full_name is wrapped by asterisks when last name is Wall'
+    );
 }
 
 sub tests05 {