return $predicate;
}
+sub generate_can_predicate_for {
+ my($methods_ref, $name) = @_;
+
+ my @methods = @{$methods_ref};
+
+ my $predicate = sub{
+ my($instance) = @_;
+ if(Scalar::Util::blessed($instance)){
+ foreach my $method(@methods){
+ if(!$instance->can($method)){
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+ };
+
+ if(defined $name){
+ no strict 'refs';
+ *{ caller() . '::' . $name } = $predicate;
+ return;
+ }
+
+ return $predicate;
+}
package
Mouse::Util::TypeConstraints;
as_is => [qw(
as where message optimize_as
from via
- type subtype coerce class_type role_type enum
+
+ type subtype class_type role_type duck_type
+ enum
+ coerce
+
find_type_constraint
)],
);
);
}
+sub duck_type {
+ my($name, @methods);
+
+ if(!(@_ == 1 && ref($_[0]) eq 'ARRAY')){
+ $name = shift;
+ }
+
+ @methods = (@_ == 1 && ref($_[0]) eq 'ARRAY') ? @{$_[0]} : @_;
+
+ return _create_type 'type', $name => (
+ optimized_as => Mouse::Util::generate_can_predicate_for(\@methods),
+
+ type => 'DuckType',
+ );
+}
+
sub typecast_constraints { # DEPRECATED
my($class, $pkg, $type, $value) = @_;
Carp::croak("wrong arguments count") unless @_ == 4;
#define MY_CXT_KEY "Mouse::Util::TypeConstraints::_guts" XS_VERSION
typedef struct sui_cxt{
GV* universal_isa;
+ GV* universal_can;
} my_cxt_t;
START_MY_CXT
return SvROK(sv) && SvOBJECT(SvRV(sv));
}
+static int
+mouse_can_methods(pTHX_ AV* const methods, SV* const instance){
+ if(IsObject(instance)){
+ dMY_CXT;
+ HV* const mystash = SvSTASH(SvRV(instance));
+ GV* const mycan = gv_fetchmeth_autoload(mystash, "can", sizeof("can")-1, 0);
+ bool const use_builtin = (mycan == NULL || GvCV(mycan) == GvCV(MY_CXT.universal_isa)) ? TRUE : FALSE;
+ I32 const len = AvFILLp(methods) + 1;
+ I32 i;
+ for(i = 0; i < len; i++){
+ SV* const name = MOUSE_av_at(methods, i);
+
+ if(use_builtin){
+ if(!gv_fetchmeth_autoload(mystash, SvPVX(name), SvCUR(name), 0)){
+ return FALSE;
+ }
+ }
+ else{
+ bool ok;
+ dSP;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ EXTEND(SP, 2);
+ PUSHs(instance);
+ PUSHs(sv_mortalcopy(name));
+ PUTBACK;
+
+ call_method("can", G_SCALAR);
+
+ SPAGAIN;
+ ok = SvTRUE(TOPs);
+ (void)POPs;
+ PUTBACK;
+
+ FREETMPS;
+ LEAVE;
+
+ if(!ok){
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
static MGVTBL mouse_util_type_constraints_vtbl; /* not used, only for identity */
static CV*
return mouse_tc_generate(aTHX_ predicate_name, fptr, param);
}
+CV*
+mouse_generate_can_predicate_for(pTHX_ SV* const methods, const char* const predicate_name){
+ AV* av;
+ AV* const param = newAV_mortal();
+ I32 len;
+ I32 i;
+
+ SvGETMAGIC(methods);
+ if(!IsArrayRef(methods)){
+ croak("You must pass an ARRAY ref method names");
+ }
+ av = (AV*)SvRV(methods);
+
+ len = av_len(av) + 1;
+ for(i = 0; i < len; i++){
+ SV* const name = *av_fetch(av, i, TRUE);
+ STRLEN pvlen;
+ const char* const pv = SvPV_const(name, pvlen);
+
+ av_push(param, newSVpvn_share(pv, pvlen, 0U));
+ }
+
+ return mouse_tc_generate(aTHX_ predicate_name, (check_fptr_t)mouse_can_methods, (SV*)param);
+}
+
+
XS(XS_Mouse_constraint_check) {
dVAR;
dXSARGS;
setup_my_cxt(pTHX_ pMY_CXT){
MY_CXT.universal_isa = gv_fetchpvs("UNIVERSAL::isa", GV_ADD, SVt_PVCV);
SvREFCNT_inc_simple_void_NN(MY_CXT.universal_isa);
+
+ MY_CXT.universal_can = gv_fetchpvs("UNIVERSAL::can", GV_ADD, SVt_PVCV);
+ SvREFCNT_inc_simple_void_NN(MY_CXT.universal_can);
}
#define DEFINE_TC(name) mouse_tc_generate(aTHX_ "Mouse::Util::TypeConstraints::" STRINGIFY(name), CAT2(mouse_tc_, name), NULL)
RETVAL
void
-generate_isa_predicate_for(SV* klass, SV* predicate_name = NULL)
+generate_isa_predicate_for(SV* arg, SV* predicate_name = NULL)
+ALIAS:
+ generate_isa_predicate_for = 0
+ generate_can_predicate_for = 1
PPCODE:
{
const char* name_pv = NULL;
CV* xsub;
- SvGETMAGIC(klass);
+ SvGETMAGIC(arg);
- if(!SvOK(klass)){
- croak("You must define a class name");
+ if(!SvOK(arg)){
+ croak("You must define %s", ix == 0 ? "a class name" : "method names");
}
if(predicate_name){
SvGETMAGIC(predicate_name);
if(!SvOK(predicate_name)){
- croak("You must define a predicate_name");
+ croak("You must define %s", "a predicate name");
}
name_pv = SvPV_nolen_const(predicate_name);
}
- xsub = mouse_generate_isa_predicate_for(aTHX_ klass, name_pv);
+ if(ix == 0){
+ xsub = mouse_generate_isa_predicate_for(aTHX_ arg, name_pv);
+ }
+ else{
+ xsub = mouse_generate_can_predicate_for(aTHX_ arg, name_pv);
+ }
if(predicate_name == NULL){ /* anonymous predicate */
XPUSHs( newRV_noinc((SV*)xsub) );