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

Reply via email to