Finished slides & exercises for section 3on basic attributes.
Dave Rolsky [Tue, 16 Jun 2009 16:33:32 +0000 (11:33 -0500)]
Some tweaking of style and previous sections as well.

13 files changed:
moose-class/exercises/answers/01-classes/Employee.pm
moose-class/exercises/answers/02-roles/Employee.pm
moose-class/exercises/answers/02-roles/HasAccount.pm
moose-class/exercises/answers/03-basic-attributes/Employee.pm [new file with mode: 0644]
moose-class/exercises/answers/03-basic-attributes/HasAccount.pm [new file with mode: 0644]
moose-class/exercises/answers/03-basic-attributes/Person.pm [new file with mode: 0644]
moose-class/exercises/answers/03-basic-attributes/Printable.pm [new file with mode: 0644]
moose-class/exercises/t/01-classes.t
moose-class/exercises/t/03-basic-attributes.t [new file with mode: 0644]
moose-class/exercises/t/lib/MooseClass/Tests.pm
moose-class/slides/index.html
moose-class/slides/outline
moose-class/slides/ui/custom.css

index 6c301fc..3860493 100644 (file)
@@ -4,14 +4,14 @@ use Moose;
 
 extends 'Person';
 
-has position => ( is => 'rw' );
-has salary   => ( is => 'rw' );
-has ssn      => ( is => 'ro' );
+has title  => ( is => 'rw' );
+has salary => ( is => 'rw' );
+has ssn    => ( is => 'ro' );
 
 override full_name => sub {
     my $self = shift;
 
-    return super() . q[ (] . $self->position . q[)];
+    return super() . q[ (] . $self->title . q[)];
 };
 
 no Moose;
index 6c301fc..3860493 100644 (file)
@@ -4,14 +4,14 @@ use Moose;
 
 extends 'Person';
 
-has position => ( is => 'rw' );
-has salary   => ( is => 'rw' );
-has ssn      => ( is => 'ro' );
+has title  => ( is => 'rw' );
+has salary => ( is => 'rw' );
+has ssn    => ( is => 'ro' );
 
 override full_name => sub {
     my $self = shift;
 
-    return super() . q[ (] . $self->position . q[)];
+    return super() . q[ (] . $self->title . q[)];
 };
 
 no Moose;
index 4a4bd68..8287588 100644 (file)
@@ -2,9 +2,7 @@ package HasAccount;
 
 use Moose::Role;
 
-has balance => (
-    is => 'rw',
-);
+has balance => ( is => 'rw' );
 
 sub deposit {
     my $self   = shift;
diff --git a/moose-class/exercises/answers/03-basic-attributes/Employee.pm b/moose-class/exercises/answers/03-basic-attributes/Employee.pm
new file mode 100644 (file)
index 0000000..11b8db4
--- /dev/null
@@ -0,0 +1,35 @@
+package Employee;
+
+use Moose;
+
+extends 'Person';
+
+has '+title' => (
+    default => 'Worker',
+);
+
+has salary_level => (
+    is      => 'rw',
+    default => 1,
+);
+
+has salary => (
+    is       => 'ro',
+    lazy     => 1,
+    builder  => '_build_salary',
+    init_arg => undef,
+);
+
+has ssn    => ( is => 'ro' );
+
+sub _build_salary {
+    my $self = shift;
+
+    return $self->salary_level * 10000;
+}
+
+no Moose;
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/moose-class/exercises/answers/03-basic-attributes/HasAccount.pm b/moose-class/exercises/answers/03-basic-attributes/HasAccount.pm
new file mode 100644 (file)
index 0000000..76ea15e
--- /dev/null
@@ -0,0 +1,29 @@
+package HasAccount;
+
+use Moose::Role;
+
+has balance => (
+    is      => 'rw',
+    default => 100,
+);
+
+sub deposit {
+    my $self   = shift;
+    my $amount = shift;
+
+    $self->balance( $self->balance + $amount );
+}
+
+sub withdraw {
+    my $self   = shift;
+    my $amount = shift;
+
+    die "Balance cannot be negative"
+        if $self->balance < $amount;
+
+    $self->balance( $self->balance - $amount );
+}
+
+no Moose::Role;
+
+1;
diff --git a/moose-class/exercises/answers/03-basic-attributes/Person.pm b/moose-class/exercises/answers/03-basic-attributes/Person.pm
new file mode 100644 (file)
index 0000000..db69019
--- /dev/null
@@ -0,0 +1,33 @@
+package Person;
+
+use Moose;
+
+with 'Printable', 'HasAccount';
+
+has title => (
+    is        => 'rw',
+    predicate => 'has_title',
+    clearer   => 'clear_title',
+);
+
+has first_name => ( is => 'rw' );
+
+has last_name  => ( is => 'rw' );
+
+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;
+}
+
+sub as_string { $_[0]->full_name }
+
+no Moose;
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/moose-class/exercises/answers/03-basic-attributes/Printable.pm b/moose-class/exercises/answers/03-basic-attributes/Printable.pm
new file mode 100644 (file)
index 0000000..cb9b58c
--- /dev/null
@@ -0,0 +1,9 @@
+package Printable;
+
+use Moose::Role;
+
+requires 'as_string';
+
+no Moose::Role;
+
+1;
index d4db456..e9747fb 100644 (file)
@@ -18,7 +18,7 @@
 #
 # An Employee has the following read-write attributes:
 #
-# * position - read-write
+# * title - read-write
 # * salary - read-write
 # * ssn - read-only
 #
diff --git a/moose-class/exercises/t/03-basic-attributes.t b/moose-class/exercises/t/03-basic-attributes.t
new file mode 100644 (file)
index 0000000..c0d3bad
--- /dev/null
@@ -0,0 +1,44 @@
+# Your tasks ...
+#
+# Go back to your Person class and make the first_name and last_name
+# attributes required.
+#
+# Move the title attribute from the Employee class to the Person
+# class. Adjust full_name in the Person class so it includes the
+# title, which is optional.
+#
+# Add a predicate (has_title) and clearer (clear_title) to the title
+# attribute as well.
+#
+# If a person has no title, the full_name method should simply return
+# the first and last name. Use the title's predicate method in the new
+# full_name method.
+#
+# Go back to the Employee class
+#
+# Make the title attribute default to the string 'Worker' for the
+# Employee class. You can now inherit full_name from the Person class
+# rather than re-implementing it.
+#
+# Add a read-write salary_level attribute. This will be a number from
+# 1-10 (but we will deal with enforcing this later). This attribute
+# should default to 1.
+#
+# Make the salary attribute read-only. Also make it lazy. The default
+# should be calculated as salary_level * 10000. Use a builder method
+# to set the default. Name the builder "_build_salary". This attribute
+# should not be settable via the constructor.
+#
+# Go back to the HasAccount role and make the balance default to 100.
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use MooseClass::Tests;
+
+use Person;
+use Employee;
+
+MooseClass::Tests::tests03();
index 7c4d338..5455a27 100644 (file)
@@ -36,7 +36,7 @@ sub tests01 {
 
     count_attrs( 'Employee', $p{employee_attr_count} );
 
-    has_rw_attr( 'Employee', $_ ) for qw( position salary );
+    has_rw_attr( 'Employee', $_ ) for qw( title salary );
     has_ro_attr( 'Employee', 'ssn' );
 
     has_overridden_method( 'Employee', 'full_name' );
@@ -45,7 +45,7 @@ sub tests01 {
 }
 
 sub tests02 {
-    tests01( person_attr_count => 3 );
+    tests01( person_attr_count => 3, @_ );
 
     local $Test::Builder::Level = $Test::Builder::Level + 1;
 
@@ -61,6 +61,50 @@ sub tests02 {
     employee02();
 }
 
+sub tests03 {
+    {
+        local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+        has_meta('Person');
+        has_meta('Employee');
+
+        has_rw_attr( 'Person', 'title' );
+
+        has_rw_attr( 'Employee', 'title' );
+        has_rw_attr( 'Employee', 'salary_level' );
+        has_ro_attr( 'Employee', 'salary' );
+
+        has_method( 'Employee', '_build_salary' );
+    }
+
+    ok( ! Employee->meta->has_method('full_name'),
+        'Employee no longer implements a full_name method' );
+
+    my $person_title_attr = Person->meta->get_attribute('title');
+    ok( !$person_title_attr->is_required, 'title is not required in Person' );
+    is( $person_title_attr->predicate, 'has_title',
+        'Person title attr has a has_title predicate' );
+    is( $person_title_attr->clearer, 'clear_title',
+        'Person title attr has a clear_title clearer' );
+
+    my $balance_attr = Person->meta->get_attribute('balance');
+    is( $balance_attr->default, 100, 'balance defaults to 100' );
+
+    my $employee_title_attr = Employee->meta->get_attribute('title');
+    is( $employee_title_attr->default, 'Worker',
+        'title defaults to Worker in Employee' );
+
+    my $salary_level_attr = Employee->meta->get_attribute('salary_level');
+    is( $salary_level_attr->default, 1, 'salary_level defaults to 1' );
+
+    my $salary_attr = Employee->meta->get_attribute('salary');
+    ok( !$salary_attr->init_arg,   'no init_arg for salary attribute' );
+    ok( $salary_attr->has_builder, 'salary attr has a builder' );
+
+    person03();
+    employee03();
+}
+
 sub has_meta {
     my $class = shift;
 
@@ -90,66 +134,56 @@ sub count_attrs {
     my $count = shift;
 
     my $noun = PL_N( 'attribute', $count );
-    is(
-        scalar $class->meta->get_attribute_list, $count,
-        "$class defines $count $noun"
-    );
+    is( scalar $class->meta->get_attribute_list, $count,
+        "$class defines $count $noun" );
 }
 
 sub has_rw_attr {
     my $class = shift;
     my $name  = shift;
 
-    my $article = A($name);
+    my $articled = A($name);
     ok( $class->meta->has_attribute($name),
-        "$class has $article $name attribute" );
+        "$class has $articled attribute" );
 
     my $attr = $class->meta->get_attribute($name);
 
-    is(
-        $attr->get_read_method, $name,
-        "$name attribute has a reader accessor - $name()"
-    );
-    is(
-        $attr->get_write_method, $name,
-        "$name attribute has a writer accessor - $name()"
-    );
+    is( $attr->get_read_method, $name,
+        "$name attribute has a reader accessor - $name()" );
+    is( $attr->get_write_method, $name,
+        "$name attribute has a writer accessor - $name()" );
 }
 
 sub has_ro_attr {
     my $class = shift;
     my $name  = shift;
 
-    my $article = A($name);
+    my $articled = A($name);
     ok( $class->meta->has_attribute($name),
-        "$class has $article $name attribute" );
+        "$class has $articled attribute" );
 
     my $attr = $class->meta->get_attribute($name);
 
-    is(
-        $attr->get_read_method, $name,
-        "$name attribute has a reader accessor - $name()"
-    );
-    is(
-        $attr->get_write_method, undef,
-        "$name attribute does not have a writer"
-    );
+    is( $attr->get_read_method, $name,
+        "$name attribute has a reader accessor - $name()" );
+    is( $attr->get_write_method, undef,
+        "$name attribute does not have a writer" );
 }
 
 sub has_method {
     my $class = shift;
     my $name  = shift;
 
-    my $article = A($name);
-    ok( $class->meta->has_method($name), "$class has $article $name method" );
+    my $articled = A($name);
+    ok( $class->meta->has_method($name), "$class has $articled method" );
 }
 
 sub has_overridden_method {
     my $class = shift;
     my $name  = shift;
 
-    my $article = A($name);
-    ok( $class->meta->has_method($name), "$class has $article $name method" );
+    my $articled = A($name);
+    ok( $class->meta->has_method($name), "$class has $articled method" );
 
     my $meth = $class->meta->get_method($name);
     isa_ok( $meth, 'Moose::Meta::Method::Overridden' );
@@ -180,23 +214,18 @@ sub person01 {
         last_name  => 'Baggins',
     );
 
-    is(
-        $person->full_name, 'Bilbo Baggins',
-        'full_name() is correctly implemented'
-    );
+    is( $person->full_name, 'Bilbo Baggins',
+        'full_name() is correctly implemented' );
 }
 
 sub employee01 {
     my $employee = Employee->new(
         first_name => 'Amanda',
         last_name  => 'Palmer',
-        position   => 'Singer',
+        title      => 'Singer',
     );
 
-    is(
-        $employee->full_name, 'Amanda Palmer (Singer)',
-        'full_name() is properly overriden in Employee'
-    );
+    is(        $employee->full_name, 'Amanda Palmer (Singer)',        'full_name() is properly overriden in Employee'    );
 }
 
 sub person02 {
@@ -206,10 +235,8 @@ sub person02 {
         balance    => 0,
     );
 
-    is(
-        $person->as_string, 'Bilbo Baggins',
-        'as-string() is correctly implemented'
-    );
+    is( $person->as_string, 'Bilbo Baggins',
+        'as_string() is correctly implemented' );
 
     account_tests($person);
 }
@@ -218,34 +245,65 @@ sub employee02 {
     my $employee = Employee->new(
         first_name => 'Amanda',
         last_name  => 'Palmer',
-        position   => 'Singer',
+        title      => 'Singer',
         balance    => 0,
     );
 
-    is(
-        $employee->as_string, 'Amanda Palmer (Singer)',
-        'as_string() uses overridden full_name method in Employee'
-    );
+    is( $employee->as_string, 'Amanda Palmer (Singer)',
+        'as_string() uses overridden full_name method in Employee' );
 
     account_tests($employee);
 }
 
+sub person03 {
+    my $person = Person->new(
+        first_name => 'Bilbo',
+        last_name  => 'Baggins',
+    );
+
+    is( $person->full_name, 'Bilbo Baggins',
+        'full_name() is correctly implemented for a Person without a title' );
+    ok( !$person->has_title,
+        'Person has_title predicate is working correctly' );
+
+    $person->title('Ringbearer');
+    ok( $person->has_title, 'Person has_title predicate is working correctly' );
+    is( $person->full_name, 'Bilbo Baggins (Ringbearer)',
+        'full_name() is correctly implemented for a Person with a title' );
+
+    $person->clear_title;
+    ok( !$person->has_title, 'Person clear_title method cleared the title' );
+
+    account_tests( $person, 100 );
+}
+
+sub employee03 {
+    my $employee = Employee->new(
+        first_name   => 'Jimmy',
+        last_name    => 'Foo',
+        salary_level => 3,
+        salary       => 42,
+    );
+
+    is( $employee->salary, 30000,
+        'salary is calculated from salary_level, and salary passed to constructor is ignored' );
+}
+
 sub account_tests {
     local $Test::Builder::Level = $Test::Builder::Level + 1;
 
     my $person = shift;
+    my $base_amount = shift || 0;
 
     $person->deposit(50);
-    eval { $person->withdraw(75) };
-    like(
-        $@, qr/\QBalance cannot be negative/,
-        'cannot withdraw more than is in our balance'
-    );
+    eval { $person->withdraw( 75 + $base_amount ) };
+    like( $@, qr/\QBalance cannot be negative/,
+          'cannot withdraw more than is in our balance' );
 
-    $person->withdraw(23);
+    $person->withdraw( 23 );
 
-    is( $person->balance, 27,
-        'balance is 25 after deposit of 50 and withdrawal of 23' );
+    is( $person->balance, 27 + $base_amount,
+        'balance is 27 (+ starting balance) after deposit of 50 and withdrawal of 23' );
 }
 
 1;
index 59fbfd6..2ff609a 100644 (file)
@@ -61,7 +61,7 @@ img#me05 {top: 43px;left: 36px;}
   <ul>
     <li><strong>Declarative</strong> OO sugar</li>
     <li>Introspectable</li>
-    <li>Extensible</li>
+    <li>Extensible (MooseX::* on CPAN)</li>
   </ul>
 </div>
 
@@ -814,7 +814,7 @@ sub last_name {
 
 <div class="slide">
     <h1>Typo?</h1>
-    <code>$self-&gt;{last_na<span class="highlight">n</span>e}</code>
+    <code>$self-&gt;{last_na<span class="wrong">n</span>e}</code>
 </div>
 
 <div class="slide">
@@ -1090,7 +1090,7 @@ use Moose;
 <div class="slide">
   <h1>Exercises</h1>
 
-  <pre>$ cd exercises
+  <pre># cd exercises
 $ perl bin/prove -lv t/00-prereq.t
 
 Missing anything? Install it. (see tarballs/)
@@ -1575,12 +1575,494 @@ requires 'compare';
 <div class="slide">
   <h1>Exercises</h1>
 
-  <pre>$ cd exercises
+  <pre># cd exercises
 # perl bin/prove -lv t/02-roles.t
 
 Iterate til this passes all its tests</pre>
 </div>
 
+<div class="slide fake-slide0">
+  <h1>Part 3: Basic Attributes</h1>
+</div>
+
+<div class="slide">
+  <h1>Attributes Are Huge</h1>
+
+  <ul>
+    <li>Moose's biggest feature</li>
+    <li>The target of <em>many</em> MooseX modules</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Quick Review</h1>
+
+  <ul>
+    <li>Declared with <code>has</code></li>
+    <li>Read-only or read-write</li>
+  </ul>
+
+  <pre><code>package Shirt;
+use Moose;
+
+has 'color'     =&gt; ( is =&gt; 'ro' );
+has 'is_ripped' =&gt; ( is =&gt; 'rw' );</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Required-ness</h1>
+
+  <ul>
+    <li>Required means "must be passed to the constructor"</li>
+    <li>But can be <code>undef</code></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Required-ness</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has first_name =&gt; (
+    is       =&gt; 'ro',
+    <span class="current">required =&gt; 1,</span>
+);
+
+<span class="incremental">Person->new( first_name =&gt; undef ); # ok
+Person->new(); # kaboom</span></code></pre>
+</div>
+
+<div class="slide">
+  <h1>Default and Builder</h1>
+
+  <ul>
+    <li>Attributes can have defaults</li>
+    <li>Simple non-referecne scalars (number, string)</li>
+    <li>Subroutine reference</li>
+    <li>A builder method</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Default</h1>
+
+  <ul>
+    <li>Can be a non-reference scalar (including <code>undef</code>)</li>
+  </ul>
+
+  <pre><code>package Person;
+use Moose;
+
+has bank =&gt; (
+    is      =&gt; 'rw',
+    default =&gt; 'Spire FCU',
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Default</h1>
+
+  <ul>
+    <li>Can be a subroutine reference</li>
+  </ul>
+
+  <pre><code>package Person;
+use Moose;
+
+has bank =&gt; (
+    is      =&gt; 'rw',
+    default =&gt;
+        sub { Bank-&gt;new(
+                  name =&gt; 'Spire FCU' ) },
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Default as a Subroutine Reference</h1>
+
+  <ul>
+    <li>Called as a method on the object</li>
+    <li>Called anew for each object</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Why No Other Reference Types?</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has bank =&gt; (
+    is      =&gt; 'rw',
+    <span class="wrong">default =&gt; Bank-&gt;new(
+                   name =&gt; 'Spire FCU' ),</span>
+);</code></pre>
+
+  <ul>
+    <li>Now <strong>every</strong> person shares the <strong>same</strong> Bank object!</li>
+  </ul>
+</div>
+
+<div class="slide">
+     <h1>Defaulting to an Empty Reference</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has packages =&gt; (
+    is      =&gt; 'rw',
+    default =&gt; <span class="highlight">sub { [] }</span>,
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>What if I Want to Share?</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+my $highlander_bank =
+    Bank-&gt;new( name =&gt; 'Spire FCU' );
+
+has bank =&gt; (
+    is      =&gt; 'rw',
+    default =&gt; sub { $highlander_bank },
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Builder</h1>
+
+  <ul>
+    <li>A method <em>name</em> which returns the default</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Builder</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has bank =&gt; (
+    is      =&gt; 'rw',
+    builder =&gt; '_build_bank',
+);
+
+sub _build_bank {
+    my $self = shift;
+    return Bank-&gt;new( name => 'Spire FCU' );
+}</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Default vs Builder</h1>
+
+  <ul>
+    <li>Use default for simple scalars</li>
+    <li>Use default to return empty references</li>
+    <li>Use default for <em>very</em> trivial subroutine references</li>
+    <li>Use builder for everything else</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Builder Bonuses</h1>
+
+  <ul>
+    <li>Can be overridden and method modified, because it's called by <em>name</em></li>
+    <li>Roles can require a builder</li>
+  </ul>
+</div>
+      
+<div class="slide">
+  <h1>Role Requires Builder</h1>
+
+  <pre><code>package HasBank;
+use Moose::Role;
+
+requires '_build_bank';
+
+has bank =&gt; (
+    is      =&gt; 'ro',
+    builder =&gt; '_build_bank',
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Lazy, Good for Nothing Attributes</h1>
+
+  <ul>
+    <li>Normally, defaults are generated during object construction</li>
+    <li>This can be expensive</li>
+    <li>We want to default to <code>$self-&gt;size * 2</code>, but attribute initialization order is unpredictable</li>
+    <li>Use lazy attributes!</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>The Power of Dynamic Defaults</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has shoe_size =&gt; (
+    is =&gt; 'ro',
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>The Power of Dynamic Defaults</h1>
+
+  <pre><code>has shoes =&gt; (
+    is      =&gt; 'ro',
+    <span class="highlight">lazy    =&gt; 1,</span>
+    builder => '_build_shoes',
+);
+
+sub _build_shoes {
+    my $self = shift;
+
+    return Shoes-&gt;new(
+        size =&gt; <span class="highlight">$_[0]-&gt;shoe_size</span> );
+}</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Lazy is Good</h1>
+
+  <ul>
+    <li>Lazy defaults are executed when the attribute is read</li>
+    <li>Can see other object attributes</li>
+    <li>Still need to watch out for circular laziness</li>
+  </ul>
+</div>    
+
+<div class="slide">
+  <h1>Clearer and Predicate</h1>
+
+  <ul>
+    <li>Attributes can have a value, including <code>undef</code>, or not</li>
+    <li>Can clear the value with a clearer method</li>
+    <li>Can check for the existence of a value with a predicate method</li>
+    <li>By default, these methods are not created</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Clearer and Predicate</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has account =&gt; (
+    is        =&gt; 'ro',
+    lazy      =&gt; 1,
+    builder   =&gt; '_build_account',
+    <span class="highlight">clearer   =&gt; '_clear_account',
+    predicate =&gt; 'has_account',</span>
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Clearer and Lazy Defaults</h1>
+
+  <ul>
+    <li>Lazy defaults are good for computed attributes</li>
+    <li>Clear the attribute when the source data changes</li>
+    <li>Recalculated at next access</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Renaming constructor arguments</h1>
+
+  <ul>
+    <li>By default, constructor names = attribute names</li>
+    <li>Use <code>init_arg</code> to change this</li>
+    <li>Set <code>init_arg =&gt; undef</code> to make it unconstructable</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Some <code>init_arg</code> examples</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has shoe_size => (
+    is       =&gt; 'ro',
+    <span class="highlight">init_arg =&gt; 'foot_size',</span>
+);
+
+Person->new( <span class="wrong">shoe_size =&gt; 13</span> );
+
+my $person =
+    Person->new( <span class="right">foot_size =&gt; 13</span> );
+print $person->shoe_size;</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Some <code>init_arg</code> examples</h1>
+
+<pre><code>package Person;
+use Moose;
+
+has shoes => (
+    is       =&gt; 'ro',
+    <span class="highlight">init_arg =&gt; undef,</span>
+);
+
+Person->new( <span class="wrong">shoes =&gt; Shoes-&gt;new</span> );</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Why Set <code>init_arg =&gt; undef</code>?</h1>
+
+  <ul>
+    <li>Use this with a lazy default for attributes-as-cache</li>
+    <li>Compute the value as needed</li>
+    <li>Ensure that it is always generated correctly (not set by constructor)</li>
+    <li>Use triggers or method modifiers (coming soon) to clear the value</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Attribute Inheritance</h1>
+
+  <ul>
+    <li>By default, subclasses inherit attribute as-is</li>
+    <li>Can change some attribute parameters in subclasses
+      <ul>
+        <li>default</li>
+        <li>builder</li>
+        <li>required</li>
+        <li>lazy</li>
+        <li>others we've not yet covered</li>
+      </ul>
+    </li>
+  </ul>
+</div>   
+
+<div class="slide">
+  <h1>Attribute Inheritance Example</h1>
+
+  <pre><code>package Employee;
+use Moose;
+
+extends 'Person';
+
+has '<span class="highlight">+first_name</span>' =&gt; (
+    default =&gt; 'Joe',
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Attribute Inheritance Warning</h1>
+
+  <ul>
+    <li>An attribute is a contract about a class's API</li>
+    <li>Don't break that contract in a subclass</li>
+    <li>Especially important in the context of types</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Changing Accessor Names</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has first_name =&gt; (
+    <span class="highlight">reader</span> =&gt; 'first_name',
+    <span class="highlight">writer</span> =&gt; 'first_name',
+);</code></pre>
+
+  <ul>
+    <li>The long-hand version of <code>is =&gt; 'rw'</code></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Changing Accessor Names</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has first_name =&gt; (
+    <span class="highlight">reader</span> =&gt; 'first_name',
+    <span class="highlight">writer</span> =&gt; undef,
+);</code></pre>
+
+  <ul>
+    <li>The long-hand version of <code>is =&gt; 'ro'</code></li>
+  </ul>
+</div>
+
+
+<div class="slide">
+  <h1>Changing Accessor Names</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has first_name =&gt; (
+    <span class="highlight">reader</span> =&gt; 'get_first_name',
+    <span class="highlight">writer</span> =&gt; 'set_first_name,
+);</code></pre>
+</div>
+
+<div class="slide">
+  <h1>Changing Accessor Names</h1>
+
+  <pre><code>package Person;
+use Moose;
+
+has first_name =&gt; (
+    <span class="highlight">is</span>     =&gt; 'rw',
+    <span class="highlight">writer</span> =&gt; '_first_name',
+);</code></pre>
+
+  <ul>
+    <li>Can also mix-and-match</li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>ETOOMUCHTYPING</h1>
+
+  <ul>
+    <li><code>MooseX::FollowPBP</code><br /><code>get_foo</code> and <code>set_foo</code></li>
+    <li><code>MooseX::SemiAffordanceAccessor</code><br /><code>foo</code> and <code>set_foo</code></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>ETOOMUCHTYPING</h1>
+
+  <pre><code>package Person;
+use Moose;
+<span class="highlight">use MooseX::SemiAffordanceAccessor;</span>
+
+has first_name =&gt; (
+    is =&gt; 'rw',
+);</code></pre>
+
+  <ul>
+    <li>Creates <code>first_name</code> and <code>set_first_name</code></li>
+  </ul>
+</div>
+
+<div class="slide">
+  <h1>Exercises</h1>
+
+  <pre># cd exercises
+# perl bin/prove -lv t/03-basic-attributes.t
+
+Iterate til this passes all its tests</pre>
+</div>
+
 </div> 
 </body>
 </html>
@@ -1594,3 +2076,4 @@ This work is licensed under a Creative Commons Attribution-Share Alike
 http://creativecommons.org/licenses/by-sa/3.0/us/ for details.
 
 -->
+
index 379453b..338e312 100644 (file)
 * required
 * default & builder
 * lazy
+* predicate, clearer
 * init_arg
+* reader & writer
+* attribute inheritance
+* MX attribute naming modules
 
 == Exercises
 
@@ -52,6 +56,7 @@
 ** make first & last name required
 * Go back to Employee
 ** make title default to "Worker"
+** add predicate & clearer for title
 ** add salary_level attribute, number from 1-10
 ** salary, lazy default of salary_level * 1,000, init_arg is undef
 
 
 == Exercises
 
-= Advanced attributes
-
-* delegation
-* metaclass & traits
-
-== Exercises
-
 = Types
 
 * built-in types
 
 == Exercises
 
+= Advanced attributes
+
+* weak_ref
+* triggers
+* delegation
+* metaclass & traits
+
+== Exercises
+
 = Introspection
 
 == Exercises
index 602d2a5..29fd936 100644 (file)
@@ -58,8 +58,9 @@ img.for-slide {
     line-height: 110%;
 }
 
+.slide .current,
 .highlight {
-    color: #B02;
+    color: #03c;
 }
 
 .slide pre.small {
@@ -71,10 +72,11 @@ img.for-slide {
 }
 
 .match-moose {
-    color: #B02;
+    color: #03c;
 }
+
 .match-unsweet {
-    color: #B02;
+    color: #03c;
     font-weight: bold;
 }