Answer using Moo
# oo.t
use strict;
use warnings;
use lib '.';
use Test::More;
use_ok 'Service', 'Service.pl loads';
my $xml = Service->new_by_name('xml', 'xmldata');
$xml->populate;
is $xml->template, '<data>xmldata</data>', 'XML OK';
my $json = Service->new_by_name('json', 'jsondata');
$json->populate;
is $json->template, '{"data": jsondata}', 'JSON OK';
done_testing;
# Service.pm
package Service;
use Moo;
has 'data' => (is => 'ro', required => 1);
has 'template' => (is => 'rw', builder => '_build_template');
sub populate {
my $self = shift;
(my $tmpl = $self->template) =~ s/\[data\]/$self->data/emsi;
$self->template($tmpl);
}
sub new_by_name {
my (undef, $name, $data) = @_;
$name = uc $name;
my $class = __PACKAGE__ . "::$name";
eval "require $class";
$class->new(data => $data);
}
1;
# Service/XML.pm
package Service::XML;
use Moo;
extends 'Service';
sub _build_template { '<data>[data]</data>' }
1;
# Service/JSON.pm
package Service::JSON;
use Moo;
extends 'Service';
sub _build_template { '{"data": [data]}' }
1;
Answer using MooX::Pression
In this example, Service/XML.pm and Service/JSON.pm are no longer needed; all classes are defined in Service.pm.
use MooX::Pression prefix => 'MyApp';
class ::Service {
has data (is => ro, type => Str, required => true);
has template (is => rw, type => Str, builder => true);
method populate () {
(my $tmpl = $self->template) =~ s/\[data\]/$self->data/emsi;
$self->template($tmpl);
}
method new_by_name (Str $name, Str $data) {
$name = uc $name;
my $child = "$class\::$name";
$child->new(data => $data);
}
class +XML {
method _build_template () { '<data>[data]</data>' }
}
class +JSON {
method _build_template () { '{"data": [data]}' }
}
}
1;