Thanks Stevan. As it happens I'm already using the My::Framework approach -
the bit that I was getting stuck on (which now seems fairly obvious) was
that I was "use"ng packages in the My::Types library (required for some of
the coercions).

e.g. to support the "My::Versioned" role being initialised from a string I
had something like:

*  package My::Object;
*  use Moose;
  with 'My::Versioned';

*  package My::Versioned;
*  use My::Types qw( Version );
  use Moose::Role;
  has 'version' => (is => 'rw', isa => 'Version');

*  package My::Types
*  use MooseX::Types -declare ['Version'];
> use My::Version;

  subtype 'Version'
      => as 'Object'
      => where { $_->isa( 'My::Version' ) }
      => message { "object '$_' does not appear to be a valid Version" };

  coerce 'Version'
      => from 'Str'
          => via { My::Version::init_from_string( $_ ) };

I localised the "use" statements in the My::Types package to take them out
of the compile-time loop:

- use My::Version;

  coerce 'Version'
      => from 'Str'
-         => via { My::Version::init_from_string( $_ ) };
+         => via { use My::Version; My::Version::init_from_string( $_ ) };

Is this a really horrible hack?



2009/1/30 Stevan Little <stevan.lit...@iinteractive.com>

> Ian,
>
> If your looking to decrease fragility in a framework, but don't want to
> have to enforce some kind of "you must load this 3 modules in this order"
> rule, then you might want to look into the docs on extending and embedding
> Moose. Then you do can just do:
>
>  package My::App;
>  use My::Framework; # load Moose and all your deps in strict order
>
> - Stevan
>
>
>
>
> On Jan 30, 2009, at 3:37 AM, Ian Sillitoe wrote:
>
>  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
>>
>>
>


-- 
Ian Sillitoe
CATH Team -- http://cathdb.info

Reply via email to