How can I export a subroutine from a Moose package?

112 Views Asked by At

How can I export a normal, non-OO subroutine from a Moose package? In a regular package, I'd do it with Exporter, @ISA and @EXPORT.

1

There are 1 best solutions below

7
tobyink On BEST ANSWER

Moose is for building classes and roles. While you technically can also export functions, it's not necessarily the best idea.

Here's an example Moose class which also exports a function.

MyApp/Widget.pm

use v5.26;
use warnings;

package MyApp::Widget;

use Exporter qw( import );
our @EXPORT_OK = qw( is_widget );

use Moose;
use namespace::autoclean -except => 'import';

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

sub is_widget {
    my $object = shift;
    blessed( $object ) and $object->isa( __PACKAGE__ );
}

__PACKAGE__->meta->make_immutable;

Here's how you might use it:

use v5.26;
use warnings;
use MyApp::Widget qw( is_widget );

my $w = 'MyApp::Widget'->new( name => 'Foo' );
say is_widget( $w );
say $w->is_widget;

Note that even though is_widget was intended an exportable function, it can also be called as a method! In this case, that's a feature rather than a bug, but often that will be an inconvenience.

A better idea might be to create two separate packages: one for your class and one for your exportable functions.

MyApp/Widget.pm

use v5.26;
use warnings;

package MyApp::Widget;

use Moose;
use namespace::autoclean;

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

__PACKAGE__->meta->make_immutable;

MyApp/Util.pm

use v5.26;
use warnings;

package MyApp::Util;

use Exporter qw( import );
our @EXPORT_OK = qw( is_widget );

use Scalar::Util qw( blessed );

sub is_widget {
    my $object = shift;
    blessed( $object ) and $object->isa( 'MyApp::Widget' );
}

1;

And you'd call use your packages like this:

use v5.26;
use warnings;
use MyApp::Widget;
use MyApp::Util qw( is_widget );

my $w = 'MyApp::Widget'->new( name => 'Foo' );
say is_widget( $w );

Because the Moose class and the Exporter are now cleanly separated, you can no longer call $w->is_widget — it's entirely a function and no longer a method.