Skip to content

Commit

Permalink
Add option '()' to allow to define the prototype of the sub
Browse files Browse the repository at this point in the history
  • Loading branch information
dolmen committed Jan 21, 2013
1 parent f1bca42 commit fdbe706
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
1 change: 1 addition & 0 deletions Changes
@@ -1,6 +1,7 @@
Revision history for System-Sub.

{{$NEXT}}
Add option '()' to define the prototype of the sub.

0.130180 2013-01-18 DOLMEN (Olivier Mengué)
Minor fixes from BOOK (Philippe Bruhat).
Expand Down
2 changes: 2 additions & 0 deletions dist.ini
Expand Up @@ -22,6 +22,8 @@ format = %-9v %{yyyy-MM-dd}d DOLMEN (Olivier Mengué)
[GithubMeta]
remote = github

[Prereqs]
Scalar::Util = 1.11 ; For set_prototype
[AutoPrereqs]
[MinimumPerl]

Expand Down
24 changes: 23 additions & 1 deletion lib/System/Sub.pm
Expand Up @@ -6,6 +6,8 @@ use File::Which ();
use Sub::Name 'subname';
use Symbol 'gensym';
use IPC::Run qw(start finish);
use Scalar::Util 1.11 (); # set_prototype(&$) appeared in 1.11

our @CARP_NOT;

use constant DEBUG => !! $ENV{PERL_SYSTEM_SUB_DEBUG};
Expand Down Expand Up @@ -41,7 +43,10 @@ sub import
my $name = shift;
# Must be a scalar
_croak "invalid arg: SCALAR expected" unless defined ref $name && ! ref $name;
my $fq_name;
my ($fq_name, $proto);
if ($name =~ s/\(([^)]*)\)$//s) {
$proto = $1;
}
if (index($name, ':') > 0) {
$fq_name = $name;
$name = substr($fq_name, 1+rindex($fq_name, ':'));
Expand All @@ -62,6 +67,8 @@ sub import
_croak 'duplicate @ARGV' if $args;
$args = $options;
last
} elsif ($opt eq '()') {
$proto = shift @$options;
} elsif ($opt =~ /^\$?0$/) { # $0
$cmd = shift @$options;
} elsif ($opt =~ /^\@?ARGV$/) { # @ARGV
Expand Down Expand Up @@ -94,6 +101,10 @@ sub import
? _build_sub($name, [ $cmd, ($args ? @$args : ())], \%options)
: sub { _croak "'$name' not found in PATH" };

# As set_prototype *has* a prototype, we have to workaround it
# with '&'
&Scalar::Util::set_prototype($sub, $proto) if defined $proto;

no strict 'refs';
*{$fq_name} = subname $fq_name, $sub;
}
Expand Down Expand Up @@ -231,6 +242,13 @@ System::Sub - Wrap external command with a DWIM sub
}
}
# Import with a prototype (see perlsub)
use System::Sub 'hostname()'; # Empty prototype: no args allowed
use strict;
# This will fail at compile time with "Too many arguments"
hostname("xx");
=head1 DESCRIPTION
See also C<L<System::Sub::AutoLoad>> for even simpler usage.
Expand Down Expand Up @@ -291,6 +309,10 @@ The sigil (C<$>, C<@>, C<%>) is optional.
=item *
C<()>: prototype of the sub. See L<perlsub/Prototypes>.
=item *
C<$0>: the path to the executable file. It will be expanded from PATH if it
doesn't contain a directory separator.
Expand Down
32 changes: 32 additions & 0 deletions t/23-prototype.t
@@ -0,0 +1,32 @@

use strict;
use warnings;

use Test::More (-x '/bin/hostname' ? (tests => 8)
: (skip_all => 'No /bin/hostname'));

use System::Sub
hostname => [ '$0' => '/bin/hostname' ],
'hostname_proto()' => [ '$0' => '/bin/hostname' ],
'hostname_proto2' => [ '()' => '', '$0' => '/bin/hostname' ];

my $expected = `hostname`;
chomp $expected;

my $got = hostname;
is($got, $expected, 'scalar context');
is(prototype \&hostname, undef, 'prototype: undef');

$got = hostname_proto;
is($got, $expected, 'scalar context');
is(prototype \&hostname_proto, '', 'prototype: ""');

is(scalar eval 'hostname_proto(1)', undef, 'call with arg fails');
like($@, qr/Too many arguments for main::hostname_proto at /, 'error "Too many arguments"');

$got = hostname_proto2;
is($got, $expected, 'scalar context');
is(prototype \&hostname_proto2, '', 'prototype: ""');


# vim:set et sw=4 sts=4:

0 comments on commit fdbe706

Please sign in to comment.