Chris, > You can use the fact that the attribute's default is called as a method on > the instance itself to look up the associated metaclass, then the attribute, > then the type constraint... At that point the type constraint, if any, > should exist. :)
Wait ... what? the default sub is called as a method? no shit? <test test test> No shit! It really is ... I never knew that. Very cool! Okay, for the curious, here's basically what I ended up with. I will need to add tests and polish up the error checking, but it's probably most of the way towards being releasable. Unfortunately, I don't know when I'm going to find the time to finish it up--testing user input stuff is a big PITA; I'll probably need to gen up a dummy test chrome and all that. But, anyway, here's some code if someone didn't feel like waiting for me to get organized and wanted to just run with it. Comments are for my more junior teammates and not necessarily for you guys. Thanks to everyone for all the great help! -- Buddy use MooseX::Declare; use Method::Signatures::Modifiers; role MooseX::PromptableAttributes::Trait::Attribute { Moose::Util::meta_attribute_alias 'Promptable'; has prompt => ( is => 'ro', isa => 'Str' ); around _process_options ($class: $name, $options) { # You have to make a copy of $options->{prompt} because of the way closures work. See, # $options is a lexical variable, and that will be saved inside the sub we return. But it's # still just a pointer to the actual structure we were passed. By the time our sub is # actually _called_ (i.e. at object instantiation time), that structure won't exist any # more. So we need to save a copy of the prompt string in another lexical so it can get # bundled up in the closure instead. See? if (my $prompt = $options->{'prompt'}) { # Ditto about $options->{default}. # We'll allow a default for an attribute with a prompt, but only if it's a simple # scalar. If provided, this default will become the default for our prompt (i.e., what # the user gets if they just hit RETURN). my $default; if (exists $options->{'default'}) { $default = $options->{'default'}; die("default for a promptable attribute must be a scalar") if ref $default; } # This is only needed for the error message below. Sure, it might not exist, but then # there'll be no type constrait and consequently no error, so it would never be used in # that case. my $type = $options->{'isa'}; $options->{'default'} = method { my $validator = sub { # Yes, this is a closure inside a closure. It's not as bad as it seems, though. # Just take it slow. my ($val) = @_; # remember: the $self below is for the default method, *not* the validator sub # (the validator sub doesn't have a $self: it's not a method) my $attr = $self->meta->get_attribute($name); # just care whether verify_against_type_constraint() blows up or not if ( eval { $attr->verify_against_type_constraint($val); 1; } ) { # didn't blow up; value is good return 1; } else { print "Sorry; value must be a(n) $type\n"; return 0; } }; return $MooseX::PromptableAttributes::chrome->prompt($prompt, $validator, $default); }; } return $class->$orig($name, $options); } } # All packages succeed 1;