perlvalidationmoose

Moose attribute initialization


What is the typical approach for custom initialization of certain attributes when using Moose?

For instance, suppose I take two dates in string format as input to my class:

has startdate  => (is => 'ro', isa => 'Str', required => 1);
has enddate    => (is => 'ro', isa => 'Str');

These dates come in as strings, but I need them formatted in a specific date format (ISO8601), without Moose I would just initialize them in new() but I am unsure about with Moose.

It seems that the viable options from reading the docs are in BUILDARGS, BUILD, or using coercion. Which of these would be most appropriate given that I have a function _format_as_iso8601() that can take a date and return it formatted correctly?


Solution

  • BUILD is called after the constructor, which makes it handy to verify state but not necessarily useful to format incoming arguments.

    BUILDARGS would let you modify incoming arguments before the constructor is called, which makes it a better fit for this case. Your attribute is read-only, so this could work.

    But... if you're hungry for static typing, why would you stop after promising "this is a string"? If you create a subtype for ISO8601 strings, you can promise "this is a string and it has X format". Even better, you're doing that in a way that's immediately and trivially portable to other attributes.

    I rather doubt the regex below will work for you, but I hope it will get the point across:

    #define the type
    subtype 'iso8601',
        as 'Str',
        where { /\d{4}-\d{2}-\d{2}/ },
        message { "Not a valid ISO8601 string ($_)" };
    
    #provide a coercion
    coerce 'iso8601',
        from 'Str',
        via { _format_as_iso8601 $_ };
    
    #tell moose to coerce the value
    has startdate  => (is => 'ro', isa => 'iso8601', required => 1, coerce => 1);