Make sure that eager builder does not run after clearer
[gitmo/Mouse.git] / lib / Mouse / Attribute.pm
index 08eab31..1a971bf 100644 (file)
@@ -49,6 +49,7 @@ sub generate_accessor {
     my $trigger    = $attribute->trigger;
     my $type       = $attribute->type_constraint;
     my $constraint = $attribute->find_type_constraint;
+    my $builder    = $attribute->builder;
 
     my $accessor = 'sub {
         my $self = shift;';
@@ -81,9 +82,13 @@ sub generate_accessor {
 
     if ($attribute->is_lazy) {
         $accessor .= '$self->{$key} = ';
-        $accessor .= ref($default) eq 'CODE'
-                   ? '$default->($self)'
-                   : '$default';
+
+        $accessor .= $attribute->has_builder
+                   ? '$self->$builder'
+                     : ref($default) eq 'CODE'
+                     ? '$default->($self)'
+                     : '$default';
+
         $accessor .= ' if !exists($self->{$key});';
     }
 
@@ -135,7 +140,7 @@ sub create {
     my ($self, $class, $name, %args) = @_;
 
     confess "You must specify a default for lazy attribute '$name'"
-        if $args{lazy} && !exists($args{default});
+        if $args{lazy} && !exists($args{default}) && !exists($args{builder});
 
     confess "Trigger is not allowed on read-only attribute '$name'"
         if $args{trigger} && $args{is} ne 'rw';
@@ -144,15 +149,11 @@ sub create {
         if ref($args{default})
         && ref($args{default}) ne 'CODE';
 
-    $args{handles} = { map { $_ => $_ } @{ $args{handles} } }
-        if $args{handles}
-        && ref($args{handles}) eq 'ARRAY';
+    $args{handles} = { $self->_canonicalize_handles($args{handles}) }
+        if $args{handles};
 
-    confess "You must pass a HASH or ARRAY to handles"
-        if exists($args{handles})
-        && ref($args{handles}) ne 'HASH';
-
-    $args{type_constraint} = delete $args{isa};
+    $args{type_constraint} = delete $args{isa}
+        if exists $args{isa};
 
     my $attribute = $self->new(%args, name => $name, class => $class);
     my $meta = $class->meta;
@@ -205,8 +206,7 @@ sub verify_type_constraint {
 
     my $type = $self->type_constraint
         or return 1;
-    my $constraint = $self->find_type_constraint
-        or return 1;
+    my $constraint = $self->find_type_constraint;
 
     return 1 if $constraint->($_);
 
@@ -215,6 +215,21 @@ sub verify_type_constraint {
     Carp::confess("Attribute ($name) does not pass the type constraint because: Validation failed for \'$type\' failed with value $_");
 }
 
+sub _canonicalize_handles {
+    my $self    = shift;
+    my $handles = shift;
+
+    if (ref($handles) eq 'HASH') {
+        return %$handles;
+    }
+    elsif (ref($handles) eq 'ARRAY') {
+        return map { $_ => $_ } @$handles;
+    }
+    else {
+        confess "Unable to canonicalize the 'handles' option with $handles";
+    }
+}
+
 1;
 
 __END__
@@ -292,5 +307,15 @@ Creates a new code reference for the attribute's clearer.
 
 Creates a new code reference for each of the attribute's handles methods.
 
+=head2 find_type_constraint -> CODE
+
+Returns a code reference which can be used to check that a given value passes
+this attribute's type constraint;
+
+=head2 verify_type_constraint Item -> 1 | ERROR
+
+Checks that the given value passes this attribute's type constraint. Returns 1
+on success, otherwise C<confess>es.
+
 =cut