Better post on MooseX::Declare method signatures

So here is a better description of my problem, with simplified code examples.

First, the Holiday date checker that works (yes I know there is DateTime event classes and all that and I should use that framework, but I just wanted something that worked right away).

use strict;
use warnings;
use MooseX::Declare;
class Holiday::California {
    use DateTime;
    use Carp;
    has '_ca_state_holidays' => (
        'is'      => 'ro',
        'isa'     => 'HashRef',
        'builder' => '_build__ca_state_holidays'
    );
    method _build__ca_state_holidays {
        return {
            '2007/01/01' => q{New Year's Day},
            '2007/01/15' => q{Martin Luther King Jr. Day},
            '2007/02/12' => q{Lincoln's Birthday},
            '2007/02/19' => q{Washington's Birthday},
            '2007/05/28' => q{Memorial Day},
            '2007/07/04' => q{Independence Day},
            '2007/09/03' => q{Labor Day},
            '2007/10/08' => q{Columbus Day},
            '2007/11/12' => q{Veteran's Day (observed)},
            '2007/11/22' => q{Thanksgiving Day},
            '2007/11/23' => q{Day after Thanksgiving},
            '2007/12/24' => q{Christmas Eve},
            '2007/12/25' => q{Christmas Day},
        };
    }
    method is_holiday_or_weekend  ( $dt ) {
        confess if (! $dt->isa('DateTime') );
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {
            return 1;
        }
        elsif (
            defined $self->_ca_state_holidays->{ $dt->ymd('/') } )
        {
            return 1;
        }
        return 0;
    }
}
1;

Note the manual type checking to make it work. The calling code is

#!/usr/bin/perl -w
use warnings;
use strict;
use Data::Dumper;
use version; our $VERSION = qv('0.0.1');
use English qw(-no_match_vars);
use Carp;
use DateTime::Format::Pg;
use DateTime;

use FindBin;
use lib "$FindBin::Bin/lib";
use Holiday::California;

# some test timestamps from psql
my %test_tss = (
    '2007-01-01 00:40:00' => 1,
    '2007-01-10 00:50:00' => 0,
    '2007-01-15 00:50:00' => 1,
    '2007-05-28 00:50:00' => 1,
    '2007-07-03 00:50:00' => 0,
    '2007-07-04 00:50:00' => 1,
    '2007-11-11 00:50:00' => 1,    # I think this is a weekend
    '2007-11-12 00:50:00' => 1,    # veterans day is usually a monday
  );
  my $checker = Holiday::California->new();

# this should croak
my $err = eval { $checker->is_holiday_or_weekend( ( keys %test_tss )[0] ); };
if ( $err || $EVAL_ERROR ) {
    carp 'failed to process a non-date';
}
foreach my $ts ( keys %test_tss ) {
    my $dt         = DateTime::Format::Pg->parse_datetime($ts);
    my $is_or_isnt = $checker->is_holiday_or_weekend($dt);
    if ( $is_or_isnt != $test_tss{$ts} ) {
        carp "hey, $ts isn't what I thought it was";
    }
    carp "$ts ", $is_or_isnt ? 'is' : 'is not', ' a holiday or weekend';
}


That works fine, output is

failed to process a non-date at caller.pl line 34
2007-01-15 00:50:00 is a holiday or weekend at caller.pl line 44
2007-11-11 00:50:00 is a holiday or weekend at caller.pl line 44
2007-11-12 00:50:00 is a holiday or weekend at caller.pl line 44
2007-05-28 00:50:00 is a holiday or weekend at caller.pl line 44
2007-01-10 00:50:00 is not a holiday or weekend at caller.pl line 44
2007-01-01 00:40:00 is a holiday or weekend at caller.pl line 44
2007-07-04 00:50:00 is a holiday or weekend at caller.pl line 44
2007-07-03 00:50:00 is not a holiday or weekend at caller.pl line 44

So first, in Holiday::California, take out the manual type check and use DateTime in the method signature.

    method is_holiday_or_weekend  ( DateTime $dt ) {
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {


As everybody who has used this before is expecting, this breaks with the following compile time error:


'DateTime' could not be parsed to a type constraint - maybe you need to pre-declare the type with class_type at /usr/lib64/perl5/site_perl/5.10.1/MooseX/Declare/Syntax/MethodDeclaration.pm line 40
Compilation failed in require at caller.pl line 15.
BEGIN failed--compilation aborted at caller.pl line 15.

So I looked up class_type on CPAN search and found http://search.cpan.org/~rkitover/MooseX-Types-0.21/lib/MooseX/Types.pm and http://search.cpan.org/~drolsky/Moose-0.94/lib/Moose/Cookbook/Basics/Recipe5.pod. So I figure the syntax should be:

    class_type 'DateTime';
    method is_holiday_or_weekend  ( DateTime $dt ) {
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {

But that fails with a slightly more Mom-like error message:

String found where operator expected at /home/james/perlwork/moosex_testing/lib/Holiday/California.pm line 34, near "class_type 'DateTime'"
	(Do you need to predeclare class_type?)


(Which translates as “How many times to I have to tell you that syntactic sugar will rot your brain!”)

So okay, more reading the fine manuals, and I find http://search.cpan.org/~drolsky/Moose-0.94/lib/Moose/Manual/Types.pod#TYPE_CREATION_HELPERS where it says

The Moose::Util::TypeConstraints module exports a number of helper functions for creating specific kinds of types. These include class_type, role_type, and maybe_type. See the docs for details.

Now my code looks like

    use Moose::Util::TypeConstraints;
    class_type 'DateTime';
    method is_holiday_or_weekend  ( DateTime $dt ) {
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {


And it appears I’m back to my first error:


'DateTime' could not be parsed to a type constraint - maybe you need to pre-declare the type with class_type at /usr/lib64/perl5/site_perl/5.10.1/MooseX/Declare/Syntax/MethodDeclaration.pm line 40
Compilation failed in require at caller.pl line 15.
BEGIN failed--compilation aborted at caller.pl line 15.

The pod link above recommended a better way:

use MooseX::Declare;
class Holiday::California {
    use DateTime;
    use Carp;
    use Moose::Util::TypeConstraints;

    # class_type 'DateTime';
    # or better
    class_type MyDateTime, { class => 'DateTime' };

...

    method is_holiday_or_weekend  ( MyDateTime $dt ) {
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {


Same error, just this time with MyDateTime, not DateTime.

The comments on my original post were similar but somewhat difficult for me to apply as a newcomer to Moose and MooseX::Declare. My understanding from reading the MooseX::Declare docs is that you have to plop all your use ...; lines inside of your class. But one comment suggested putting it outside of the class structure, right after MooseX::Declare, as follows:

use strict;
use warnings;
use MooseX::Declare;

use Moose::Util::TypeConstraints;
class_type 'DateTime';

class Holiday::California {


Same error as before.

Another comment said to use this incantation:

use Moose::Util::TypeConstraints;
class_type 'DateTime';
no Moose::Util::TypeConstraints;


Again, whether at the top of the code outside of the class line, or just inside the class line, or way down at the method made no difference, I still got the same error that DateTime could not be parsed as a type constraint.

Two comments recommended using use MooseX::Types::DateTime qw(DateTime); That did the trick. So now I have

use MooseX::Declare;

class Holiday::California {

    use DateTime;
    use Carp;
    use MooseX::Types::DateTime qw(DateTime);

...

    method is_holiday_or_weekend  ( DateTime $dt ) {
        if ( $dt->day_abbr() =~ /sun|sat/isxm ) {


And at last I get the output I expect.

failed to process a non-date at caller.pl line 34
2007-01-15 00:50:00 is a holiday or weekend at caller.pl line 44
2007-11-11 00:50:00 is a holiday or weekend at caller.pl line 44
...

I still don’t understand why or how to use class_type DateTime properly so that MooseX::Declare actually pays attention to it.

About these ads

3 thoughts on “Better post on MooseX::Declare method signatures

  1. The lookup of type constraints happens at compile time. You’re declaring your class_type at runtime.

    Using the recommended practice of putting your type definitions in a type library will fix that, except you don’t even need to do that for a DateTime type as there’s already a MooseX::Types::DateTime on cpan, as I’ve previously pointed out.

    If, for whatever reson, you don’t like any of those two, there’s BEGIN blocks to allow you to declare your type before MooseX::Method::Signatures will look it up.

  2. You’re declaring your class_type at runtime.

    Ah, that is what I was missing!

    And that explains the recommendation to put type definitions in a type library. I was under the impression that the class_type call was an equivalent but more tedious version of declaring types in a type library.

    The MooseX::Types::DateTime works for this case, but of course it won’t work for my own types. Thanks for the insight into how to get this to work in the general case.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s