How to create your own Moose sub types in Perl

| 2 Comments

I learnt today (thanks to irc.perl.org #moose) that you can do this in Moose:

has cookies => (
    is  => 'rw',
    isa => 'Bool|Int',  # Bool or Int type
);

You can also state that the type should be an instance of a specific class:

has web_browser => (
    is  => 'rw',
    isa => 'WWW::Mechanize',
);

However, you cannot use the above OR construct with class names:

has web_browser => (
    is  => 'rw',
    isa => 'WWW::Mechanize|My::Test::Mechanize', # FAIL
);

At least, not directly out of the box ….

class_type

The quickest way to crack this nut seems to be with Moose::Util::TypeConstraints class_type.

use Moose::Util::TypeConstraints;
class_type q[WWW::Mechanize];
class_type q[My::Test::Mechanize];
no Moose::Util::TypeConstraints;

and now this will work:

has web_browser => (
    is  => 'rw',
    isa => 'WWW::Mechanize|My::Test::Mechanize',
);

OR … Sub types

Moose::Manual::Types tells us we can create our own sub types, giving an example of a PositiveInt type.

That in turn points to Type Constraint Naming in Moose::Util::TypeConstraints which says:

Since the types created by this module are global, it is suggested that you namespace your types just as you would namespace your modules. So instead of creating a Color type for your My::Graphics module, you would call the type My::Graphics::Types::Color instead

I don’t know if this is the right way to do things, but I now have the following setup:

Defining My::Types

In lib/My/Types.pm I have:

package My::Types;

use Moose::Util::TypeConstraints;

subtype 'My::Types::PositiveInt'
    => as 'Int'
    => where { $_ > 0 }
    => message { "'$_' is not a int > 0" };

subtype 'My::Types::Mech'
    => as 'Object'
    => where {
        $_->isa('WWW::Mechanize')
        || 
        $_->isa('My::Test::Mechanize')
    }
    => message {
        "'$_' is neither WWW::Mechanize or My::Test::Mechanize"
    };

no Moose::Util::TypeConstraints;

1;

Using My::Types

And then within my other code / modules / classes, I can have:

use My::Types;

has web_browser => (
    is  => 'rw',
    isa => 'My::Types::Mech',
);

Which will ensure $thing->web_browser is either a WWW::Mechanize object or my own My::Test::Mechanize object (used for testing purposes).

OR …. MooseX::Types

An (third) alternative approach is MooseX::Types, which allows for having custom types and choosing which ones you import in any one place.

To define your types, in lib/My/Types.pm

package My::Types;

use MooseX::Types
    -declare => [qw<PositiveInt Mech>];

use MooseX::Types::Moose qw<Int Object>;

subtype PositiveInt,
    as Int,
    where { $_ > 0 },
    message { "'$_' is not a int > 0" };

subtype Mech,
    as Object,
    where {
        $_->isa('WWW::Mechanize')
        ||
        $_->isa('My::Test::Mechanize')
    },
    message {
        "'$_' is neither WWW::Mechanize or My::Test::Mechanize"
    };

1;

and then to use it in your code:

use My::Types qw<Mech>;
has web_browser => (
    is  => 'rw',
    isa => Mech,
);

Note that you do not want the quotes around Mech here else it will fail.

Which one to use?

I haven’t a clue. I think I currently favour defining my own subtype through Moose::Util::TypeConstraints but that is possibly just because it was my first working solution.

Comments welcome :)

ps. thanks again to the friendly people on #moose for putting up with my dumb questions about all this.

2 Comments

MooseX::Types is the way to go.

Oh the wonders of Moose. There are many more things to discover in the manual http://search.cpan.org/perldoc?Moose::Manual though not so easy to read, until now...

We've created a printable version of the Moose manual in elegant typography, easy on the eye. http://www.lulu.com/content/paperback-book/moose/7406976

Leave a comment

About this Entry

This page contains a single entry by mint published on July 19, 2009 3:08 PM.

Stunning image manipulation in HTML/CSS/JavaScript was the previous entry in this blog.

Apache mod_rewrite and proxypass is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.