File Coverage

blib/lib/MooseX/Method/Signatures.pm
Criterion Covered Total %
statement 216 218 99.0
branch 53 64 82.8
condition 13 20 65.0
subroutine 37 39 94.8
pod 0 7 0.0
total 319 348 91.6


line stmt bran cond sub pod time code
1 28     28   6858243 use strict;
  28         53  
  28         993  
2 28     28   125 use warnings;
  28         42  
  28         1446  
3              
4             package MooseX::Method::Signatures;
5             # git description: v0.47-12-g2cd3471
6             $MooseX::Method::Signatures::VERSION = '0.48';
7             # ABSTRACT: DEPRECATED: Method declarations with type constraints and no source filter
8             # KEYWORDS: moose extension method declaration signature prototype syntax sugar deprecated
9              
10 28     28   9727 use Moose 0.89;
  28         4320738  
  28         170  
11 28     28   171369 use Devel::Declare 0.005011 ();
  28         120700  
  28         844  
12 28     28   184 use B::Hooks::EndOfScope 0.10;
  28         468  
  28         199  
13 28     28   2265 use Moose::Meta::Class;
  28         50  
  28         765  
14 28     28   14077 use MooseX::LazyRequire 0.06;
  28         444106  
  28         234  
15 28     28   302191 use MooseX::Types::Moose 0.19 qw/Str Bool CodeRef/;
  28         1206653  
  28         316  
16 28     28   155561 use Text::Balanced qw/extract_quotelike/;
  28         438884  
  28         2543  
17 28     28   14506 use MooseX::Method::Signatures::Meta::Method;
  28         100  
  28         1481  
18 28     28   222 use MooseX::Method::Signatures::Types qw/PrototypeInjections/;
  28         55  
  28         264  
19 28     28   34349 use Sub::Name;
  28         60  
  28         1727  
20 28     28   142 use Moose::Util 'find_meta';
  28         41  
  28         232  
21 28     28   5137 use Module::Runtime 'use_module';
  28         54  
  28         188  
22 28     28   1126 use Carp;
  28         52  
  28         1664  
23              
24 28     28   148 use aliased 'Devel::Declare::Context::Simple', 'ContextSimple';
  28         46  
  28         236  
25              
26 28     28   68287 use namespace::autoclean;
  28         53  
  28         269  
27              
28             has package => (
29             is => 'ro',
30             isa => Str,
31             lazy_required => 1,
32             );
33              
34             has context => (
35             is => 'ro',
36             isa => ContextSimple,
37             lazy => 1,
38             builder => '_build_context',
39             );
40              
41             has initialized_context => (
42             is => 'ro',
43             isa => Bool,
44             default => 0,
45             );
46              
47             has custom_method_application => (
48             is => 'ro',
49             isa => CodeRef,
50             predicate => 'has_custom_method_application',
51             );
52              
53             has prototype_injections => (
54             is => 'ro',
55             isa => PrototypeInjections,
56             predicate => 'has_prototype_injections',
57             );
58              
59             sub _build_context {
60 87     87   157 my ($self) = @_;
61 87         2952 return ContextSimple->new(into => $self->package);
62             }
63              
64             sub import {
65 88     88   357062 my ($class, %args) = @_;
66 88         264 my $caller = caller();
67 88         414 $class->setup_for($caller, \%args);
68             }
69              
70             sub setup_for {
71 88     88 0 185 my ($class, $pkg, $args) = @_;
72              
73             # process arguments to import
74 88         164 while (my ($declarator, $injections) = each %{ $args }) {
  91         496  
75 3         108 my $obj = $class->new(
76             package => $pkg,
77             prototype_injections => {
78             declarator => $declarator,
79             injections => $injections,
80             },
81             );
82              
83             Devel::Declare->setup_for($pkg, {
84 3     3   382 $declarator => { const => sub { $obj->parser(@_) } },
85 3         23 });
86              
87             {
88 28     28   9084 no strict 'refs';
  28         52  
  28         3139  
  3         62  
89 3     0   7 *{ "${pkg}::$declarator" } = sub {};
  3         21  
  0         0  
90             }
91             }
92              
93 88         3337 my $self = $class->new(package => $pkg);
94              
95             Devel::Declare->setup_for($pkg, {
96 152     152   147382 method => { const => sub { $self->parser(@_) } },
97 88         1248 });
98              
99             {
100 28     28   134 no strict 'refs';
  28         45  
  28         44423  
  88         3199  
101 88     1   352 *{ "${pkg}::method" } = sub {};
  88         520  
  1         8  
102             }
103              
104 88         7717 return;
105             }
106              
107             sub strip_name {
108 155     155 0 288 my ($self) = @_;
109 155         5184 my $ctx = $self->context;
110 155         683 my $ret = $ctx->strip_name;
111 155 100       4802 return $ret if defined $ret;
112              
113 20         54 my $line = $ctx->get_linestr;
114 20         107 my $offset = $ctx->offset;
115 20         64 local $@;
116 20         50 my $copy = substr($line, $offset);
117 20         100 my ($str) = extract_quotelike($copy);
118 20 100       1629 return unless defined $str;
119              
120 2 50 33     17 return if ($@ && $@ =~ /^No quotelike operator found/);
121 2 50       6 die $@ if $@;
122              
123 2         7 substr($line, $offset, length $str) = '';
124 2         9 $ctx->set_linestr($line);
125              
126 2         16 return \$str;
127             }
128              
129             sub strip_traits {
130 155     155 0 241 my ($self) = @_;
131              
132 155         5406 my $ctx = $self->context;
133 155         489 my $linestr = $ctx->get_linestr;
134              
135 155 100 100     970 unless (substr($linestr, $ctx->offset, 2) eq 'is' ||
136             substr($linestr, $ctx->offset, 4) eq 'does') {
137             # No 'is' means no traits
138 148         1884 return;
139             }
140              
141 7         78 my @traits;
142              
143 7         17 while (1) {
144 17 100       72 if (substr($linestr, $ctx->offset, 2) eq 'is') {
    100          
145             # Eat the 'is' so we can call strip_names_and_args
146 6         35 substr($linestr, $ctx->offset, 2) = '';
147             } elsif (substr($linestr, $ctx->offset, 4) eq 'does') {
148             # Eat the 'does' so we can call strip_names_and_args
149 4         44 substr($linestr, $ctx->offset, 4) = '';
150             } else {
151 7         66 last;
152             }
153              
154 10         53 $ctx->set_linestr($linestr);
155 10         51 push @traits, @{ $ctx->strip_names_and_args };
  10         32  
156             # Get the current linestr so that the loop can look for more 'is'
157 10         660 $ctx->skipspace;
158 10         74 $linestr = $ctx->get_linestr;
159             }
160              
161 7 50       21 confess "expected traits after 'is' or 'does', found nothing"
162             unless scalar(@traits);
163              
164             # Let's check to make sure these traits aren't aliased locally
165 7         24 for my $t (@traits) {
166 12 50       41 next if $t->[0] =~ /::/;
167 12         37 my $class = $ctx->get_curstash_name;
168 12   33     74 my $meta = find_meta($class) || Moose::Meta::Class->initialize($class);
169 12         162 my $func = $meta->get_package_symbol('&' . $t->[0]);
170 12 100       154 next unless $func;
171              
172 7         13 my $proto = prototype $func;
173 7 50 33     31 next if !defined $proto || length $proto;
174              
175 7         24 $t->[0] = $func->();
176             }
177              
178 7         19 return \@traits;
179             }
180              
181             sub strip_return_type_constraint {
182 155     155 0 270 my ($self) = @_;
183 155         5223 my $ctx = $self->context;
184 155         452 my $returns = $ctx->strip_name;
185 155 100       2724 return unless defined $returns;
186 2 50       5 confess "expected 'returns', found '${returns}'"
187             unless $returns eq 'returns';
188 2         6 return $ctx->strip_proto;
189             }
190              
191             sub parser {
192 155     155 0 326 my $self = shift;
193 155         261 my $err;
194              
195             # Keep any previous compile errors from getting stepped on. But report
196             # errors from inside MXMS nicely.
197             {
198 155         258 local $@;
  155         246  
199 155         308 eval { $self->_parser(@_) };
  155         490  
200 155         47638 $err = $@;
201             }
202              
203 155 100       3248 die $err if $err;
204             }
205              
206             my $anon_counter = 1;
207             sub _parser {
208 155     155   258 my $self = shift;
209 155         6466 my $ctx = $self->context;
210 155 50       5592 $ctx->init(@_) unless $self->initialized_context;
211              
212 155         1841 $ctx->skip_declarator;
213 155         5133 my $name = $self->strip_name;
214 155         617 my $proto = $ctx->strip_proto;
215 155   100     5806 my $attrs = $ctx->strip_attrs || '';
216 155         3492 my $traits = $self->strip_traits;
217 155         543 my $ret_tc = $self->strip_return_type_constraint;
218              
219 155         550 my $compile_stash = $ctx->get_curstash_name;
220              
221 155         937 my %args = (
222             # This might get reset later, but its where we search for exported
223             # symbols at compile time
224             package_name => $compile_stash,
225             );
226 155 100       823 $args{ signature } = qq{($proto)} if defined $proto;
227 155 100       425 $args{ traits } = $traits if $traits;
228 155 100       468 $args{ return_signature } = $ret_tc if defined $ret_tc;
229              
230             # Class::MOP::Method requires a name
231 155   66     583 $args{ name } = $name || '__ANON__'.($anon_counter++).'__';
232              
233 155 100       6718 if ($self->has_prototype_injections) {
234 3 50       12 confess('Configured declarator does not match context declarator')
235             if $ctx->declarator ne $self->prototype_injections->{declarator};
236 3         80 $args{prototype_injections} = $self->prototype_injections->{injections};
237             }
238              
239 155         344 my $meth_class = 'MooseX::Method::Signatures::Meta::Method';
240 155 100       550 if ($args{traits}) {
241 7         22 my @traits = ();
242 7         10 foreach my $t (@{$args{traits}}) {
  7         25  
243 12         49 use_module($t->[0]);
244 12 100       323 if ($t->[1]) {
245 1         86 %args = (%args, eval $t->[1]);
246             };
247 12         36 push @traits, $t->[0];
248             }
249 7         72 my $meta = Moose::Meta::Class->create_anon_class(
250             superclasses => [ $meth_class ],
251             roles => [ @traits ],
252             cache => 1,
253             );
254 7         34365 $meth_class = $meta->name;
255 7         26 delete $args{traits};
256             }
257              
258 155     0   1851 my $proto_method = $meth_class->wrap(sub { }, %args);
  0         0  
259              
260 152         487 my $after_block = ')';
261              
262 152 100       414 if ($traits) {
263 7 100       16 if (my @trait_args = grep { defined } map { $_->[1] } @{ $traits }) {
  12         51  
  12         33  
  7         24  
264 1         7 $after_block = q{, } . join(q{,} => @trait_args) . $after_block;
265             }
266             }
267              
268 152 100       477 if (defined $name) {
269 136 100       675 my $name_arg = q{, } . (ref $name ? ${$name} : qq{q[${name}]});
  2         7  
270 136         376 $after_block = $name_arg . $after_block . q{;};
271             }
272              
273 152         6102 my $inject = $proto_method->injectable_code;
274 152         803 $inject = $self->scope_injector_call($after_block) . $inject;
275              
276 152         1083 $ctx->inject_if_block($inject, "(sub ${attrs} ");
277              
278             my $create_meta_method = sub {
279 154     154   325 my ($code, $pkg, $meth_name, @args) = @_;
280 154         1143 subname $pkg . "::" .$meth_name, $code;
281              
282             # we want to reinitialize with all the args,
283             # so we give the opportunity for traits to wrap the correct
284             # closure.
285 154         204 my %other_args = %{$proto_method};
  154         5597  
286 154         404 delete $other_args{body};
287 154         251 delete $other_args{actual_body};
288              
289 154         1217 my $ret = $meth_class->wrap(
290             $code,
291             %other_args, @args
292             );
293 152         7909 };
294              
295 152 100       430 if (defined $name) {
296             my $apply = $self->has_custom_method_application
297             ? $self->custom_method_application
298             : sub {
299 136     136   259 my ($meta, $name, $method) = @_;
300              
301 136 100 100     16740 if (warnings::enabled("redefine") && (my $meta_meth = $meta->get_method($name))) {
302 2 100       183 warnings::warn("redefine", "Method $name redefined on package ${ \$meta->name }")
  1         211  
303             if $meta_meth->isa('MooseX::Method::Signatures::Meta::Method');
304             }
305              
306 136         9219 $meta->add_method($name => $method);
307 136 50       6557 };
308              
309             $ctx->shadow(sub {
310 136     136   8141 my ($code, $name, @args) = @_;
311              
312 136         239 my $pkg = $compile_stash;
313 136 50       530 ($pkg, $name) = $name =~ /^(.*)::([^:]+)$/
314             if $name =~ /::/;
315              
316 136         427 my $meth = $create_meta_method->($code, $pkg, $name, @args);
317 136         832 my $meta = Moose::Meta::Class->initialize($pkg);
318              
319 136         5538 $meta->$apply($name, $meth);
320 136         10495 return;
321 136         1221 });
322             }
323             else {
324             $ctx->shadow(sub {
325 18     18   7166 return $create_meta_method->(shift, $compile_stash, '__ANON__', @_);
326 16         123 });
327             }
328             }
329              
330             sub scope_injector_call {
331 152     152 0 310 my ($self, $code) = @_;
332 152         414 $code =~ s/'/\\'/g; # we're generating code that's quoted with single quotes
333 152         226 return qq[BEGIN { ${\ref $self}->inject_scope('${code}') }];
  152         854  
334             }
335              
336             sub inject_scope {
337 152     152 0 13333 my ($class, $inject) = @_;
338             on_scope_end {
339 152     152   8362 my $line = Devel::Declare::get_linestr();
340 152 50       684 return unless defined $line;
341 152         657 my $offset = Devel::Declare::get_linestr_offset();
342 152         385 substr($line, $offset, 0) = $inject;
343 152         843 Devel::Declare::set_linestr($line);
344 152         1537 };
345             }
346              
347             __PACKAGE__->meta->make_immutable;
348              
349             1;
350              
351             __END__
352              
353             =pod
354              
355             =encoding UTF-8
356              
357             =head1 NAME
358              
359             MooseX::Method::Signatures - DEPRECATED: Method declarations with type constraints and no source filter
360              
361             =head1 VERSION
362              
363             version 0.48
364              
365             =head1 SYNOPSIS
366              
367             package Foo;
368              
369             use Moose;
370             use MooseX::Method::Signatures;
371              
372             method morning (Str $name) {
373             $self->say("Good morning ${name}!");
374             }
375              
376             method hello (Str :$who, Int :$age where { $_ > 0 }) {
377             $self->say("Hello ${who}, I am ${age} years old!");
378             }
379              
380             method greet (Str $name, Bool :$excited = 0) {
381             if ($excited) {
382             $self->say("GREETINGS ${name}!");
383             }
384             else {
385             $self->say("Hi ${name}!");
386             }
387             }
388              
389             $foo->morning('Resi'); # This works.
390              
391             $foo->hello(who => 'world', age => 42); # This too.
392              
393             $foo->greet('Resi', excited => 1); # And this as well.
394              
395             $foo->hello(who => 'world', age => 'fortytwo'); # This doesn't.
396              
397             $foo->hello(who => 'world', age => -23); # This neither.
398              
399             $foo->morning; # Won't work.
400              
401             $foo->greet; # Will fail.
402              
403             =head1 DESCRIPTION
404              
405             Provides a proper method keyword, like "sub" but specifically for making methods
406             and validating their arguments against Moose type constraints.
407              
408             =head1 SIGNATURE SYNTAX
409              
410             The signature syntax is heavily based on Perl 6. However not the full Perl 6
411             signature syntax is supported yet and some of it never will be.
412              
413             =head2 Type Constraints
414              
415             method foo ( $affe) # no type checking
416             method bar (Animal $affe) # $affe->isa('Animal')
417             method baz (Animal|Human $affe) # $affe->isa('Animal') || $affe->isa('Human')
418              
419             =head2 Positional vs. Named
420              
421             method foo ( $a, $b, $c) # positional
422             method bar (:$a, :$b, :$c) # named
423             method baz ( $a, $b, :$c) # combined
424              
425             =head2 Required vs. Optional
426              
427             method foo ($a , $b!, :$c!, :$d!) # required
428             method bar ($a?, $b?, :$c , :$d?) # optional
429              
430             =head2 Defaults
431              
432             method foo ($a = 42) # defaults to 42
433              
434             =head2 Constraints
435              
436             method foo ($foo where { $_ % 2 == 0 }) # only even
437              
438             =for stopwords Invocant
439              
440             =head2 Invocant
441              
442             method foo ( $moo) # invocant is called $self and is required
443             method bar ($self: $moo) # same, but explicit
444             method baz ($class: $moo) # invocant is called $class
445              
446             =head2 Labels
447              
448             method foo (: $affe ) # called as $obj->foo(affe => $value)
449             method bar (:apan($affe)) # called as $obj->foo(apan => $value)
450              
451             =head2 Traits
452              
453             method foo (Affe $bar does trait)
454             method foo (Affe $bar is trait)
455              
456             The only currently supported trait is C<coerce>, which will attempt to coerce
457             the value provided if it doesn't satisfy the requirements of the type
458             constraint.
459              
460             =head2 Placeholders
461              
462             method foo ($bar, $, $baz)
463              
464             =for stopwords sigil
465              
466             Sometimes you don't care about some parameters you're being called with. Just put
467             the bare sigil instead of a full variable name into the signature to avoid an
468             extra lexical variable to be created.
469              
470             =head2 Complex Example
471              
472             method foo ( SomeClass $thing where { $_->can('stuff') }:
473             Str $bar = "apan",
474             Int :$baz! = 42 where { $_ % 2 == 0 } where { $_ > 10 } )
475              
476             # the invocant is called $thing, must be an instance of SomeClass and
477             has to implement a 'stuff' method
478             # $bar is positional, required, must be a string and defaults to "apan"
479             # $baz is named, required, must be an integer, defaults to 42 and needs
480             # to be even and greater than 10
481              
482             =head1 BUGS, CAVEATS AND NOTES
483              
484             This module is as stable now, but this is not to say that it is entirely bug
485             free. If you notice any odd behaviour (messages not being as good as they could
486             for example) then please raise a bug.
487              
488             =head2 Fancy signatures
489              
490             L<Parse::Method::Signatures> is used to parse the signatures. However, some
491             signatures that can be parsed by it aren't supported by this module (yet).
492              
493             =head2 No source filter
494              
495             While this module does rely on the hairy black magic of L<Devel::Declare> it
496             does not depend on a source filter. As such, it doesn't try to parse and
497             rewrite your source code and there should be no weird side effects.
498              
499             Devel::Declare only effects compilation. After that, it's a normal subroutine.
500             As such, for all that hairy magic, this module is surprisingly stable.
501              
502             =head2 What about regular subroutines?
503              
504             L<Devel::Declare> cannot yet change the way C<sub> behaves. However, the
505             L<signatures|signatures> module can. Right now it only provides very basic
506             signatures, but it's extendable enough that plugging MooseX::Method::Signatures
507             signatures into that should be quite possible.
508              
509             =head2 What about the return value?
510              
511             Type constraints for return values can be declared using
512              
513             method foo (Int $x, Str $y) returns (Bool) { ... }
514              
515             however, this feature only works with scalar return values and is still
516             considered to be experimental.
517              
518             =head2 Interaction with L<Moose::Role>
519              
520             =head3 Methods not seen by a role's C<requires>
521              
522             Because the processing of the L<MooseX::Method::Signatures>
523             C<method> and the L<Moose> C<with> keywords are both
524             done at runtime, it can happen that a role will require
525             a method before it is declared (which will cause
526             Moose to complain very loudly and abort the program).
527              
528             For example, the following will not work:
529              
530             # in file Canine.pm
531              
532             package Canine;
533              
534             use Moose;
535             use MooseX::Method::Signatures;
536              
537             with 'Watchdog';
538              
539             method bark { print "Woof!\n"; }
540              
541             1;
542              
543              
544             # in file Watchdog.pm
545              
546             package Watchdog;
547              
548             use Moose::Role;
549              
550             requires 'bark'; # will assert! evaluated before 'method' is processed
551              
552             sub warn_intruder {
553             my $self = shift;
554             my $intruder = shift;
555              
556             $self->bark until $intruder->gone;
557             }
558              
559             1;
560              
561             A workaround for this problem is to use C<with> only
562             after the methods have been defined. To take our previous
563             example, B<Canine> could be reworked thus:
564              
565             package Canine;
566              
567             use Moose;
568             use MooseX::Method::Signatures;
569              
570             method bark { print "Woof!\n"; }
571              
572             with 'Watchdog';
573              
574             1;
575              
576             A better solution is to use L<MooseX::Declare> instead of plain
577             L<MooseX::Method::Signatures>. It defers application of roles until the end
578             of the class definition. With it, our example would becomes:
579              
580             # in file Canine.pm
581              
582             use MooseX::Declare;
583              
584             class Canine with Watchdog {
585             method bark { print "Woof!\n"; }
586             }
587              
588             1;
589              
590             # in file Watchdog.pm
591              
592             use MooseX::Declare;
593              
594             role Watchdog {
595             requires 'bark';
596              
597             method warn_intruder ( $intruder ) {
598             $self->bark until $intruder->gone;
599             }
600             }
601              
602             1;
603              
604             =head3 I<Subroutine redefined> warnings
605              
606             When composing a L<Moose::Role> into a class that uses
607             L<MooseX::Method::Signatures>, you may get a "Subroutine redefined"
608             warning. This happens when both the role and the class define a
609             method/subroutine of the same name. (The way roles work, the one
610             defined in the class takes precedence.) To eliminate this warning,
611             make sure that your C<with> declaration happens after any
612             method/subroutine declarations that may have the same name as a
613             method/subroutine within a role.
614              
615             =head1 WARNING
616              
617             =for stopwords mst
618              
619             =for comment rafl agreed we should have a warning, and mst wrote this for MooseX::Declare, but it applies equally well here:
620              
621             B<Warning:> MooseX::Method::Signatures and L<MooseX::Declare> are based on
622             L<Devel::Declare>, a giant bag of crack originally implemented by mst with the
623             goal of upsetting the perl core developers so much by its very existence that
624             they implemented proper keyword handling in the core.
625              
626             As of perl5 version 14, this goal has been achieved, and modules such as
627             L<Devel::CallParser>, L<Function::Parameters>, and L<Keyword::Simple> provide
628             mechanisms to mangle perl syntax that don't require hallucinogenic drugs to
629             interpret the error messages they produce.
630              
631             If you want to use declarative syntax in new code, please for the love
632             of kittens get yourself a recent perl and look at L<Moops> and
633             L<core signatures|perlsub/Signatures> instead.
634              
635             =head1 SEE ALSO
636              
637             =over 4
638              
639             =item *
640              
641             L<MooseX::Declare>
642              
643             =item *
644              
645             L<Method::Signatures::Simple>
646              
647             =item *
648              
649             L<Method::Signatures>
650              
651             =item *
652              
653             L<Devel::Declare>
654              
655             =item *
656              
657             L<Parse::Method::Signatures>
658              
659             =item *
660              
661             L<Moose>
662              
663             =item *
664              
665             L<signatures>
666              
667             =back
668              
669             =head1 AUTHOR
670              
671             Florian Ragwitz <rafl@debian.org>
672              
673             =head1 COPYRIGHT AND LICENSE
674              
675             This software is copyright (c) 2014 by Florian Ragwitz.
676              
677             This is free software; you can redistribute it and/or modify it under
678             the same terms as the Perl 5 programming language system itself.
679              
680             =head1 CONTRIBUTORS
681              
682             =for stopwords Karen Etheridge Ash Berlin Daniel Ruoso Nicholas Perez Yanick Champoux Cory Watson Justin Hunter Rhesa Rozendaal Hakim Cassimally Kent Fredric Lukas Mai Dagfinn Ilmari MannsÃ¥ker Matt Kraai Ricardo SIGNES Steffen Schwigon Dave Rolsky Jesse Luehrs Jonathan Rockway Scott Duff Maik Hentsche Sebastian Willert
683              
684             =over 4
685              
686             =item *
687              
688             Karen Etheridge <ether@cpan.org>
689              
690             =item *
691              
692             Ash Berlin <ash@cpan.org>
693              
694             =item *
695              
696             Daniel Ruoso <daniel@ruoso.com>
697              
698             =item *
699              
700             Nicholas Perez <nperez@cpan.org>
701              
702             =item *
703              
704             Yanick Champoux <yanick@babyl.dyndns.org>
705              
706             =item *
707              
708             Cory Watson <gphat@cpan.org>
709              
710             =item *
711              
712             Justin Hunter <justin.d.hunter@gmail.com>
713              
714             =item *
715              
716             Rhesa Rozendaal <rhesa@cpan.org>
717              
718             =item *
719              
720             Hakim Cassimally <osfameron@cpan.org>
721              
722             =item *
723              
724             Kent Fredric <kentfredric@gmail.com>
725              
726             =item *
727              
728             Lukas Mai <l.mai@web.de>
729              
730             =item *
731              
732             Dagfinn Ilmari MannsÃ¥ker <ilmari@ilmari.org>
733              
734             =item *
735              
736             Matt Kraai <kraai@ftbfs.org>
737              
738             =item *
739              
740             Ricardo SIGNES <rjbs@cpan.org>
741              
742             =item *
743              
744             Steffen Schwigon <ss5@renormalist.net>
745              
746             =item *
747              
748             Dave Rolsky <autarch@urth.org>
749              
750             =item *
751              
752             Jesse Luehrs <doy@tozt.net>
753              
754             =item *
755              
756             Jonathan Rockway <jon@jrock.us>
757              
758             =item *
759              
760             Jonathan Scott Duff <duff@pobox.com>
761              
762             =item *
763              
764             Maik Hentsche <maik.hentsche@amd.com>
765              
766             =item *
767              
768             Sebastian Willert <willert@cpan.org>
769              
770             =back
771              
772             =cut