Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,87 @@
# PODNAME: MooseX::Role::Parameterized::Extending
# ABSTRACT: extending MooseX::Role::Parameterized roles
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Extending - extending MooseX::Role::Parameterized roles
=head1 VERSION
version 1.11
=head1 DESCRIPTION
There are heaps of useful modules in the C<MooseX> namespace that you can use
to make your roles more powerful. However, they do not always work out of the
box with L<MooseX::Role::Parameterized>, but it's fairly straight-forward to
achieve the functionality you desire.
L<MooseX::Role::Parameterized> was designed to be as extensible as the rest of
L<Moose>, and as such it is possible to apply custom traits to both the
parameterizable role or the ordinary roles they generate. In this example, we
will look at applying the fake trait C<MooseX::MagicRole> to a parameterizable
role.
First we need to define a new metaclass for our parameterizable role.
package MyApp::Meta::Role::Parameterizable;
use Moose;
extends 'MooseX::Role::Parameterized::Meta::Role::Parameterizable';
with 'MooseX::MagicRole';
This is a class (observe that it uses L<Moose>, not L<Moose::Role>) which
extends the class which governs parameterizable roles.
L<MooseX::Role::Parameterized::Meta::Role::Parameterizable> is the metaclass
that packages using L<MooseX::Role::Parameterized> receive by default.
Note that the class we are extending,
L<MooseX::Role::Parameterized::Meta::Role::ParameterizB<I<able>>|MooseX::Role::Parameterized::Meta::Role::Parameterizable>,
is entirely distinct from the similarly-named class which governs the
ordinary roles that parameterized roles generate. An instance of
L<MooseX::Role::Parameterized::Meta::Role::ParameterizB<I<ed>>|MooseX::Role::Parameterized>
represents a role with its parameters already bound.
Now we can take advantage of our new subclass by specifying that we want to use
C<MyApp::Meta::Role::Parameterizable> as our metaclass when importing
L<MooseX::Role::Parameterized>:
package MyApp::Role;
use MooseX::Role::Parameterized -metaclass => 'MyApp::Meta::Role::Parameterizable';
role {
...
}
And there you go! C<MyApp::Role> now has the C<MooseX::MagicRole> trait applied.
=head1 NAME
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,57 @@
package MooseX::Role::Parameterized::Meta::Role::Parameterized;
# ABSTRACT: metaclass for parameterized roles
our $VERSION = '1.11';
use Moose;
extends 'Moose::Meta::Role';
with 'MooseX::Role::Parameterized::Meta::Trait::Parameterized';
__PACKAGE__->meta->make_immutable;
no Moose;
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Meta::Role::Parameterized - metaclass for parameterized roles
=head1 VERSION
version 1.11
=head1 DESCRIPTION
This is the metaclass for parameterized roles; that is, parameterizable roles
with their parameters bound. See
L<MooseX::Role::Parameterized::Meta::Trait::Parameterized> which has all the guts.
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,262 @@
package MooseX::Role::Parameterized::Meta::Trait::Parameterizable;
# ABSTRACT: trait for parameterizable roles
our $VERSION = '1.11';
use Moose::Role;
use MooseX::Role::Parameterized::Meta::Role::Parameterized;
use MooseX::Role::Parameterized::Parameters;
use Module::Runtime 'use_module';
use namespace::autoclean;
has parameterized_role_metaclass => (
is => 'ro',
isa => 'ClassName',
default => 'MooseX::Role::Parameterized::Meta::Role::Parameterized',
);
has parameters_class => (
is => 'ro',
isa => 'ClassName',
default => 'MooseX::Role::Parameterized::Parameters',
);
has parameters_metaclass => (
is => 'rw',
isa => 'Moose::Meta::Class',
lazy => 1,
builder => '_build_parameters_metaclass',
handles => {
has_parameter => 'has_attribute',
add_parameter => 'add_attribute',
construct_parameters => 'new_object',
},
predicate => '_has_parameters_metaclass',
);
has role_generator => (
is => 'rw',
isa => 'CodeRef',
predicate => 'has_role_generator',
);
sub _build_parameters_metaclass {
my $self = shift;
return $self->parameters_class->meta->create_anon_class(
superclasses => [$self->parameters_class],
);
}
my $package_counter = 0;
sub generate_role {
my $self = shift;
my %args = @_;
my $parameters = blessed($args{parameters})
? $args{parameters}
: $self->construct_parameters(%{ $args{parameters} });
confess "A role generator is required to apply parameterized roles (did you forget the 'role { ... }' block in your parameterized role '".$self->name."'?)"
unless $self->has_role_generator;
my $parameterized_role_metaclass = $self->parameterized_role_metaclass;
use_module($parameterized_role_metaclass);
my $package = $args{package};
unless ($package) {
$package_counter++;
$package = $self->name . '::__ANON__::SERIAL::' . $package_counter;
}
my $role = $parameterized_role_metaclass->create(
$package,
genitor => $self,
parameters => $parameters,
);
local $MooseX::Role::Parameterized::CURRENT_METACLASS = $role;
# The generate_role method is being called directly by things like
# MooseX::ClassCompositor. We don't want to force such modules to pass
# this arg so we default to something sane.
my $orig_apply = $args{orig_apply} || Moose::Meta::Role->can('apply');
$self->$orig_apply($role);
$self->role_generator->($parameters,
operating_on => $role,
consumer => $args{consumer},
);
# don't just return $role here, because it might have been changed when
# metaroles are applied
return $MooseX::Role::Parameterized::CURRENT_METACLASS;
}
sub _role_for_combination {
my $self = shift;
my $parameters = shift;
return $self->generate_role(
parameters => $parameters,
);
}
around apply => sub {
my $orig = shift;
my $self = shift;
my $consumer = shift;
my %args = @_;
my $role = $self->generate_role(
consumer => $consumer,
parameters => \%args,
orig_apply => $orig,
);
$role->apply($consumer, %args);
};
around reinitialize => sub {
my $orig = shift;
my $class = shift;
my ($pkg) = @_;
my $meta = blessed($pkg) ? $pkg : find_meta($pkg);
my $meta_meta = $meta->meta;
my %p;
if ( $meta_meta->can('does_role') && $meta_meta->does_role(__PACKAGE__) ) {
%p = map { $_ => $meta->$_ }
qw( parameterized_role_metaclass parameters_class );
$p{parameters_metaclass} = $meta->parameters_metaclass
if $meta->_has_parameters_metaclass;
$p{role_generator} = $meta->role_generator
if $meta->has_role_generator;
}
my $new = $class->$orig(
@_,
%p,
);
return $new;
};
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Meta::Trait::Parameterizable - trait for parameterizable roles
=head1 VERSION
version 1.11
=head1 DESCRIPTION
This is the trait that is applied to the metaclass for parameterizable roles,
roles that have their parameters currently unbound. These are the roles that
you use L<Moose/with>, but instead of composing the parameterizable role, we
construct a new parameterized role
(L<MooseX::Role::Parameterized::Meta::Role::Parameterized>) and use that new
parameterized role instead.
=head1 ATTRIBUTES
=head2 parameterized_role_metaclass
The name of the class that will be used to construct the parameterized role.
=head2 parameters_class
The name of the class that will be used to construct the parameters object.
=head2 parameters_metaclass
A metaclass representing this role's parameters. It will be an anonymous
subclass of L</parameters_class>. Each call to
L<MooseX::Role::Parameters/parameter> adds an attribute to this metaclass.
When this role is consumed, the parameters object will be instantiated using
this metaclass.
=head2 role_generator
A code reference that is used to generate a role based on the parameters
provided by the consumer. The user usually specifies it using the
L<MooseX::Role::Parameterized/role> keyword.
=head1 METHODS
=head2 add_parameter $name, %options
Delegates to L<Moose::Meta::Class/add_attribute> on the
L</parameters_metaclass> object.
=head2 construct_parameters %arguments
Creates a new L<MooseX::Role::Parameterized::Parameters> object using metaclass
L</parameters_metaclass>.
The arguments are those specified by the consumer as parameter values.
=head2 generate_role %arguments
This method generates and returns a new instance of
L</parameterized_role_metaclass>. It can take any combination of
three named arguments:
=over 4
=item parameters
A hashref of parameters for the role, same as would be passed in at a "with"
statement.
=item package
A package name that, if present, we will use for the generated role; if not,
we generate an anonymous role.
=item consumer
=for stopwords metaobject
A consumer metaobject, if available.
=back
=head2 apply
Overrides L<Moose::Meta::Role/apply> to automatically generate the
parameterized role.
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,101 @@
package MooseX::Role::Parameterized::Meta::Trait::Parameterized;
# ABSTRACT: trait for parameterized roles
our $VERSION = '1.11';
use Moose::Role;
use MooseX::Role::Parameterized::Parameters;
use Moose::Util 'find_meta';
use namespace::autoclean;
has genitor => (
is => 'ro',
does => 'MooseX::Role::Parameterized::Meta::Trait::Parameterizable',
required => 1,
);
has parameters => (
is => 'rw',
isa => 'MooseX::Role::Parameterized::Parameters',
);
around reinitialize => sub {
my $orig = shift;
my $class = shift;
my ($pkg) = @_;
my $meta = blessed($pkg) ? $pkg : find_meta($pkg);
my $genitor = $meta->genitor;
my $parameters = $meta->parameters;
my $new = $class->$orig(
@_,
(defined($genitor) ? (genitor => $genitor) : ()),
(defined($parameters) ? (parameters => $parameters) : ()),
);
# in case the role metaclass was reinitialized
$MooseX::Role::Parameterized::CURRENT_METACLASS = $new;
return $new;
};
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Meta::Trait::Parameterized - trait for parameterized roles
=head1 VERSION
version 1.11
=head1 DESCRIPTION
This is the trait for parameterized roles; that is, parameterizable roles with
their parameters bound. All this actually provides is a place to store the
L<MooseX::Role::Parameterized::Parameters> object as well as the
L<MooseX::Role::Parameterized::Meta::Role::Parameterizable> object that
generated this role object.
=head1 ATTRIBUTES
=for stopwords genitor metaobject
=head2 genitor
Returns the L<MooseX::Role::Parameterized::Meta::Role::Parameterizable>
metaobject that generated this role.
=head2 parameters
Returns the L<MooseX::Role::Parameterized::Parameters> object that represents
the specific parameter values for this parameterized role.
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,60 @@
package MooseX::Role::Parameterized::Parameters;
# ABSTRACT: base class for parameters
our $VERSION = '1.11';
use Moose;
__PACKAGE__->meta->make_immutable;
no Moose;
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Parameters - base class for parameters
=head1 VERSION
version 1.11
=head1 DESCRIPTION
This is the base class for parameter objects. Currently empty, but I reserve
the right to add things here.
Each parameterizable role gets their own anonymous subclass of this;
L<MooseX::Role::Parameterized/parameter> actually operates on these anonymous
subclasses.
Each parameterized role gets their own instance of the anonymous subclass
(owned by the parameterizable role).
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut

View File

@@ -0,0 +1,248 @@
# PODNAME: MooseX::Role::Parameterized::Tutorial
# ABSTRACT: why and how
__END__
=pod
=encoding UTF-8
=head1 NAME
MooseX::Role::Parameterized::Tutorial - why and how
=head1 VERSION
version 1.11
=head1 MOTIVATION
Roles are composable units of behavior. They are useful for factoring out
functionality common to many classes from any part of your class hierarchy. See
L<Moose::Cookbook::Roles::Comparable_CodeReuse> for an introduction to L<Moose::Role>.
While combining roles affords you a great deal of flexibility, individual roles
have very little in the way of configurability. Core Moose provides C<-alias>
for renaming methods and C<-excludes> for ignoring methods. These options are
primarily for resolving role conflicts. Depending on how much of a purist you are,
these options are I<solely> for resolving role conflicts. See
L<Moose::Cookbook::Roles::Restartable_AdvancedComposition> for more about C<-alias> and C<-excludes>.
Because roles serve many different masters, they usually provide only the least
common denominator of functionality. To empower roles further, more
configurability than C<-alias> and C<-excludes> is required. Perhaps your role
needs to know which method to call when it is done processing. Or what default
value to use for its C<url> attribute.
Parameterized roles offer a solution to these (and other) kinds of problems.
=head1 USAGE
=head2 C<with>
The syntax of a class consuming a parameterized role has not changed
from the standard C<with>. You pass in parameters just like you
pass in C<-alias> and C<-excludes> to ordinary roles (though your
custom parameters do not get hyphens, since these are not core Moose
composition parameters):
with 'MyRole::InstrumentMethod' => {
method_name => 'dbh_do',
log_to => 'query.log',
};
You can still combine parameterized roles. You just need to specify parameters
immediately after the role they belong to:
with (
'My::Parameterized::Role' => {
needs_better_example => 1,
},
'My::Other::Role',
);
We, like Moose itself, use L<Data::OptList> to make sure that a list of role
names and associated parameters is handled correctly.
=head2 C<parameter>
Inside your parameterized role, you specify a set of parameters. This is
exactly like specifying the attributes of a class. Instead of L<Moose/has> you
use the keyword C<parameter>, but your parameters can use any options to
C<has>.
parameter 'delegation' => (
isa => 'HashRef|ArrayRef|RegexpRef',
predicate => 'has_delegation',
);
You do have to declare what parameters you accept, just like you have to
declare what attributes you accept for regular Moose objects.
One departure from C<has> is that we create a reader accessor for you by
default. In other words, we assume C<< is => 'ro' >>. We create this reader for
convenience because generally the parameterized role is the only consumer of
the parameters object, so data hiding is not as important than in the general
case of L<Moose/has>. If you do not want an accessor, you can use
C<< is => 'bare' >>.
=head2 C<role>
C<role> takes a block of code that will be used to generate your role with its
parameters bound. Here is where you declare components that depend on
parameters. You can declare attributes, methods, modifiers, etc. The first
argument to the C<role> is an object containing the parameters specified by
C<with>. You can access the parameters just like regular attributes on that
object.
Each time you compose this parameterized role, the C<role {}> block will be
executed. It will receive a new parameter object and produce an entirely new
role. That's the whole point, after all.
Due to limitations inherent in Perl, you must declare methods with
C<< method name => sub { ... } >> instead of the usual C<sub name { ... }>.
Your methods may, of course, close over the parameter object. This means that
your methods may use parameters however they wish!
=head1 USES
Ideally these will become fully-explained examples in something resembling
L<Moose::Cookbook>. But for now, only a brain dump.
=over 4
=item Configure a role's attributes
You can rename methods with core Moose, but now you can rename attributes. You
can now also choose type, default value, whether it's required, B<traits>, etc.
parameter traits => (
isa => 'ArrayRef',
default => sub { [] },
);
parameter type => (
isa => 'Str',
default => 'Any',
);
role {
my $p = shift;
has action => (
traits => $p->traits,
isa => $p->type,
...
);
};
=item Inform a role of your class' attributes and methods
Core roles can only require methods with specific names chosen by the role. Now
your roles can demand that the class specifies a method name you wish the role to
instrument, or which attributes to dump to a file.
parameter instrument_method => (
isa => 'Str',
required => 1,
);
role {
my $p = shift;
around $p->instrument_method => sub { ... };
};
=item Arbitrary execution choices
Your role may be able to provide configuration in how the role's methods
operate. For example, you can tell the role whether to save intermediate
states.
parameter save_intermediate => (
isa => 'Bool',
default => 0,
);
role {
my $p = shift;
method process => sub {
...
if ($p->save_intermediate) { ... }
...
};
};
=item Deciding a backend
Your role may be able to freeze and thaw your instances using L<YAML>, L<JSON>,
L<Storable>. Which backend to use can be a parameter.
parameter format => (
isa => (enum ['Storable', 'YAML', 'JSON']),
default => 'Storable',
);
role {
my $p = shift;
if ($p->format eq 'Storable') {
method freeze => \&Storable::freeze;
method thaw => \&Storable::thaw;
}
elsif ($p->format eq 'YAML') {
method freeze => \&YAML::Dump;
method thaw => \&YAML::Load;
}
...
};
=item Additional validation
Ordinary roles can require that its consumers have a particular list of method
names. Since parameterized roles have direct access to its consumer, you can inspect it and throw errors if the consumer does not meet your needs.
role {
my $p = shift;
my %args = @_;
my $consumer = $args{consumer};
$consumer->find_attribute_by_name('stack')
or confess "You must have a 'stack' attribute";
my $push = $consumer->find_method_by_name('push')
or confess "You must have a 'push' method";
my $params = $push->parsed_signature->positional_params->params;
@$params == 1
or confess "Your push method must take a single parameter";
$params->[0]->sigil eq '$'
or confess "Your push parameter must be a scalar";
...
};
=back
=head1 SUPPORT
Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Role-Parameterized>
(or L<bug-MooseX-Role-Parameterized@rt.cpan.org|mailto:bug-MooseX-Role-Parameterized@rt.cpan.org>).
There is also a mailing list available for users of this distribution, at
L<http://lists.perl.org/list/moose.html>.
There is also an irc channel available for users of this distribution, at
L<C<#moose> on C<irc.perl.org>|irc://irc.perl.org/#moose>.
=head1 AUTHOR
Shawn M Moore <code@sartak.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2008 by Shawn M Moore.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut