j1n3l0 has asked for the wisdom of the Perl Monks concerning the following question:

Hello everyone,

Can anyone explain why this coersion does not appear to work:

use 5.026; use Test::Most; { package Thing; use Moose; use Types::Standard qw( Bool Str ); has delete => ( coerce => 1, is => 'ro', isa => Str->plus_coercions( Bool, q{ $_ ? 'yes' : 'no' } ), ); } { cmp_methods( new_ok( Thing => [ delete => 1 ] ), [ delete => 'yes' ], 'attributes are coerced as expected', ); } done_testing;

I get the following output:

# Failed test 'attributes are coerced as expected' # at t/coercion.t line 16. # Compared $data->delete # got : '1' # expect : 'yes' # Looks like you failed 1 test of 2.

I was following the instructions documented here. Any and all assistance is appreciated.


Smoothie, smoothie, hundre prosent naturlig!

Replies are listed 'Best First'.
Re: Cannot coerce from Bool to Str
by choroba (Cardinal) on Sep 06, 2018 at 14:48 UTC
    Coercion is triggered when the basic type constraint isn't satisfied. But 1 satisfies Str, so there's no need to call the coercion from Bool.

    Update: The common way to have a boolean yes/no attribute in Moose would be something like

    package Thing; use Moose; use Moose::Util::TypeConstraints qw{ enum }; has delete => (is => 'ro', isa => enum([qw[ yes no ]]), init_arg => undef, lazy => 1, builder => '_build_delete'); has _delete => (is => 'ro', isa => 'Bool', init_arg => 'delete'); sub _build_delete { my ($self) = @_; $self->_delete ? 'yes' : 'no' }

    I'm a bit uncomfortable with _delete having a constructor argument delete that has the same name as an attribute that has no init argument, but it seems to work.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Cannot coerce from Bool to Str
by tobyink (Canon) on Sep 08, 2018 at 20:39 UTC

    As per choroba's answer, coercions are only triggered if the value fails the type constraint, and "1" is a valid string.

    You could use Enum["yes","no"] as your type constraint instead of Str.

    use 5.026; use Test::Most; { package Thing; use Moose; use Types::Standard qw( Bool Enum ); has delete => ( coerce => 1, is => 'ro', isa => Enum->of(qw/yes no/)->plus_coercions( Bool, q{ $_ ? +'yes' : 'no' } ), ); } { cmp_methods( new_ok( Thing => [ delete => 1 ] ), [ delete => 'yes' ], 'attributes are coerced as expected', ); } done_testing;