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

Reply via email to