thanks, Jesse, that works for me. I did know that.. pretty sure I won't
forget it again! 8)

J

On Fri, Jan 27, 2012 at 1:01 PM, Jesse Luehrs <d...@tozt.net> wrote:

> On Fri, Jan 27, 2012 at 12:51:40PM +1000, Jason Galea wrote:
> > Hi all,
> >
> > I need to stop looking at this.. I have a headache now..
> >
> > I'm messing around with building a Plack app and had some really odd
> > behaviour which I tracked back and boiled down to the test cases below.
> > (unfortunately the result was my app connecting to a separate dev
> database
> > after creating an empty test database and telling me a user existed
> where I
> > knew it didn't.. took me a while to get past that..)
> >
> > anyhoo.. can someone please explain the oddities I'm seeing in these
> tests?
> >
> > My::Builder works as expected until I call make_immutable on it, then it
> > returns undef.
> >
> > My::Builder2 is the same as My::Builder except
> >  - the 'app' attribute is lazy.
> >  - it's been made immutable
> > it works as expected.
> >
> > My:Builder3 is the same as My::Builder except
> >  - it's been made immutable
> >  - I renamed the 'app' attribute as 'attr2'..
> > it works as expected.
> >
> > My::Builder4 is the same as My::Builder except
> >  - attr1 uses lazy_build
> >  - it's been made immutable
> > it fails, but it returns attr1's default value
> >
> > please tell me I'm doing something stupid so I can stop trying to work it
> > all out.. 8)
> >
> > cheers,
> >
> > J
> >
> > use strict;
> > use warnings;
> > use Test::More;
> >
> > {
> >     package My::Builder;
> >     use Moose;
> >
> >     has attr1 => ( is => 'ro', default => 'default value' );
> >
> >     has app => ( is => 'ro', builder => '_build_app' );
> >
> >     sub _build_app{ shift->attr1; }
> >
> >     sub web_app{
> >         my $self = shift;
> >         return sub { $self->app };
> >     }
> >
> >
> >
> >     package My::Builder2;
> >     use Moose;
> >
> >     has attr1 => ( is => 'ro', default => 'default value' );
> >
> >     has app => ( is => 'ro', builder => '_build_app', lazy => 1 );
> >
> >     sub _build_app{ shift->attr1; }
> >
> >     sub web_app{
> >         my $self = shift;
> >         return sub { $self->app };
> >     }
> >
> >     __PACKAGE__->meta->make_immutable;
> >
> >
> >     package My::Builder3;
> >     use Moose;
> >
> >     has attr1 => ( is => 'ro', default => 'default value' );
> >
> >     has attr2 => ( is => 'ro', builder => '_build_attr2' );
> >
> >     sub _build_attr2{ shift->attr1; }
> >
> >     sub web_app{
> >         my $self = shift;
> >         return sub { $self->attr2 };
> >     }
> >
> >     __PACKAGE__->meta->make_immutable;
> >
> >
> >     package My::Builder4;
> >     use Moose;
> >
> >     has attr1 => ( is => 'ro', lazy_build => 1 );
> >     sub _build_attr1{ 'default value' }
> >
> >     has app => ( is => 'ro', builder => '_build_app' );
> >
> >     sub _build_app{ shift->attr1; }
> >
> >     sub web_app{
> >         my $self = shift;
> >         return sub { $self->app };
> >     }
> >
> >     __PACKAGE__->meta->make_immutable;
> >
> > }
> >
> > my $app = My::Builder->new( attr1 => 'my value' )->web_app;
> > is $app->(), 'my value', 'no make immutable or lazy ok';
> >
> > $app = My::Builder2->new( attr1 => 'my value' )->web_app;
> > is $app->(), 'my value', 'make_immutable with lazy ok';
> >
> > $app = My::Builder3->new( attr1 => 'my value' )->web_app;
> > is $app->(), 'my value', 'make_immutable without lazy breaks.. not!';
> >
> > My::Builder->meta->make_immutable;
> > $app = My::Builder->new( attr1 => 'my value' )->web_app;
> > is $app->(), 'my value', 'make_immutable without lazy breaks (returns
> > undef)';
> >
> > $app = My::Builder4->new( attr1 => 'my value' )->web_app;
> > is $app->(), 'my value', 'make_immutable without lazy breaks (returns
> > default value)';
> >
> > done_testing();
> >
> >
> > $ prove -lv t/999-make-immutable-breaksit.t
> > t/999-make-immutable-breaksit.t ..
> > ok 1 - no make immutable or lazy ok
> > ok 2 - make_immutable with lazy ok
> > ok 3 - make_immutable without lazy breaks.. not!
> > not ok 4 - make_immutable without lazy breaks (returns undef)
> >
> > #   Failed test 'make_immutable without lazy breaks (returns undef)'
> > #   at t/999-make-immutable-breaksit.t line 86.
> > #          got: undef
> > #     expected: 'my value'
> > not ok 5 - make_immutable without lazy breaks (returns default value)
> >
> > #   Failed test 'make_immutable without lazy breaks (returns default
> value)'
> > #   at t/999-make-immutable-breaksit.t line 89.
> > #          got: 'default value'
> > #     expected: 'my value'
> > 1..5
> > # Looks like you failed 2 tests of 5.
> > Dubious, test returned 2 (wstat 512, 0x200)
> > Failed 2/5 subtests
> >
> > Test Summary Report
> > -------------------
> > t/999-make-immutable-breaksit.t (Wstat: 512 Tests: 5 Failed: 2)
> >   Failed tests:  4-5
> >   Non-zero exit status: 2
> > Files=1, Tests=5,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.18 cusr
>  0.01
> > csys =  0.21 CPU)
> > Result: FAIL
>
> This actually doesn't have anything to do with make_immutable. The issue
> is that attributes without lazy are initialized in an arbitrary order,
> so when the 'app' attribute is initialized (at 'new' time, since it
> isn't lazy), it might be initialized before attr1, and it might be
> initialized after. The only way to guarantee the order is to make app
> lazy. A good rule of thumb is that any attribute whose default or
> builder depends on the value of another attribute should be lazy.
>
> -doy
>



-- 

Jason Galea
Web Developer

Ph 07 40556926
Mob 04 12345 534
www.eightdegrees.com.au

Reply via email to