File Coverage

blib/lib/MooseX/Method/Signatures.pm
Criterion Covered Total %
statement 217 219 99.0
branch 53 64 82.8
condition 13 20 65.0
subroutine 38 40 95.0
pod 0 7 0.0
total 321 350 91.7


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