correct typo in ifndef
[p5sagit/Devel-Declare.git] / lib / Devel / Declare.pm
CommitLineData
94caac6e 1package Devel::Declare;
2
3use strict;
4use warnings;
5use 5.008001;
6
78160085 7our $VERSION = '0.001003';
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"
86c3de80 123 .'my $ret = $body->(@_);
124 return $ret;
323ae557 125 };
126 sub { ($body) = @_; };';
127}
128
129sub setup_declarators {
130 my ($class, $pack, $to_setup) = @_;
86c3de80 131 die "${class}->setup_declarators(\$pack, \\\%to_setup)"
132 unless defined($pack) && ref($to_setup) eq 'HASH';
133 my %setup_for_args;
323ae557 134 foreach my $name (keys %$to_setup) {
135 my $info = $to_setup->{$name};
136 my $flags = $info->{flags} || DECLARE_NAME;
137 my $run = $info->{run};
138 my $compile = $info->{compile};
139 my $proto = $info->{proto} || '&';
140 my $sub_proto = $proto;
141 # make all args optional to enable lvalue for DECLARE_NONE
142 $sub_proto =~ s/;//; $sub_proto = ';'.$sub_proto;
86c3de80 143 #my $installer = $class->build_sub_installer($pack, $name, $proto);
144 my $installer = $class->build_sub_installer($pack, $name, '@');
145 my $proto_maker = eval q!
146 sub {
147 my $body = shift;
148 sub (!.$sub_proto.q!) {
149 $body->(@_);
150 };
151 };
152 !;
153 $installer->(sub :lvalue {
c5534496 154 if (@_) {
155 if (ref $_[0] eq 'HASH') {
156 shift;
157 my $r = $run->(undef, undef, @_);
158 return $r;
159 } else {
160 return $_[1];
161 }
86c3de80 162 }
163 return my $sv;
164 });
165 $setup_for_args{$name} = [
166 $flags,
167 sub {
168 my ($usepack, $use, $inpack, $name, $proto) = @_;
169 my $extra_code = $compile->($name, $proto);
170 my $main_handler = $proto_maker->(sub {
c5534496 171 ("DONE", $run->($name, $proto, @_));
86c3de80 172 });
173 my ($name_h, $XX);
174 if (defined $proto) {
175 $name_h = sub :lvalue { return my $sv; };
176 $XX = $main_handler;
c5534496 177 } elsif (defined $name && length $name) {
86c3de80 178 $name_h = $main_handler;
c5534496 179 } else {
180 $extra_code ||= '';
181 $extra_code = '}, sub {'.$extra_code;
86c3de80 182 }
183 return ($name_h, $XX, $extra_code);
184 }
185 ];
323ae557 186 }
86c3de80 187 $class->setup_for($pack, \%setup_for_args);
188}
189
190sub install_declarator {
191 my ($class, $target_pack, $target_name, $flags, $filter, $handler) = @_;
192 $class->setup_declarators($target_pack, {
193 $target_name => {
194 flags => $flags,
195 compile => $filter,
196 run => $handler,
197 }
198 });
323ae557 199}
200
94caac6e 201=head1 NAME
202
203Devel::Declare -
204
205=head1 SYNOPSIS
206
f5f9f113 207Look at the tests. This module is currently on CPAN to ease smoke testing
208and allow early adopters who've been involved in the design to experiment
209with it.
210
94caac6e 211=head1 DESCRIPTION
212
213=head2 import
214
215 use Devel::Declare qw(list of subs);
216
217Calls Devel::Declare->setup_for(__PACKAGE__ => \@list_of_subs);
218
219=head2 unimport
220
221 no Devel::Declare;
222
223Calls Devel::Declare->teardown_for(__PACKAGE__);
224
225=head2 setup_for
226
227 Devel::Declare->setup_for($package => \@subnames);
228
229Installs declarator magic (unless already installed) and registers
230"${package}::$name" for each member of @subnames
231
232=head2 teardown_for
233
234 Devel::Declare->teardown_for($package);
235
236Deregisters all subs currently registered for $package and uninstalls
237declarator magic if number of teardown_for calls matches number of setup_for
238calls.
239
240=head1 AUTHOR
241
02f5a508 242Matt S Trout - <mst@shadowcat.co.uk>
94caac6e 243
02f5a508 244Company: http://www.shadowcat.co.uk/
94caac6e 245Blog: http://chainsawblues.vox.com/
246
247=head1 LICENSE
248
249This library is free software under the same terms as perl itself
250
251=cut
252
2531;