Stevan, Thanks to your comments I managed to fix my code - it was driving me nuts so I really appreciate your help. I now need to go back through my changes to find out exactly which bit fixed the problem so I understand it better.
The process of untangling module dependencies in my local code base wasn't terribly pleasant even though I don't _think_ I'm doing anything too unusual or circuitous. So while I'm obviously happy that my tests now pass, I'm worried that things are left a little fragile (although I'm aware the perceived fragility is most likely caused by my lack of understanding, which I'm working on). One of my goals at $work was to write an internal framework that would be easy for others to use, understand and extend - I'm now concerned that I'm leaving a fairly large and unpleasant gotcha lurking under the surface. Happy for hear any more ideas about techniques to help avoid this in the future. Cheers, Ian 2009/1/29 Stevan Little <stevan.lit...@iinteractive.com> > I tend to do the following: > > - make a MyApp::Types package that handles *all* custom type definitions > and type coercions > - make all other packages load MyApp::Types (even if they dont need it, > because their deps might need it) > > Then as far as circular refs are concerned, they are tricky and each case > is a little different. The key is to map out what you need and the force > things to load in that order (this is where the global MyApp::Types can be > really handy). > > Also, find_type_constraint is your friend, it is exported from > Moose::Util::TypeConstraints and can basically be used to detect if the type > already exists or not. Something like: > > if (find_type_constraint('Foo')) { > # do something wiht the Foo type here, like add a coercion > } > > Of course the ideal situation is that you break your circular refs instead, > but sometimes that is just not possible. > > - Stevan > > > On Jan 29, 2009, at 11:59 AM, i...@sillit.com wrote: > > Stevan, >> >> Many thanks for getting back so quickly - that sounds exactly the sort >> of thing that must be happening. >> >> Before I dive in - could I ask what the recommended best practice for >> avoiding circular "use" cases is (if any)? I've generally tried to >> "use" all the modules required by the package to keep each package as >> independent as possible without loading everything unneccessarily. Or >> am I missing something? >> >> Cheers >> >> Ian >> >> On 29/01/2009, Stevan Little <stevan.lit...@iinteractive.com> wrote: >> >>> Ian, >>> >>> Types can sometimes be very sensitive to load order since they are >>> really done at runtime (it is the runtime immediately following >>> compile time, but still runtime). Based on your description I think >>> you are getting bitten by the auto-creation of types (if Moose doesn't >>> know the type it makes it into a Object subtype that isa(<name of >>> type>)). >>> >>> I would check to make sure that you do not have any circular >>> dependencies in how you load your modules, Perl will handle these >>> fine, but Moose types will be sensitive to the order in which the >>> circle is traversed. >>> >>> - Stevan >>> >>> On Jan 29, 2009, at 6:54 AM, Ian Sillitoe wrote: >>> >>> Hello, >>>> >>>> I have a problem with subtype/coercions (MooseX::Types library) that >>>> I've >>>> spent ages trying to understand, then ages trying abstract into a >>>> simple >>>> test case but all simple cases seem to work as expected but just not >>>> in my >>>> larger code base. I was hoping that if I put the symptoms up someone >>>> may >>>> spot what is going on and put me out of my misery (or at least point >>>> me to >>>> where I should continue my digging). >>>> >>>> Please excuse any typos in the (incomplete) example code below. I >>>> have coded >>>> up a full version of this simple scenario and I can't replicate the >>>> error >>>> I'm getting in my real code base anyway - so this is meant purely for >>>> illustration. >>>> >>>> # Types library >>>> package My::Types; >>>> use Moose; >>>> use MooseX::Types -declare [qw( Version DomainID )]; >>>> use My::Version; >>>> >>>> subtype 'Version', >>>> as 'Object', >>>> where { $_->isa( 'My::Version' ) }; >>>> >>>> coerce 'Version', >>>> from 'Str', >>>> via { My::Version::init_from_string( $_ ) }; >>>> >>>> subtype 'ObjectID', >>>> as 'Str'; >>>> >>>> # Role to associate versions with objects >>>> package My::Role::Verisoned; >>>> use Moose::Role; >>>> use My::Types qw(Version); >>>> has 'version' => (is => 'rw', isa => 'Version', coerce => 1); >>>> >>>> # Versioned Object1 >>>> package My::Object1; >>>> use Moose; >>>> with 'My::Role::Versioned'; >>>> >>>> # Versioned Object2 >>>> package My::Object2; >>>> use Moose; >>>> use My::Types qw( DomainID ); >>>> use My::Object1; >>>> with 'My::Role::Versioned'; >>>> >>>> has 'domain_id' => ( is => 'rw', isa => 'DomainID' ); >>>> >>>> sub get_object1 { >>>> my $self = shift; >>>> return My::Object1->new( version => $self->version ); >>>> } >>>> >>>> In this simple case, the subtype/coerce works as expected in both >>>> objects >>>> (the type constaint of the 'version' attribute is the identical) >>>> >>>> $o2 = My::Object2->new( version => '3.2' ); >>>> $o1 = $o2->get_object1; >>>> >>>> warn "Object1: " . >>>> My::Object1->meta->get_attribute_map->{version}->{type_constraint}- >>>> >>>>> dump; >>>>> >>>> warn "Object2: " . >>>> My::Object2->meta->get_attribute_map->{version}->{type_constraint}- >>>> >>>>> dump; >>>>> >>>> >>>> Object1: $VAR1 = bless( { >>>> 'compiled_type_constraint' => 'CODE(0xa41e310)', >>>> 'parent' => 'My::Version', >>>> 'coercion' => >>>> 'Moose::Meta::TypeCoercion=HASH(0xa991c70)', >>>> 'name' => 'Version', >>>> 'constraint' => 'CODE(0x9eb4600)', >>>> 'package_defined_in' => 'My::Types' >>>> }, 'Moose::Meta::TypeConstraint' ); >>>> >>>> Object2: $VAR1 = bless( { >>>> 'compiled_type_constraint' => 'CODE(0xa41e310)', >>>> 'parent' => 'My::Version', >>>> 'coercion' => >>>> 'Moose::Meta::TypeCoercion=HASH(0xa991c70)', >>>> 'name' => 'Version', >>>> 'constraint' => 'CODE(0x9eb4600)', >>>> 'package_defined_in' => 'My::Types' >>>> }, 'Moose::Meta::TypeConstraint' ); >>>> >>>> However, in what seems to be a similar scenario in my live code, I >>>> get the >>>> error that the 'version' attribute doesn't pass the type constraint >>>> when >>>> creating an instance of Object1 inside Object2::get_object1 (it no >>>> longer >>>> seems to be aware of the subtype/coercion in My::Types): >>>> >>>> Attribute (version) does not pass the type constraint because: >>>> Validation >>>> failed for 'Version' failed with value >>>> My::Version::V3_2_0=HASH(0x1aea0930) >>>> at .../Moose/Meta/Attribute.pm line 415 >>>> >>>> When I look at the type constraint of the 'version' attribute in >>>> these two >>>> classes they now appear different: >>>> >>>> warn "Object1: " . >>>> My::Object1->meta->get_attribute_map->{version}->{type_constraint}- >>>> >>>>> dump; >>>>> >>>> warn "Object2: " . >>>> My::Object2->meta->get_attribute_map->{version}->{type_constraint}- >>>> >>>>> dump; >>>>> >>>> >>>> Object1: $VAR1 = bless( { >>>> 'compiled_type_constraint' => 'CODE(0x1ae87c00)', >>>> 'parent' => 'Object', >>>> 'hand_optimized_type_constraint' => >>>> $VAR1->{'compiled_type_constraint'}, >>>> 'name' => 'Version', >>>> 'constraint' => 'CODE(0x1a3c4b80)', >>>> 'class' => 'Version' >>>> }, 'Moose::Meta::TypeConstraint::Class' ); >>>> >>>> Object2: $VAR1 = bless( { >>>> 'compiled_type_constraint' => 'CODE(0x1a929c10)', >>>> 'parent' => 'My::Version', >>>> 'coercion' => >>>> 'Moose::Meta::TypeCoercion=HASH(0x1ae9da00)', >>>> 'name' => 'Version', >>>> 'constraint' => 'CODE(0x1a3c4b80)', >>>> 'package_defined_in' => 'My::Types' >>>> }, 'Moose::Meta::TypeConstraint' ); >>>> >>>> >>>> I hope that difference shows what I might be doing wrong or where to >>>> start >>>> looking (parent => 'Object'? hand_optimized_type_constraint?). This >>>> change >>>> appears to be related to which modules have been "use"d -- in my >>>> live code I >>>> can remove this error by *taking out* the equivalent of "use >>>> My::Types" on >>>> Object2. >>>> >>>> Very much appreciate any help/pointers. >>>> >>>> Cheers, >>>> >>>> Ian >>>> >>>> >>>> -- >>>> Ian Sillitoe >>>> CATH Team -- http://cathdb.info >>>> >>> >>> >>> >> >> -- >> Ian Sillitoe >> CATH Team -- http://cathdb.info >> > > -- Ian Sillitoe CATH Team -- http://cathdb.info