In Mason 1, extending Mason’s behavior means either using the limited plugin API (allowing you to take action before and after a request or component call), or else writing subclasses, which is problematic when you try to use more than one together.
In Mason 2, plugins are based on Moose roles, allowing you to modify behavior in each of Mason’s main subclasses — Interp, Request, Compiler, Compilation, and Component — in a composable way.
Here’s my first Mason 2 plugin, to perltidy object files after components are compiled – useful in development. First, a nominal base class:
package Mason::Plugin::TidyObjectFiles;
use Moose;
extends 'Mason::Plugin';
1;
(This has to exist for Mason to recognize the plugin, but there isn’t much to do there right now.)
Then, a role for each of Mason’s subclasses that we want to modify. In this case we only need to modify Mason::Compiler:
package Mason::Plugin::TidyObjectFiles::Compiler;
use Moose::Role;
use Perl::Tidy;
use strict;
use warnings;
has 'tidy_options' => ( is => 'ro' );
around 'write_object_file' => sub {
my ( $orig, $self, $object_file, $object_contents ) = @_;
my $argv = $self->tidy_options || '';
my $source = $object_contents;
Perl::Tidy::perltidy(
'perltidyrc' => '/dev/null',
source => \$source,
destination => \$object_contents,
argv => $argv
);
$self->$orig( $object_file, $object_contents );
};
1;
To use this, I simply list ‘TidyObjectFiles’ in my plugins:
my $mason = Mason->new(..., plugins=>[ 'TidyObjectFiles' ]);
or, if I want to specify perltidy options:
my $mason = Mason->new(..., plugins=>[ 'TidyObjectFiles' ], tidy_options => '-noll -l=100');
The Mason::Plugin:: prefix is automatically added to plugin names. You can also specify a full path with a leading ‘+’, e.g.
plugins => ['+MyApp::Plugin::MyPlugin']
Now my object files go from looking like this:
no warnings 'redefine';
sub _comp_info { return {comp_dir_path => '/',comp_is_external => 1,comp_path => '/hi.m'} }
sub main {
my $self = shift;
my $m = $self->m;
my $_buffer = $m->current_buffer;
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
$$_buffer .= 'Hi there! The time is ';
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
{ $$_buffer .= scalar(localtime) if defined( scalar(localtime) ) }
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
$$_buffer .= '.
';
;return;
}
to this:
no warnings 'redefine';
sub _comp_info {
return {
comp_dir_path => '/',
comp_is_external => 1,
comp_path => '/hi.m'
};
}
sub main {
my $self = shift;
my $m = $self->m;
my $_buffer = $m->current_buffer;
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
$$_buffer .= 'Hi there! The time is ';
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
{ $$_buffer .= scalar(localtime) if defined( scalar(localtime) ) }
#line 1 "/Users/swartz/git/mason.git/tmp/comps/hi.m"
$$_buffer .= '.
';
return;
}