On Thu, Mar 17, 2011 at 6:07 PM, Buddy Burden <barefootco...@gmail.com> wrote: > Moosites, > > I'm working on adding type checking to Method::Signatures, and I want > it to be able to handle roles. So, if I say: > > method foo (Blargy $bar) > > and Blargy is a role, I want it to verify that $bar is an object whose > class composes the Blargy role. I believe that's as simple as > checking that > > $bar->DOES('Blargy') > > right? But I've run into a couple of surprises, and I was hoping that > the Moose masters here could shed some light. :) > > First thing is that I note that Moose::Util::TypeConstraint::Role > doesn't actually do that. Rather, it does this: > > Moose::Util::does_role($_[0], $role) > > Is that different from just calling DOES? > > Second thing was that, since I have to account for the type being > _either_ a class or a role (or a basic type such as Int, etc, but > let's assume I already called find_or_parse_type_constraint earlier), > I tried something like this: > > if > (Moose::Util::TypeConstraint::find_type_constraint->("ClassName")->check($type)) > { > $constr = Moose::Util::TypeConstraint::class_type($type); > } > elsif > (Moose::Util::TypeConstraint::find_type_constraint->("RoleName")->check($type)) > { > $constr = Moose::Util::TypeConstraint::role_type($type); > } > > You can probably see right off that I have a bug there. I can't check > to see if it's a classname first, because every role _is_ a class. I > need to check for roles first. That wasn't the surprising part. The > surprising part was that _it still works this way_. ANAICT, role_type > never gets called, and all my role constraints end up being created by > class_type, and when I dump them out they are indeed > Moose::Meta::TypeConstraint::Class objects and not > Moose::Meta::TypeConstraint::Role objects, but still when I get to the > end and do: > > $constr->check($val); > > it passes. Now, I reversed the order anyway, just to make myself feel > better (and to avoid running into some super-subtle bug one day that I > might kill myself over not being able to track down), but I'm mightily > curious as to why it actually works. > > My real problem, though, it that I want this to work with all possible > roles, regardless of where they come from. Now, assuming I go back > and invoke the proper Any::Moose incantations (which I have), then > when Moose is loaded this works with Moose roles but not Mouse roles, > and when Mouse is loaded it works with Mouse roles but not Moose > roles, which I think _might_ be good enough. Or is it? I suppose > there's always a chance that you could be including some modules which > use Moose and some which use Mouse ... but that seems so unlikely that > I'm not sure it's worth worrying about. I'd be interested in hearing > what others think. > > And of course no matter which one I have loaded, it doesn't work with > Role::Basic roles. Because the RoleName type constraint doesn't > consider them to be roles, and, even if it did, it wouldn't DTRT (see > first surprise). So I'll have to check for that separately (seems > easy enough) and create my own type constraint for it, which I _think_ > should just do the DOES check. Something like so: > > type "RoleBasic$type" => where { $_->DOES($type) }; > > Right? Or should I make them all subtypes of a common type (I can't > see any advantage in that ATM, but perhaps I'm missing something)? > > Am I on the right track here, or does anyone smarter than I see any > pitfalls? TIA! > > > -- Buddy >
Did anyone have any thoughts about this? My current code looks like this: my %mutc; # This is a helper function to initialize our %mutc variable. sub _init_mutc { require Any::Moose; Any::Moose->import('::Util::TypeConstraints'); no strict 'refs'; my $class = any_moose('::Util::TypeConstraints'); $mutc{class} = $class; $mutc{findit} = \&{ $class . '::find_or_parse_type_constraint' }; $mutc{pull} = \&{ $class . '::find_type_constraint' }; $mutc{make_class} = \&{ $class . '::class_type' }; $mutc{make_role} = \&{ $class . '::role_type' }; $mutc{isa_class} = $mutc{pull}->("ClassName"); $mutc{isa_role} = $mutc{pull}->("RoleName"); } # This is a helper function to find (or create) the constraint we need for a given type. It would # be called when the type is not found in our cache. sub _make_constraint { my ($type) = @_; _init_mutc() unless $mutc{class}; # Look for basic types (Int, Str, Bool, etc). This will also create a new constraint for any # parameterized types (e.g. ArrayRef[Int]) or any disjunctions (e.g. Int|ScalarRef[Int]). my $constr = eval { $mutc{findit}->($type) }; if ($@) { _type_error("the type $type is unrecognized (looks like it doesn't parse correctly)"); } return $constr if $constr; # Check for roles. Note that you *must* check for roles before you check for classes, because a # role ISA class. return $mutc{make_role}->($type) if $mutc{isa_role}->check($type); # Now check for classes. return $mutc{make_class}->($type) if $mutc{isa_class}->check($type); _type_error("the type $type is unrecognized (perhaps you forgot to load it?)"); } So I'm thinking that AFA Role::Basic goes, I could just add something like this: # Check for Role::Basic roles. if ($type->isa('Role::Basic') { return $mutc{type}->( $type, { where => sub { $_->DOES($type) } } ); } It would go just before the check against the RoleName constraint. It would of course assume that $mutc{type} was defined just like others in _init_mutc: $mutc{type} = \&{ $class . '::type' }; Does that seem rational? I would especially love to hear from Ovid, if he's following this discussion. -- Buddy