If we re-import Mouse, don't thrash the class's superclasses.
[gitmo/Mouse.git] / lib / Mouse / Attribute.pm
index 41a56df..4bb1856 100644 (file)
@@ -9,41 +9,65 @@ sub new {
     my $class = shift;
     my %args  = @_;
 
-    $args{init_arg} ||= $args{name};
+    $args{init_arg} = $args{name}
+        unless exists $args{init_arg};
     $args{is} ||= '';
 
     bless \%args, $class;
 }
 
-sub name      { $_[0]->{name} }
-sub class     { $_[0]->{class} }
-sub default   { $_[0]->{default} }
-sub predicate { $_[0]->{predicate} }
-sub clearer   { $_[0]->{clearer} }
-sub handles   { $_[0]->{handles} }
-sub weak_ref  { $_[0]->{weak_ref} }
-sub init_arg  { $_[0]->{init_arg} }
+sub name            { $_[0]->{name}            }
+sub class           { $_[0]->{class}           }
+sub default         { $_[0]->{default}         }
+sub predicate       { $_[0]->{predicate}       }
+sub clearer         { $_[0]->{clearer}         }
+sub handles         { $_[0]->{handles}         }
+sub weak_ref        { $_[0]->{weak_ref}        }
+sub init_arg        { $_[0]->{init_arg}        }
+sub type_constraint { $_[0]->{type_constraint} }
+
+sub has_name            { exists $_[0]->{name}            }
+sub has_class           { exists $_[0]->{class}           }
+sub has_default         { exists $_[0]->{default}         }
+sub has_predicate       { exists $_[0]->{predicate}       }
+sub has_clearer         { exists $_[0]->{clearer}         }
+sub has_handles         { exists $_[0]->{handles}         }
+sub has_weak_ref        { exists $_[0]->{weak_ref}        }
+sub has_init_arg        { exists $_[0]->{init_arg}        }
+sub has_type_constraint { exists $_[0]->{type_constraint} }
 
 sub generate_accessor {
     my $attribute = shift;
 
-    my $key     = $attribute->{init_arg};
-    my $default = $attribute->{default};
-    my $trigger = $attribute->{trigger};
+    my $name       = $attribute->{name};
+    my $key        = $attribute->{init_arg};
+    my $default    = $attribute->{default};
+    my $trigger    = $attribute->{trigger};
+    my $type       = $attribute->{type_constraint};
+    my $constraint = $attribute->find_type_constraint;
 
     my $accessor = 'sub {
         my $self = shift;';
 
     if ($attribute->{is} eq 'rw') {
         $accessor .= 'if (@_) {
-            $self->{$key} = $_[0];';
+            local $_ = $_[0];';
+
+        if ($constraint) {
+            $accessor .= 'do {
+                my $display = defined($_) ? $_ : "undef";
+                Carp::confess("Attribute ($name) does not pass the type constraint because: Validation failed for \'$type\' failed with value $display") unless $constraint->();
+            };'
+        }
+
+        $accessor .= '$self->{$key} = $_;';
 
         if ($attribute->{weak_ref}) {
             $accessor .= 'Scalar::Util::weaken($self->{$key});';
         }
 
         if ($trigger) {
-            $accessor .= '$trigger->($self, $_[0], $attribute);';
+            $accessor .= '$trigger->($self, $_, $attribute);';
         }
 
         $accessor .= '}';
@@ -124,9 +148,13 @@ sub create {
         if exists($args{handles})
         && ref($args{handles}) ne 'HASH';
 
+    $args{type_constraint} = delete $args{isa};
+
     my $attribute = $self->new(%args, name => $name, class => $class);
     my $meta = $class->meta;
 
+    $meta->add_attribute($attribute);
+
     # install an accessor
     if ($attribute->{is} eq 'rw' || $attribute->{is} eq 'ro') {
         my $accessor = $attribute->generate_accessor;
@@ -134,8 +162,6 @@ sub create {
         *{ $class . '::' . $name } = $accessor;
     }
 
-    $meta->add_attribute($attribute);
-
     for my $method (qw/predicate clearer/) {
         if (exists $attribute->{$method}) {
             my $generator = "generate_$method";
@@ -156,6 +182,34 @@ sub create {
     return $attribute;
 }
 
+sub find_type_constraint {
+    my $self = shift;
+    my $type = $self->type_constraint;
+
+    return unless $type;
+
+    my $checker = Mouse::TypeRegistry->optimized_constraints->{$type};
+    return $checker if $checker;
+
+    confess "Unable to parse type constraint '$type'";
+}
+
+sub verify_type_constraint {
+    my $self = shift;
+    local $_ = shift;
+
+    my $type = $self->type_constraint
+        or return 1;
+    my $constraint = $self->find_type_constraint
+        or return 1;
+
+    return 1 if $constraint->($_);
+
+    my $name = $self->name;
+    local $_ = "undef" unless defined($_);
+    Carp::confess("Attribute ($name) does not pass the type constraint because: Validation failed for \'$type\' failed with value $_");
+}
+
 1;
 
 __END__