| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package MooseX::Test::Role; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | our $VERSION = '0.06'; | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 6 |  |  | 6 |  | 308364 | use strict; | 
|  | 6 |  |  |  |  | 16 |  | 
|  | 6 |  |  |  |  | 224 |  | 
| 6 | 6 |  |  | 6 |  | 34 | use warnings; | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 187 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 6 |  |  | 6 |  | 44 | use Carp qw( confess ); | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 564 |  | 
| 9 | 6 |  |  | 6 |  | 2416 | use Class::Load qw( try_load_class ); | 
|  | 6 |  |  |  |  | 93566 |  | 
|  | 6 |  |  |  |  | 303 |  | 
| 10 | 6 |  |  | 6 |  | 38 | use List::Util qw( first ); | 
|  | 6 |  |  |  |  | 13 |  | 
|  | 6 |  |  |  |  | 673 |  | 
| 11 | 6 |  |  | 6 |  | 34 | use Test::Builder; | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 172 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 6 |  |  | 6 |  | 36 | use Exporter qw( import unimport ); | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 5723 |  | 
| 14 |  |  |  |  |  |  | our @EXPORT = qw( requires_ok consumer_of consuming_object consuming_class ); | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | sub requires_ok { | 
| 17 | 1 |  |  | 1 | 1 | 34749 | my ( $role, @required ) = @_; | 
| 18 | 1 |  |  |  |  | 5 | my $msg = "$role requires " . join( ', ', @required ); | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 1 |  |  |  |  | 4 | my $role_type = _derive_role_type($role); | 
| 21 | 0 | 0 |  |  |  | 0 | if (!$role_type) { | 
| 22 | 0 |  |  |  |  | 0 | ok( 0, $msg ); | 
| 23 | 0 |  |  |  |  | 0 | return; | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 0 |  |  |  |  | 0 | foreach my $req (@required) { | 
| 27 | 0 | 0 |  | 0 |  | 0 | unless ( first { $_ eq $req } _required_methods($role_type, $role) ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 28 | 0 |  |  |  |  | 0 | ok( 0, $msg ); | 
| 29 | 0 |  |  |  |  | 0 | return; | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  | } | 
| 32 | 0 |  |  |  |  | 0 | ok( 1, $msg ); | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | sub consuming_class { | 
| 36 | 2 |  |  | 2 | 1 | 28537 | my ( $role, %args ) = @_; | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 2 | 50 |  |  |  | 12 | my %methods = exists $args{methods} ? %{ $args{methods} } : (); | 
|  | 0 |  |  |  |  | 0 |  | 
| 39 |  |  |  |  |  |  |  | 
| 40 | 2 |  |  |  |  | 11 | my $role_type = _derive_role_type($role); | 
| 41 | 0 | 0 |  |  |  | 0 | confess 'first argument should be a role' unless $role_type; | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 0 |  |  |  |  | 0 | my $package = _package_name(); | 
| 44 | 0 |  |  |  |  | 0 | _add_methods( | 
| 45 |  |  |  |  |  |  | package   => $package, | 
| 46 |  |  |  |  |  |  | role_type => $role_type, | 
| 47 |  |  |  |  |  |  | role      => $role, | 
| 48 |  |  |  |  |  |  | methods   => \%methods, | 
| 49 |  |  |  |  |  |  | ); | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 0 |  |  |  |  | 0 | _apply_role( | 
| 52 |  |  |  |  |  |  | package   => $package, | 
| 53 |  |  |  |  |  |  | role_type => $role_type, | 
| 54 |  |  |  |  |  |  | role      => $role, | 
| 55 |  |  |  |  |  |  | ); | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 0 |  |  |  |  | 0 | return $package; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | sub consuming_object { | 
| 61 | 1 |  |  | 1 | 1 | 28794 | my $class = consuming_class(@_); | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | # Moose and Moo can be instantiated and should be. Role::Tiny however isn't | 
| 64 |  |  |  |  |  |  | # a full OO implementation and so doesn't provide a "new" method. | 
| 65 | 0 | 0 |  |  |  | 0 | return $class->can('new') ? $class->new() : $class; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub consumer_of { | 
| 69 | 1 |  |  | 1 | 1 | 27597 | my ( $role, %methods ) = @_; | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 1 | 0 |  |  |  | 5 | confess 'first argument to consumer_of should be a role' unless _derive_role_type($role); | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 0 |  |  |  |  | 0 | return consuming_object( $role, methods => \%methods ); | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | sub _required_methods { | 
| 77 | 0 |  |  | 0 |  | 0 | my ($role_type, $role) = @_; | 
| 78 | 0 |  |  |  |  | 0 | my @methods; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 0 | 0 |  |  |  | 0 | if ($role_type eq 'Moose::Role') { | 
|  |  | 0 |  |  |  |  |  | 
| 81 | 0 |  |  |  |  | 0 | @methods = $role->meta->get_required_method_list(); | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  | elsif ($role_type eq 'Role::Tiny') { | 
| 84 | 0 |  |  |  |  | 0 | my $info = _role_tiny_info($role); | 
| 85 | 0 | 0 | 0 |  |  | 0 | if ($info && ref($info->{requires}) eq 'ARRAY') { | 
| 86 | 0 |  |  |  |  | 0 | @methods = @{$info->{requires}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 0 | 0 |  |  |  | 0 | return wantarray ? @methods : \@methods; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub _derive_role_type { | 
| 94 | 4 |  |  | 4 |  | 9 | my $role = shift; | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 4 | 0 | 33 |  |  | 71 | if ($role->can('meta') && $role->meta()->isa('Moose::Meta::Role')) { | 
| 97 |  |  |  |  |  |  | # Also covers newer Moo::Roles | 
| 98 | 0 |  |  |  |  |  | return 'Moose::Role'; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 0 | 0 | 0 |  |  |  | if (try_load_class('Role::Tiny') && _role_tiny_info($role)) { | 
| 102 |  |  |  |  |  |  | # Also covers older Moo::Roles | 
| 103 | 0 |  |  |  |  |  | return 'Role::Tiny'; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 0 |  |  |  |  |  | return; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | my $package_counter = 0; | 
| 110 |  |  |  |  |  |  | sub _package_name { | 
| 111 | 0 |  |  | 0 |  |  | return 'MooseX::Test::Role::Consumer' . $package_counter++; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | sub _apply_role { | 
| 115 | 0 |  |  | 0 |  |  | my %args = @_; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 0 |  |  |  |  |  | my $package   = $args{package}; | 
| 118 | 0 |  |  |  |  |  | my $role_type = $args{role_type}; | 
| 119 | 0 |  |  |  |  |  | my $role      = $args{role}; | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | # We'll need a thing that exports a "with" sub | 
| 122 | 0 |  |  |  |  |  | my $with_exporter; | 
| 123 | 0 | 0 |  |  |  |  | if ($role_type eq 'Moose::Role') { | 
|  |  | 0 |  |  |  |  |  | 
| 124 | 0 |  |  |  |  |  | $with_exporter = 'Moose'; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  | elsif ($role_type eq 'Role::Tiny') { | 
| 127 | 0 |  |  |  |  |  | $with_exporter = 'Role::Tiny::With'; | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  | else { | 
| 130 | 0 |  |  |  |  |  | confess "Unknown role type $role_type"; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 0 |  |  |  |  |  | my $source = qq{ | 
| 134 |  |  |  |  |  |  | package $package; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | use $with_exporter; | 
| 137 |  |  |  |  |  |  | with('$role'); | 
| 138 |  |  |  |  |  |  | }; | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | #warn $source; | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 0 |  |  |  |  |  | eval($source); | 
| 143 | 0 | 0 |  |  |  |  | die $@ if $@; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 0 |  |  |  |  |  | return $package; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | sub _add_methods { | 
| 149 | 0 |  |  | 0 |  |  | my %args = @_; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 0 |  |  |  |  |  | my $role_type = $args{role_type}; | 
| 152 | 0 |  |  |  |  |  | my $package   = $args{package}; | 
| 153 | 0 |  |  |  |  |  | my $role      = $args{role}; | 
| 154 | 0 |  |  |  |  |  | my $methods   = $args{methods}; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 0 |  | 0 | 0 |  |  | $methods->{$_} ||= sub { undef } for _required_methods( $role_type, $role ); | 
|  | 0 |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 0 |  |  |  |  |  | my $meta; | 
| 159 | 0 | 0 |  |  |  |  | $meta = Moose::Meta::Class->create($package) if $role_type eq 'Moose::Role'; | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 0 |  |  |  |  |  | while ( my ( $method, $subref ) = each(%{$methods}) ) { | 
|  | 0 |  |  |  |  |  |  | 
| 162 | 0 | 0 |  |  |  |  | if ($meta) { | 
| 163 | 0 |  |  |  |  |  | $meta->add_method($method => $subref); | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | else { | 
| 166 | 6 |  |  | 6 |  | 36 | no strict 'refs'; | 
|  | 6 |  |  |  |  | 11 |  | 
|  | 6 |  |  |  |  | 1325 |  | 
| 167 |  |  |  |  |  |  | #no warnings 'redefine'; | 
| 168 | 0 |  |  |  |  |  | *{ $package . '::' . $method } = $subref; | 
|  | 0 |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 0 |  |  |  |  |  | return; | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | sub _role_tiny_info { | 
| 176 |  |  |  |  |  |  | # This seems brittle, but there aren't many options to get this data. | 
| 177 |  |  |  |  |  |  | # Moo relies on %INFO too, so it seems like it would be a hard thing | 
| 178 |  |  |  |  |  |  | # for to move away from. | 
| 179 |  |  |  |  |  |  |  | 
| 180 | 0 |  |  | 0 |  |  | my $role = shift; | 
| 181 | 0 |  |  |  |  |  | return $Role::Tiny::INFO{$role}; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | my $Test = Test::Builder->new(); | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | # Done this way for easier testing | 
| 187 |  |  |  |  |  |  | our $ok = sub { $Test->ok(@_) }; | 
| 188 | 0 |  |  | 0 | 0 |  | sub ok { $ok->(@_) } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | 1; | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | =pod | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | =head1 NAME | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | MooseX::Test::Role - Test functions for Moose roles | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | use MooseX::Test::Role; | 
| 201 |  |  |  |  |  |  | use Test::More tests => 2; | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | requires_ok('MyRole', qw/method1 method2/); | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | my $consumer = consuming_object( | 
| 206 |  |  |  |  |  |  | 'MyRole', | 
| 207 |  |  |  |  |  |  | methods => { | 
| 208 |  |  |  |  |  |  | method1 => sub { 1 } | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  | ); | 
| 211 |  |  |  |  |  |  | ok( $consumer->myrole_method ); | 
| 212 |  |  |  |  |  |  | is( $consumer->method1, 1 ); | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | my $consuming_class = consuming_class('MyRole'); | 
| 215 |  |  |  |  |  |  | ok( $consuming_class->class_method() ); | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | Provides functions for testing roles. Supports roles created with | 
| 220 |  |  |  |  |  |  | L, L or L. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | =head1 BACKGROUND | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | Unit testing a role can be hard. A major problem is creating classes that | 
| 225 |  |  |  |  |  |  | consume the role. | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | One could side-step the problem entirely and just call the subroutines in the | 
| 228 |  |  |  |  |  |  | role's package directly. For example, | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | Fooable->bar(); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | That only works until C calls another method in the consuming class | 
| 233 |  |  |  |  |  |  | though. Mock objects are a tempting way to solve that problem: | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | my $consumer = Test::MockObject->new(); | 
| 236 |  |  |  |  |  |  | $consumer->set_always('baz', 1); | 
| 237 |  |  |  |  |  |  | Fooable::bar($consumer); | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | But if C happens to call another method in the role then | 
| 240 |  |  |  |  |  |  | the mock consumer will have to mock that method too. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | A better way is to create a class to consume the role: | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | package FooableTest; | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | use Moose; | 
| 247 |  |  |  |  |  |  | with 'Fooable'; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | sub required_method {} | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | package main; | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | my $consumer = FooableTest->new(); | 
| 254 |  |  |  |  |  |  | $consumer->bar(); | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | This can work well for some roles. Unfortunately, if several variations have to | 
| 257 |  |  |  |  |  |  | be tested, it may be necessary to create several consuming test classes, which | 
| 258 |  |  |  |  |  |  | gets tedious. | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | Moose can create anonymous classes which consume roles: | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | my $consumer = Moose::Meta::Class->create_anon_class( | 
| 263 |  |  |  |  |  |  | roles   => ['Fooable'], | 
| 264 |  |  |  |  |  |  | methods => { | 
| 265 |  |  |  |  |  |  | required_method => sub {}, | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  | )->new_object(); | 
| 268 |  |  |  |  |  |  | $consumer->bar(); | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | This can still be tedious, especially for roles that require lots of methods. | 
| 271 |  |  |  |  |  |  | C simply makes this easier to do. | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | =head1 EXPORTED FUNCTIONS | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | =over 4 | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | =item C \%methods)> | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | Creates a class which consumes the role, and returns it's package name. | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | C<$role> must be the package name of a role. L, L and | 
| 282 |  |  |  |  |  |  | L are supported. | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | Any method required by the role will be stubbed. To override the default stub | 
| 285 |  |  |  |  |  |  | methods, or to add additional methods, specify the name and a coderef: | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | consuming_class('MyRole', | 
| 288 |  |  |  |  |  |  | method1 => sub { 'one' }, | 
| 289 |  |  |  |  |  |  | method2 => sub { 'two' }, | 
| 290 |  |  |  |  |  |  | required_method => sub { 'required' }, | 
| 291 |  |  |  |  |  |  | ); | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | =item C \%methods)> | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | Creates a class which consumes the role, and returns an instance of it. | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | If the class does not have a C method (which is commonly the case for | 
| 298 |  |  |  |  |  |  | L), then the package name will be returned instead. | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | See C for arguments. C is essentially | 
| 301 |  |  |  |  |  |  | equivalent to: | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | consuming_class(@_)->new(); | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | =item C | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | Alias of C, without named arguments. This is left in for | 
| 308 |  |  |  |  |  |  | compatibility, new code should use C. | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | =item C | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | Tests if role requires one or more methods. | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | =back | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | =head1 GITHUB | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | Patches, comments or mean-spirited code reviews are all welcomed on GitHub: | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | L | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | =head1 AUTHOR | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | Paul Boyd | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | This software is copyright (c) 2014 by Paul Boyd. | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 331 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | =cut |