Move Mouse::Meta::Attribute::_process_options into XS
gfx [Mon, 11 Jan 2010 05:32:01 +0000 (14:32 +0900)]
lib/Mouse/Meta/Attribute.pm
lib/Mouse/PurePerl.pm
xs-src/MouseAttribute.xs

index 961a5d5..498ad9d 100644 (file)
@@ -5,126 +5,6 @@ use Carp ();
 
 use Mouse::Meta::TypeConstraint;
 
-sub _process_options{
-    my($class, $name, $args) = @_;
-
-    # XXX: for backward compatibility (with method modifiers)
-    if($class->can('canonicalize_args') != \&canonicalize_args){
-        %{$args} = $class->canonicalize_args($name, %{$args});
-    }
-
-    # taken from Class::MOP::Attribute::new
-
-    defined($name)
-        or $class->throw_error('You must provide a name for the attribute');
-
-    if(!exists $args->{init_arg}){
-        $args->{init_arg} = $name;
-    }
-
-    # 'required' requires eigher 'init_arg', 'builder', or 'default'
-    my $can_be_required = defined( $args->{init_arg} );
-
-    if(exists $args->{builder}){
-        # XXX:
-        # Moose refuses a CODE ref builder, but Mouse doesn't for backward compatibility
-        # This feature will be changed in a future. (gfx)
-        $class->throw_error('builder must be a defined scalar value which is a method name')
-            #if ref $args->{builder} || !defined $args->{builder};
-            if !defined $args->{builder};
-
-        $can_be_required++;
-    }
-    elsif(exists $args->{default}){
-        if(ref $args->{default} && ref($args->{default}) ne 'CODE'){
-            $class->throw_error("References are not allowed as default values, you must "
-                              . "wrap the default of '$name' in a CODE reference (ex: sub { [] } and not [])");
-        }
-        $can_be_required++;
-    }
-
-    if( $args->{required} && !$can_be_required ) {
-        $class->throw_error("You cannot have a required attribute ($name) without a default, builder, or an init_arg");
-    }
-
-    # taken from Mouse::Meta::Attribute->new and _process_args->
-
-    if(exists $args->{is}){
-        my $is = $args->{is};
-
-        if($is eq 'ro'){
-            $args->{reader} ||= $name;
-        }
-        elsif($is eq 'rw'){
-            if(exists $args->{writer}){
-                $args->{reader} ||= $name;
-             }
-             else{
-                $args->{accessor} ||= $name;
-             }
-        }
-        elsif($is eq 'bare'){
-            # do nothing, but don't complain (later) about missing methods
-        }
-        else{
-            $is = 'undef' if !defined $is;
-            $class->throw_error("I do not understand this option (is => $is) on attribute ($name)");
-        }
-    }
-
-    my $tc;
-    if(exists $args->{isa}){
-        $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($args->{isa});
-    }
-    elsif(exists $args->{does}){
-        $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_does_type_constraint($args->{does});
-    }
-    $tc = $args->{type_constraint};
-
-    if($args->{coerce}){
-        defined($tc)
-            || $class->throw_error("You cannot have coercion without specifying a type constraint on attribute ($name)");
-
-        $args->{weak_ref}
-            && $class->throw_error("You cannot have a weak reference to a coerced value on attribute ($name)");
-    }
-
-    if ($args->{lazy_build}) {
-        exists($args->{default})
-            && $class->throw_error("You can not use lazy_build and default for the same attribute ($name)");
-
-        $args->{lazy}      = 1;
-        $args->{builder} ||= "_build_${name}";
-        if ($name =~ /^_/) {
-            $args->{clearer}   ||= "_clear${name}";
-            $args->{predicate} ||= "_has${name}";
-        }
-        else {
-            $args->{clearer}   ||= "clear_${name}";
-            $args->{predicate} ||= "has_${name}";
-        }
-    }
-
-    if ($args->{auto_deref}) {
-        defined($tc)
-            || $class->throw_error("You cannot auto-dereference without specifying a type constraint on attribute ($name)");
-
-        ( $tc->is_a_type_of('ArrayRef') || $tc->is_a_type_of('HashRef') )
-            || $class->throw_error("You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)");
-    }
-
-    if (exists $args->{trigger}) {
-        ('CODE' eq ref $args->{trigger})
-            || $class->throw_error("Trigger must be a CODE ref on attribute ($name)");
-    }
-
-    if ($args->{lazy}) {
-        (exists $args->{default} || defined $args->{builder})
-            || $class->throw_error("You cannot have lazy attribute ($name) without specifying a default value for it");
-    }
-
-    return;
-}
 
 sub new {
     my $class = shift;
@@ -132,6 +12,12 @@ sub new {
 
     my %args  = (@_ == 1) ? %{ $_[0] } : @_;
 
+
+    # XXX: for backward compatibility (with method modifiers)
+    if($class->can('canonicalize_args') != \&canonicalize_args){
+        %args = $class->canonicalize_args($name, %args);
+    }
+
     $class->_process_options($name, \%args);
 
     $args{name} = $name;
index 97534e4..2895fee 100644 (file)
@@ -387,6 +387,123 @@ sub has_builder          { exists $_[0]->{builder}         }
 
 sub has_documentation    { exists $_[0]->{documentation}   }
 
+sub _process_options{
+    my($class, $name, $args) = @_;
+
+    # taken from Class::MOP::Attribute::new
+
+    defined($name)
+        or $class->throw_error('You must provide a name for the attribute');
+
+    if(!exists $args->{init_arg}){
+        $args->{init_arg} = $name;
+    }
+
+    # 'required' requires eigher 'init_arg', 'builder', or 'default'
+    my $can_be_required = defined( $args->{init_arg} );
+
+    if(exists $args->{builder}){
+        # XXX:
+        # Moose refuses a CODE ref builder, but Mouse doesn't for backward compatibility
+        # This feature will be changed in a future. (gfx)
+        $class->throw_error('builder must be a defined scalar value which is a method name')
+            #if ref $args->{builder} || !defined $args->{builder};
+            if !defined $args->{builder};
+
+        $can_be_required++;
+    }
+    elsif(exists $args->{default}){
+        if(ref $args->{default} && ref($args->{default}) ne 'CODE'){
+            $class->throw_error("References are not allowed as default values, you must "
+                              . "wrap the default of '$name' in a CODE reference (ex: sub { [] } and not [])");
+        }
+        $can_be_required++;
+    }
+
+    if( $args->{required} && !$can_be_required ) {
+        $class->throw_error("You cannot have a required attribute ($name) without a default, builder, or an init_arg");
+    }
+
+    # taken from Mouse::Meta::Attribute->new and ->_process_args
+
+    if(exists $args->{is}){
+        my $is = $args->{is};
+
+        if($is eq 'ro'){
+            $args->{reader} ||= $name;
+        }
+        elsif($is eq 'rw'){
+            if(exists $args->{writer}){
+                $args->{reader} ||= $name;
+             }
+             else{
+                $args->{accessor} ||= $name;
+             }
+        }
+        elsif($is eq 'bare'){
+            # do nothing, but don't complain (later) about missing methods
+        }
+        else{
+            $is = 'undef' if !defined $is;
+            $class->throw_error("I do not understand this option (is => $is) on attribute ($name)");
+        }
+    }
+
+    my $tc;
+    if(exists $args->{isa}){
+        $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($args->{isa});
+    }
+    elsif(exists $args->{does}){
+        $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_does_type_constraint($args->{does});
+    }
+    $tc = $args->{type_constraint};
+
+    if($args->{coerce}){
+        defined($tc)
+            || $class->throw_error("You cannot have coercion without specifying a type constraint on attribute ($name)");
+
+        $args->{weak_ref}
+            && $class->throw_error("You cannot have a weak reference to a coerced value on attribute ($name)");
+    }
+
+    if ($args->{lazy_build}) {
+        exists($args->{default})
+            && $class->throw_error("You can not use lazy_build and default for the same attribute ($name)");
+
+        $args->{lazy}      = 1;
+        $args->{builder} ||= "_build_${name}";
+        if ($name =~ /^_/) {
+            $args->{clearer}   ||= "_clear${name}";
+            $args->{predicate} ||= "_has${name}";
+        }
+        else {
+            $args->{clearer}   ||= "clear_${name}";
+            $args->{predicate} ||= "has_${name}";
+        }
+    }
+
+    if ($args->{auto_deref}) {
+        defined($tc)
+            || $class->throw_error("You cannot auto-dereference without specifying a type constraint on attribute ($name)");
+
+        ( $tc->is_a_type_of('ArrayRef') || $tc->is_a_type_of('HashRef') )
+            || $class->throw_error("You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute ($name)");
+    }
+
+    if (exists $args->{trigger}) {
+        ('CODE' eq ref $args->{trigger})
+            || $class->throw_error("Trigger must be a CODE ref on attribute ($name)");
+    }
+
+    if ($args->{lazy}) {
+        (exists $args->{default} || defined $args->{builder})
+            || $class->throw_error("You cannot have lazy attribute ($name) without specifying a default value for it");
+    }
+
+    return;
+}
+
+
 package
     Mouse::Meta::TypeConstraint;
 
index 2caf7be..a11471f 100644 (file)
@@ -220,3 +220,208 @@ BOOT:
 
     INSTALL_CLASS_HOLDER(Attribute, accessor_metaclass, "Mouse::Meta::Method::Accessor::XS");
 
+void
+_process_options(SV* klass, SV* name, HV* args)
+CODE:
+{
+    SV** svp;
+    SV* tc = NULL;
+
+    /* 'required' requires eigher 'init_arg', 'builder', or 'default' */
+    bool can_be_required = FALSE;
+    bool has_default     = FALSE;
+    bool has_builder     = FALSE;
+
+    /* taken from Class::MOP::Attribute::new */
+
+    if(!SvOK(name)){
+        mouse_throw_error(klass, NULL,
+            "You must provide a name for the attribute");
+    }
+
+    svp = hv_fetchs(args, "init_arg", FALSE);
+    if(!svp){
+        (void)hv_stores(args, "init_arg", newSVsv(name));
+        can_be_required = TRUE;
+    }
+    else{
+        can_be_required = SvOK(*svp) ? TRUE : FALSE;
+    }
+
+    svp = hv_fetchs(args, "builder", FALSE);
+    if(svp){
+        if(!SvOK(*svp)){
+            mouse_throw_error(klass, NULL,
+                "builder must be a defined scalar value which is a method name");
+        }
+        can_be_required = TRUE;
+        has_builder     = TRUE;
+    }
+    else if((svp = hv_fetchs(args, "default", FALSE))){
+        if(SvROK(*svp) && SvTYPE(SvRV(*svp)) != SVt_PVCV) {
+            mouse_throw_error(klass, NULL,
+               "References are not allowed as default values, you must "
+                "wrap the default of '%"SVf"' in a CODE reference "
+                "(ex: sub { [] } and not [])", name);
+        }
+        can_be_required = TRUE;
+        has_default     = TRUE;
+    }
+
+    svp = hv_fetchs(args, "required", FALSE);
+    if( (svp && sv_true(*svp)) && !can_be_required){
+        mouse_throw_error(klass, NULL,
+            "You cannot have a required attribute (%"SVf") "
+            "without a default, builder, or an init_arg", name);
+    }
+
+     /* taken from Mouse::Meta::Attribute->new and ->_process_args */
+
+    svp = hv_fetchs(args, "is", FALSE);
+    if(svp){
+        const char* const is = SvOK(*svp) ? SvPV_nolen_const(*svp) : "undef";
+        if(strEQ(is, "ro")){
+            svp = hv_fetchs(args, "reader", TRUE);
+            if(!sv_true(*svp)){
+                sv_setsv(*svp, name);
+            }
+        }
+        else if(strEQ(is, "rw")){
+            if(hv_fetchs(args, "writer", FALSE)){
+                svp = hv_fetchs(args, "reader", TRUE);
+            }
+            else{
+                svp = hv_fetchs(args, "accessor", TRUE);
+            }
+            sv_setsv(*svp, name);
+        }
+        else if(strEQ(is, "bare")){
+            /* do nothing, but don't complain (later) about missing methods */
+        }
+        else{
+            mouse_throw_error(klass, NULL,
+                "I do not understand this option (is => %s) on attribute (%"SVf")",
+                is, name);
+        }
+    }
+
+    svp = hv_fetchs(args, "isa", FALSE);
+    if(svp){
+        SPAGAIN;
+        PUSHMARK(SP);
+        XPUSHs(*svp);
+        PUTBACK;
+
+        call_pv("Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint",
+            G_SCALAR);
+        SPAGAIN;
+        tc = newSVsv(POPs);
+        PUTBACK;
+    }
+    else if((svp = hv_fetchs(args, "does", FALSE))){
+        SPAGAIN;
+        PUSHMARK(SP);
+        XPUSHs(*svp);
+        PUTBACK;
+
+        call_pv("Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint",
+            G_SCALAR);
+        SPAGAIN;
+        tc = newSVsv(POPs);
+        PUTBACK;
+    }
+    if(tc){
+        (void)hv_stores(args, "type_constraint", tc);
+    }
+
+    svp = hv_fetchs(args, "coerce", FALSE);
+    if(svp){
+        if(!tc){
+            mouse_throw_error(klass, NULL,
+                "You cannot have coercion without specifying a type constraint "
+                "on attribute (%"SVf")", name);
+        }
+        svp = hv_fetchs(args, "weak_ref", FALSE);
+        if(svp && sv_true(*svp)){
+            mouse_throw_error(klass, NULL,
+                "You cannot have a weak reference to a coerced value on "
+                "attribute (%"SVf")", name);
+        }
+    }
+
+    svp = hv_fetchs(args, "lazy_build", FALSE);
+    if(svp){
+        SV* clearer;
+        SV* predicate;
+        if(has_default){
+            mouse_throw_error(klass, NULL,
+                "You can not use lazy_build and default for the same "
+                "attribute (%"SVf")", name);
+        }
+
+        svp = hv_fetchs(args, "lazy", TRUE);
+        sv_setiv(*svp, TRUE);
+
+        svp = hv_fetchs(args, "builder", TRUE);
+        if(!sv_true(*svp)){
+            sv_setpvf(*svp, "_build_%"SVf, name);
+        }
+        has_builder = TRUE;
+
+        clearer   = *hv_fetchs(args, "clearer",   TRUE);
+        predicate = *hv_fetchs(args, "predicate", TRUE);
+
+        if(SvPV_nolen_const(name)[0] == '_'){
+            if(!sv_true(clearer)){
+                sv_setpvf(clearer, "_clear%"SVf, name);
+            }
+            if(!sv_true(predicate)){
+                sv_setpvf(predicate, "_has%"SVf, name);
+            }
+        }
+        else{
+            if(!sv_true(clearer)){
+                sv_setpvf(clearer, "clear_%"SVf, name);
+            }
+            if(!sv_true(predicate)){
+                sv_setpvf(predicate, "has_%"SVf, name);
+            }
+        }
+    }
+
+    svp = hv_fetchs(args, "auto_deref", FALSE);
+    if(svp && sv_true(*svp)){
+        SV* const meth = sv_2mortal(newSVpvs_share("is_a_type_of"));
+        if(!tc){
+            mouse_throw_error(klass, NULL,
+                "You cannot auto-dereference without specifying a type "
+                "constraint on attribute (%"SVf")", name);
+        }
+
+        if(!(sv_true(mcall1(tc, meth, newSVpvs_flags("ArrayRef", SVs_TEMP)))
+            || sv_true(mcall1(tc, meth, newSVpvs_flags("HashRef", SVs_TEMP))) )){
+            mouse_throw_error(klass, NULL,
+                "You cannot auto-dereference anything other than a ArrayRef "
+                "or HashRef on attribute (%"SVf")", name);
+        }
+    }
+
+    svp = hv_fetchs(args, "trigger", FALSE);
+    if(svp){
+        if(!IsCodeRef(*svp)){
+            mouse_throw_error(klass, NULL,
+                "Trigger must be a CODE ref on attribute (%"SVf")",
+                name);
+        }
+    }
+
+
+    svp = hv_fetchs(args, "lazy", FALSE);
+    if(svp && sv_true(*svp)){
+        if(!(has_default || has_builder)){
+            mouse_throw_error(klass, NULL,
+                "You cannot have lazy attribute (%"SVf") without specifying "
+                "a default value for it", name);
+        }
+    }
+}