document info(), add args_min/args_max
Lukas Mai [Sun, 4 Nov 2012 01:32:50 +0000 (02:32 +0100)]
lib/Function/Parameters.pm
lib/Function/Parameters/Info.pm
t/info.t

index 5734c7d..c67e6ec 100644 (file)
@@ -613,6 +613,16 @@ C<< use Function::Parameters { fun => 'function', method => 'method' } >>.
 C<use Function::Parameters qw(:strict)> is equivalent to
 C<< use Function::Parameters { fun => 'function_strict', method => 'method_strict' } >>.
 
+=head2 Introspection
+
+You can ask a function at runtime what parameters it has. This functionality is
+available through the function C<Function::Parameters::info> (which is not
+exported, so you have to call it by its full name). It takes a reference to a
+function, and returns either C<undef> (if it knows nothing about the function)
+or a L<Function::Parameters::Info> object describing the parameter list.
+
+See L<Function::Parameters::Info> for examples.
+
 =head2 Wrapping C<Function::Parameters>
 
 If you want to write a wrapper around C<Function::Parameters>, you only have to
@@ -643,6 +653,10 @@ generated code corresponds to:
   # ... turns into ...
   sub bar :method { my $self = shift; my ($x, $y, @z) = @_; sub bar; ... }
 
+=head1 SEE ALSO
+
+L<Function::Parameters::Info>
+
 =head1 AUTHOR
 
 Lukas Mai, C<< <l.mai at web.de> >>
index 1519629..8604eeb 100644 (file)
@@ -20,6 +20,125 @@ for my $gen (join "\n", map "sub $_ { \@{\$_[0]->_$_} }", @pn_ro) {
        eval "$gen\n1" or die $@;
 }
 
+sub args_min {
+       my $self = shift;
+       my $r = 0;
+       $r++ if defined $self->invocant;
+       $r += $self->positional_required;
+       $r += $self->named_required * 2;
+       $r
+}
+
+sub args_max {
+       my $self = shift;
+       return 0 + 'Inf' if defined $self->slurpy || $self->named_required || $self->named_optional;
+       my $r = 0;
+       $r++ if defined $self->invocant;
+       $r += $self->positional_required;
+       $r += $self->positional_optional;
+       $r
+}
+
 'ok'
 
 __END__
+
+=encoding UTF-8
+
+=head1 NAME
+
+Function::Parameters::Info - Information about parameter lists
+
+=head1 SYNOPSIS
+
+  use Function::Parameters;
+  
+  fun foo($x, $y, :$hello, :$world = undef) {}
+  
+  my $info = Function::Parameters::info \&foo;
+  my $p0 = $info->invocant;             # undef
+  my @p1 = $info->positional_required;  # ('$x', '$y')
+  my @p2 = $info->positional_optional;  # ()
+  my @p3 = $info->named_required;       # ('$hello')
+  my @p4 = $info->named_optional;       # ('$world')
+  my $p5 = $info->slurpy;               # undef
+  my $min = $info->args_min;  # 4
+  my $max = $info->args_max;  # inf
+  
+  my $invocant = Function::Parameters::info(method () { 42 })->invocant;  # '$self'
+  
+  my $slurpy = Function::Parameters::info(fun {})->slurpy;  # '@_'
+
+=head1 DESCRIPTION
+
+L<C<Function::Parameters::info>|Function::Parameters/Introspection> returns
+objects of this class to describe parameter lists of functions. The following
+methods are available:
+
+=head2 C<< $info->invocant >>
+
+Returns the name of the variable into which the first argument is
+L<C<shift>|perlfunc/shift>ed into automatically, or C<undef> if no such thing
+exists. This will usually return C<'$self'> for methods.
+
+=head2 C<< $info->positional_required >>
+
+Returns a list of the names of the required positional parameters (or a count
+in scalar context).
+
+=head2 C<< $info->positional_optional >>
+
+Returns a list of the names of the optional positional parameters (or a count
+in scalar context).
+
+=head2 C<< $info->named_required >>
+
+Returns a list of the names of the required named parameters (or a count
+in scalar context).
+
+=head2 C<< $info->named_optional >>
+
+Returns a list of the names of the optional named parameters (or a count
+in scalar context).
+
+=head2 C<< $info->slurpy >>
+
+Returns the name of the final array or hash that gobbles up all remaining
+arguments, or C<undef> if no such thing exists.
+
+As a special case, functions defined without an explicit parameter list (i.e.
+without C<( )>) will return C<'@_'> here because they accept any number of
+arguments.
+
+=head2 C<< $info->args_min >>
+
+Returns the minimum number of arguments this function requires. This is
+computed as follows: Invocant and required positional parameters count 1 each.
+Optional parameters don't count. Required named parameters count 2 each (key +
+value). Slurpy parameters don't count either because they accept empty lists.
+
+=head2 C<< $info->args_max >>
+
+Returns the maximum number of arguments this function accepts. This is computed
+as follows: If there is any named or slurpy parameter, the result is C<Inf>.
+Otherwise the result is the sum of all invocant and positional parameters.
+
+=head1 SEE ALSO
+
+L<Function::Parameters>
+
+=head1 AUTHOR
+
+Lukas Mai, C<< <l.mai at web.de> >>
+
+=head1 COPYRIGHT & LICENSE
+
+Copyright 2012 Lukas Mai.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of either: the GNU General Public License as published
+by the Free Software Foundation; or the Artistic License.
+
+See http://dev.perl.org/licenses/ for more information.
+
+=cut
index c670cef..683fa15 100644 (file)
--- a/t/info.t
+++ b/t/info.t
@@ -2,10 +2,12 @@
 use warnings FATAL => 'all';
 use strict;
 
-use Test::More tests => 104;
+use Test::More tests => 122;
 
 use Function::Parameters;
 
+sub Inf () { 0 + 'Inf' }
+
 fun foo($pr1, $pr2, $po1 = 1, $po2 = 2, :$no1 = 3, :$no2 = 4, %r) {}
 
 {
@@ -21,6 +23,8 @@ fun foo($pr1, $pr2, $po1 = 1, $po2 = 2, :$no1 = 3, :$no2 = 4, %r) {}
        is_deeply [$info->named_optional], [qw($no1 $no2)];
        is scalar $info->named_optional, 2;
        is $info->slurpy, '%r';
+       is $info->args_min, 2;
+       is $info->args_max, Inf;
 }
 
 {
@@ -36,6 +40,8 @@ fun foo($pr1, $pr2, $po1 = 1, $po2 = 2, :$no1 = 3, :$no2 = 4, %r) {}
        is_deeply [$info->named_optional], [];
        is scalar $info->named_optional, 0;
        is $info->slurpy, undef;
+       is $info->args_min, 5;
+       is $info->args_max, Inf;
 }
 
 sub bar {}
@@ -59,6 +65,8 @@ method baz($class: $po1 = 1, $po2 = 2, $po3 = 3, :$no1 = 4, @rem) {}
        is_deeply [$info->named_optional], [qw($no1)];
        is scalar $info->named_optional, 1;
        is $info->slurpy, '@rem';
+       is $info->args_min, 1;
+       is $info->args_max, Inf;
 }
 
 {
@@ -74,6 +82,8 @@ method baz($class: $po1 = 1, $po2 = 2, $po3 = 3, :$no1 = 4, @rem) {}
        is_deeply [$info->named_optional], [];
        is scalar $info->named_optional, 0;
        is $info->slurpy, undef;
+       is $info->args_min, 1;
+       is $info->args_max, 1;
 }
 
 {
@@ -90,6 +100,8 @@ method baz($class: $po1 = 1, $po2 = 2, $po3 = 3, :$no1 = 4, @rem) {}
        is_deeply [$info->named_optional], [];
        is scalar $info->named_optional, 0;
        is $info->slurpy, '@_';
+       is $info->args_min, 0;
+       is $info->args_max, Inf;
 }
 
 {
@@ -105,6 +117,8 @@ method baz($class: $po1 = 1, $po2 = 2, $po3 = 3, :$no1 = 4, @rem) {}
        is_deeply [$info->named_optional], [];
        is scalar $info->named_optional, 0;
        is $info->slurpy, '@_';
+       is $info->args_min, 1;
+       is $info->args_max, Inf;
 }
 
 {
@@ -126,6 +140,8 @@ method baz($class: $po1 = 1, $po2 = 2, $po3 = 3, :$no1 = 4, @rem) {}
                is_deeply [$info->named_optional], [];
                is scalar $info->named_optional, 0;
                is $info->slurpy, undef;
+               is $info->args_min, 6;
+               is $info->args_max, Inf;
                is $f->(), $i;
        }
 }