make test less noisy
[p5sagit/Devel-Declare.git] / lib / Devel / Declare.pm
CommitLineData
94caac6e 1package Devel::Declare;
2
3use strict;
4use warnings;
5use 5.008001;
6
003ac394 7our $VERSION = '0.001004';
94caac6e 8
0ba8c7aa 9# mirrored in Declare.xs as DD_HANDLE_*
10
11use constant DECLARE_NAME => 1;
12use constant DECLARE_PROTO => 2;
53e3ab32 13use constant DECLARE_NONE => 4;
15d0d014 14use constant DECLARE_PACKAGE => 8+1; # name implicit
0ba8c7aa 15
86c3de80 16use vars qw(%declarators %declarator_handlers @ISA);
94caac6e 17use base qw(DynaLoader);
323ae557 18use Scalar::Util 'set_prototype';
94caac6e 19
20bootstrap Devel::Declare;
21
86c3de80 22@ISA = ();
23
94caac6e 24sub import {
0ba8c7aa 25 my ($class, %args) = @_;
94caac6e 26 my $target = caller;
0ba8c7aa 27 if (@_ == 1) { # "use Devel::Declare;"
28 no strict 'refs';
15d0d014 29 foreach my $name (qw(NAME PROTO NONE PACKAGE)) {
53e3ab32 30 *{"${target}::DECLARE_${name}"} = *{"DECLARE_${name}"};
0ba8c7aa 31 }
32 } else {
33 $class->setup_for($target => \%args);
34 }
94caac6e 35}
36
37sub unimport {
38 my ($class) = @_;
39 my $target = caller;
40 $class->teardown_for($target);
41}
42
43sub setup_for {
44 my ($class, $target, $args) = @_;
45 setup();
0ba8c7aa 46 foreach my $key (keys %$args) {
47 my $info = $args->{$key};
48 my ($flags, $sub);
49 if (ref($info) eq 'ARRAY') {
50 ($flags, $sub) = @$info;
51 } elsif (ref($info) eq 'CODE') {
52 $flags = DECLARE_NAME;
53 $sub = $info;
54 } else {
55 die "Info for sub ${key} must be [ \$flags, \$sub ] or \$sub";
56 }
57 $declarators{$target}{$key} = $flags;
58 $declarator_handlers{$target}{$key} = $sub;
59 }
94caac6e 60}
61
62sub teardown_for {
63 my ($class, $target) = @_;
64 delete $declarators{$target};
0ba8c7aa 65 delete $declarator_handlers{$target};
94caac6e 66 teardown();
67}
68
94caac6e 69my $temp_name;
0ba8c7aa 70my $temp_save;
94caac6e 71
72sub init_declare {
9026391e 73 my ($usepack, $use, $inpack, $name, $proto) = @_;
53e3ab32 74 my ($name_h, $XX_h, $extra_code)
9026391e 75 = $declarator_handlers{$usepack}{$use}->(
76 $usepack, $use, $inpack, $name, $proto, defined(wantarray)
53e3ab32 77 );
15d0d014 78 ($temp_name, $temp_save) = ([], []);
0ba8c7aa 79 if ($name) {
9026391e 80 $name = "${inpack}::${name}" unless $name =~ /::/;
0ba8c7aa 81 push(@$temp_name, $name);
82 no strict 'refs';
15d0d014 83 push(@$temp_save, \&{$name});
0ba8c7aa 84 no warnings 'redefine';
85 no warnings 'prototype';
15d0d014 86 *{$name} = $name_h;
0ba8c7aa 87 }
88 if ($XX_h) {
9026391e 89 push(@$temp_name, "${inpack}::X");
0ba8c7aa 90 no strict 'refs';
9026391e 91 push(@$temp_save, \&{"${inpack}::X"});
0ba8c7aa 92 no warnings 'redefine';
93 no warnings 'prototype';
9026391e 94 *{"${inpack}::X"} = $XX_h;
0ba8c7aa 95 }
53e3ab32 96 if (defined wantarray) {
97 return $extra_code || '0;';
98 } else {
99 return;
100 }
94caac6e 101}
102
103sub done_declare {
104 no strict 'refs';
86c3de80 105 my $name = shift(@{$temp_name||[]});
0ba8c7aa 106 die "done_declare called with no temp_name stack" unless defined($name);
86c3de80 107 my $saved = shift(@$temp_save);
15d0d014 108 $name =~ s/(.*):://;
109 my $temp_pack = $1;
0ba8c7aa 110 delete ${"${temp_pack}::"}{$name};
111 if ($saved) {
112 no warnings 'prototype';
113 *{"${temp_pack}::${name}"} = $saved;
114 }
94caac6e 115}
116
323ae557 117sub build_sub_installer {
118 my ($class, $pack, $name, $proto) = @_;
119 return eval "
120 package ${pack};
121 my \$body;
122 sub ${name} (${proto}) :lvalue {\n"
003ac394 123 .' if (wantarray) {
124 my @ret = $body->(@_);
125 return @ret;
126 }
127 my $ret = $body->(@_);
86c3de80 128 return $ret;
323ae557 129 };
130 sub { ($body) = @_; };';
131}
132
133sub setup_declarators {
134 my ($class, $pack, $to_setup) = @_;
86c3de80 135 die "${class}->setup_declarators(\$pack, \\\%to_setup)"
136 unless defined($pack) && ref($to_setup) eq 'HASH';
137 my %setup_for_args;
323ae557 138 foreach my $name (keys %$to_setup) {
139 my $info = $to_setup->{$name};
140 my $flags = $info->{flags} || DECLARE_NAME;
141 my $run = $info->{run};
142 my $compile = $info->{compile};
143 my $proto = $info->{proto} || '&';
144 my $sub_proto = $proto;
145 # make all args optional to enable lvalue for DECLARE_NONE
146 $sub_proto =~ s/;//; $sub_proto = ';'.$sub_proto;
86c3de80 147 #my $installer = $class->build_sub_installer($pack, $name, $proto);
148 my $installer = $class->build_sub_installer($pack, $name, '@');
86c3de80 149 $installer->(sub :lvalue {
003ac394 150#{ no warnings 'uninitialized'; warn 'INST: '.join(', ', @_)."\n"; }
c5534496 151 if (@_) {
152 if (ref $_[0] eq 'HASH') {
153 shift;
003ac394 154 if (wantarray) {
155 my @ret = $run->(undef, undef, @_);
156 return @ret;
157 }
c5534496 158 my $r = $run->(undef, undef, @_);
159 return $r;
160 } else {
003ac394 161 return @_[1..$#_];
c5534496 162 }
86c3de80 163 }
164 return my $sv;
165 });
166 $setup_for_args{$name} = [
167 $flags,
168 sub {
169 my ($usepack, $use, $inpack, $name, $proto) = @_;
170 my $extra_code = $compile->($name, $proto);
003ac394 171 my $shift_hashref = defined(wantarray);
172 my $main_handler = sub { shift if $shift_hashref;
c5534496 173 ("DONE", $run->($name, $proto, @_));
003ac394 174 };
86c3de80 175 my ($name_h, $XX);
176 if (defined $proto) {
177 $name_h = sub :lvalue { return my $sv; };
178 $XX = $main_handler;
c5534496 179 } elsif (defined $name && length $name) {
86c3de80 180 $name_h = $main_handler;
181 }
003ac394 182 $extra_code ||= '';
183 $extra_code = '}, sub {'.$extra_code;
86c3de80 184 return ($name_h, $XX, $extra_code);
185 }
186 ];
323ae557 187 }
86c3de80 188 $class->setup_for($pack, \%setup_for_args);
189}
190
191sub install_declarator {
192 my ($class, $target_pack, $target_name, $flags, $filter, $handler) = @_;
193 $class->setup_declarators($target_pack, {
194 $target_name => {
195 flags => $flags,
196 compile => $filter,
197 run => $handler,
198 }
199 });
323ae557 200}
201
94caac6e 202=head1 NAME
203
204Devel::Declare -
205
206=head1 SYNOPSIS
207
f5f9f113 208Look at the tests. This module is currently on CPAN to ease smoke testing
209and allow early adopters who've been involved in the design to experiment
210with it.
211
94caac6e 212=head1 DESCRIPTION
213
214=head2 import
215
216 use Devel::Declare qw(list of subs);
217
218Calls Devel::Declare->setup_for(__PACKAGE__ => \@list_of_subs);
219
220=head2 unimport
221
222 no Devel::Declare;
223
224Calls Devel::Declare->teardown_for(__PACKAGE__);
225
226=head2 setup_for
227
228 Devel::Declare->setup_for($package => \@subnames);
229
230Installs declarator magic (unless already installed) and registers
231"${package}::$name" for each member of @subnames
232
233=head2 teardown_for
234
235 Devel::Declare->teardown_for($package);
236
237Deregisters all subs currently registered for $package and uninstalls
238declarator magic if number of teardown_for calls matches number of setup_for
239calls.
240
241=head1 AUTHOR
242
02f5a508 243Matt S Trout - <mst@shadowcat.co.uk>
94caac6e 244
02f5a508 245Company: http://www.shadowcat.co.uk/
94caac6e 246Blog: http://chainsawblues.vox.com/
247
248=head1 LICENSE
249
250This library is free software under the same terms as perl itself
251
252=cut
253
2541;