File Coverage

blib/lib/Exporter/VA.pm
Criterion Covered Total %
statement 285 299 95.3
branch 121 158 76.5
condition 25 36 69.4
subroutine 54 55 98.1
pod 7 21 33.3
total 492 569 86.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Exporter::VA - Improved Exporter featuring Versioning and Aliasing.
4              
5             =cut
6              
7             ### see the main POD at the end of this file.
8              
9             package Exporter::VA;
10 1     1   24959 use strict;
  1         3  
  1         39  
11 1     1   5 use warnings;
  1         2  
  1         35  
12 1     1   5 use Carp qw/croak carp/;
  1         6  
  1         69  
13 1     1   888 use utf8;
  1         9  
  1         6  
14             our $VERSION= v1.3.0.1; # major.minor.update.docsonly
15             *VERBOSE= *STDERR{IO}; # can be redirected
16              
17             my %EXPORT= (
18             '&VERSION' => \&export_VERSION,
19             '&import' => \&export_import,
20             '&AUTOLOAD' => \&export_AUTOLOAD,
21             '.default_VERSION'=> v0.1,
22             ':normal' => [qw/ &VERSION &import &AUTOLOAD/ ],
23             '.&begin' => \&begin,
24             '&normalize_vstring' => \\&normalize_vstring,
25             '&autoload_symbol' => \\&autoload_symbol,
26             );
27              
28             sub Err
29             {
30             # improve this to give proper level information to Croak.
31 2     2 0 363 croak @_;
32             }
33              
34             sub Warn
35             {
36 0     0 0 0 carp @_;
37             }
38              
39             sub dump
40             {
41             # Currently implemented to use Data::Dumper, but might change to be more custom some day.
42 1     1 1 2 eval { require Data::Dumper };
  1         1070  
43 1 50       8170 if ($@) {
44 0         0 print VERBOSE "**(Exporter::VA::dump) ERROR: cannot load Data::Dumper module to support the dump() method or --dump pragma\n";
45 0         0 return;
46             }
47 1         3 my $self= shift;
48 1         11 print VERBOSE (Data::Dumper->Dump ( [ $self ], ["*EXPORT"]), $/);
49             }
50              
51             sub is_vstring($)
52             {
53 45     45 0 77 my $s= shift;
54 45         2252 my $count= $s =~ tr/\0-\1F//;
55 45         187 return $count > 0;
56             # to disambiguate a v-string like v65.66.67, add a trailing .0 becoming v.65.66.67.0 with same meaning.
57             }
58              
59             sub normalize_vstring ($)
60             {
61 40     40 1 7956 my $v= shift;
62             # for now, doesn't do much.
63 40 100       120 return v0 if length ($v) eq 0;
64 39 100       68 $v= pack ("U*", split (/\./,$v))
65             unless is_vstring ($v);
66             # remove trailing redundant zeros (but keep it at least 2 digits, so v1.0 is right, v1.0.0.0 is truncated)
67 39         117 $v =~ s/(?<=..)\0+$//;
68 39         150 return $v;
69             }
70              
71              
72             sub _calling_client()
73             {
74 24     24   32 my $n= 1;
75 24         31 for (;;) {
76 28         46 my $caller= caller($n);
77 28 100       92 return $caller if $caller ne __PACKAGE__; # I want the first caller of this module
78 4         6 ++$n;
79             }
80             }
81              
82             sub _check_allowed_versions
83             {
84 6     6   18 my ($version, $list)= @_;
85 6 100       23 return unless defined $list; # if .allowed_VERSIONS is not specified, anything is allowed.
86 2         7 foreach (@$list) {
87 3 100       13 return if $version eq $_; # normalized earlier.
88             }
89             # compose error message
90 1         4 my $vs= join( ', ', map { _format_vstring($_)} (@$list) );
  2         5  
91 1         4 Err "(Exporter::VA) you asked for ", _format_vstring($version), ", but the only allowed versions are $vs";
92             }
93              
94             sub generate_VERSION
95             {
96 4     4 0 4 my $export_def= shift; # might not have been blessed yet.
97             return sub { # this becomes the VERSION function in the exporting module.
98 12     12   808 my ($home, $version, $client)= @_;
99 12 100       41 $client= _calling_client() unless defined $client; # allow as optional argument
100 12 100       29 if (defined $version) {
101             # assure correct version / set desired version
102 6         15 $version= normalize_vstring($version);
103 6 50       24 Err "The version for this module has already been specified for module $client as ", _format_vstring ($export_def->{'..client_default_version'}{$client})
104             if exists $export_def->{'..client_default_version'}{$client};
105 6   66     23 my $max_version= $export_def->{'..max_VERSION'} || _get_VERSION ($home); # first time, called before setup.
106 6 50       20 Err "$client requested version ", _format_vstring ($version), " but module $home is only version ", _format_vstring ($max_version)
107             if $version gt $max_version;
108 6         24 _check_allowed_versions ($version, $export_def->{'.allowed_VERSIONS'});
109 5         25 $export_def->{'..client_default_version'}{$client}= $version;
110             }
111             else {
112             # fetch version
113 6 100       58 return $export_def->{'..client_default_version'}{$client} if exists $export_def->{'..client_default_version'}{$client};
114             # never explicitly specified, so use the module's actual current version.
115 1   33     5 $version= $export_def->{'..max_VERSION'} || _get_VERSION ($home);
116 1         3 $export_def->{'..client_default_version'}{$client}= $version; # once I decide, must always use the same result.
117 1         4 return $version;
118             }
119             }
120 4         30 }
121              
122             sub get_import_version
123             {
124 13     13 0 18 my ($self, $client)= @_;
125 13 100       36 unless (exists ($self->{'..client_default_version'}{$client})) {
126 8 50       22 Err "(Exporter::VA) you must specify a version to import, since the module has no default."
127             unless exists $$self{'.default_VERSION'};
128 8         30 $self->{'..client_default_version'}{$client}= $$self{'.default_VERSION'};
129 8 50       20 print VERBOSE "(Exporter::VA) import version not specified, using .default_VERSION\n"
130             if $$self{'.verbose_import'};
131             }
132 13         39 return $$self{'..client_default_version'}{$client};
133             }
134              
135             sub _format_vstring($)
136             {
137 9     9   50 return sprintf ("v%vd", shift);
138             }
139              
140              
141             sub _normalize_vstring_list
142             {
143 2     2   4 my $list= shift;
144 2         7 for (my $loop= 0; $loop < @$list; $loop+=2) {
145 5         10 normalize_vstring ($$list[$loop]);
146             }
147 2         8 bless $list, "ARRAY-seen";
148             }
149              
150             sub _match_vstring_list
151             {
152 7     7   17 my ($list, $desired_version)= @_;
153             # list is [ v1, item1, v2, item2, v3, item3, ... , vn, itemn ]
154             # match $desired_version between two v's, and return (v,item).
155 7         23 for (my $index=0; $index < scalar(@$list); $index+=2) {
156 17         35 my $ver_at_index= $$list[$index]; # >> might need to normalize it.
157 17 100       61 next unless ($ver_at_index ge $desired_version);
158             # I get here when I found or passed my spot.
159 5 50       15 return @$list[$index, $index+1] if ($ver_at_index eq $desired_version); # found it exactly
160             # otherwise I passed it.
161 5 50       12 return (undef, undef, "desired version not found") if $index == 0; # before the first, is not present.
162 5         31 return @$list[$index-2, $index-1];
163             }
164             # after the last, take the last. Should cap at Module's version, but that was checked earlier when VERSION was called.
165 2         9 return @$list[-2,-1];
166             }
167            
168             sub generate_import
169             {
170 4     4 0 7 my $export_def= shift;
171             return sub {
172 13     13   2103 my $home= shift;
173 13         36 $export_def->setup ($home); # happens first time used.
174 13         25 my $client= _calling_client();
175 13         38 my $version= $export_def->get_import_version ($client);
176 13         46 $export_def->callback ('.&begin', $client, $version, '.&begin', \@_);
177 13 100 100     242 @_ = ':DEFAULT' if (!@_ && defined $export_def->{':DEFAULT'});
178 13         37 $export_def -> export ($client, $version, \@_);
179 13         40 $export_def->callback ('.&end', $client, $version, '.&begin', \@_);
180 13         37 $export_def->_process_worklist();
181 13 100       3680 --$$export_def{'.verbose_import'} if $$export_def{'.verbose_import'};
182             }
183 4         29 }
184              
185             sub export
186             {
187 18     18 1 34 my ($self, $module, $version, $items)= @_;
188 18 50       51 $items= [$items] unless ref $items eq 'ARRAY'; # allow single item
189 18         45 while (my $item= shift @$items) {
190 40         56 my $verbose= $$self{'.verbose_import'}; # must check object each time, not cache it.
191 40 100       80 print VERBOSE "(Exporter::VA) ===processing import parameter ($item)===\n" if $verbose;
192 40 50       69 if (ref $item) {
193 0 0       0 print VERBOSE "(Exporter::VA) It's not a scalar, so invoking .&unknown_type callback. It's out of my hands.\n" if $verbose;
194 0         0 $self->callback ('.&unknown_type', $module, $version, $item, $items);
195             }
196             else {
197 40 100 100     262 if ($item =~ /^[\$\@\%\*\&]?\w+$/ or $item =~ /^-/ or $item =~s /^(<\w+)>$/$1/) { $self->export_one_symbol ($module,$version,$item,$items) }
  35 50 66     83  
198 5         15 elsif ($item =~ /^:\w+$/) { $self->export_one_tag ($module,$version,$item, $items) }
199             else {
200 0 0       0 print VERBOSE "(Exporter::VA) It's not syntactically correct, so invoking .&unknown_feature callback. It's out of my hands.\n" if $verbose;
201 0         0 $self->callback ('.&unknown_feature', $module, $version, $item, $items);
202             }
203             }
204             }
205             }
206              
207              
208             {
209             my %thing= reverse (SCALAR=>'$', ARRAY=>'@', HASH=>'%',CODE=>'&',IO=>'<',GLOB=>'*');
210             sub _resolve_by_name
211             {
212 12     12   20 my ($item, $home, $name)= @_;
213 12 100       27 $name= $item if $name eq ''; # blank string means "same".
214 12 100       55 my $sigil= ($name =~ s/^([\$\@\%\&\<\*])//) ? $1 : '&';
215 12         18 my $thing= $thing{$sigil};
216 12 50       28 Err "(Exporter::VA) Improper export definition for item $item: invalid symbol name syntax: $name" unless defined $thing;
217 1     1   2000 no strict 'refs';
  1         3  
  1         982  
218 12         12 my $globref= ${"$home\::"}{$name};
  12         43  
219 12 50       23 Err "(Exporter::VA) Symbol to export does not exist: *$home\::$name" unless defined $globref;
220 12         13 return *{$globref}{$thing};
  12         70  
221             } # _resolve_by_name
222             } # private vars scope
223              
224             sub _resolve_by_versionlist
225             {
226 6     6   32 my ($self, $module, $desired_version, $item, $param_tail)= @_;
227 6         13 my $versionlist= $$self{$item}; # I still have the $item name for callbacks and error messages
228 6         15 my ($got_version, $result)= _match_vstring_list ($versionlist, $desired_version);
229 6 100       33 print VERBOSE "(Exporter::VA) wanted $item version ", _format_vstring($desired_version), ", choose ", _format_vstring($got_version), "\n"
230             if $$self{'.verbose_import'};
231 6         22 return resolve (@_[0..4], $result);
232             }
233              
234             sub _resolve_by_hardlink
235             {
236 14     14   26 my ($item, $hardlink)= @_;
237             # This function just provides error checking. Returning wrong kind of ref can cause problems!
238 14 50       33 Err "(Exporter::VA) Improper export definition for item $item: ref to scalar must contain \\\\&code" unless ref($hardlink) eq 'CODE';
239 14         58 return $hardlink;
240             }
241            
242             sub resolve
243             {
244 45     45 1 101 my ($self, $module, $version, $item, $param_tail, $value)= @_;
245 45 100       116 $value= $$self{$item} unless defined ($value); # normally lookup, can supply ahead of time for recursive call.
246 45 100 66     133 if (!defined $value && $item =~ /^&(.+)$/) {
247             # it might be entered in the export def without the sigil.
248 8         16 my $base= $1;
249 8         18 $value= delete $$self{$base};
250 8 50       20 if (defined $value) {
251 8         21 $$self{$item}= $value;
252 8 100       30 print VERBOSE "(Exporter::VA) adding leading & to ($base) entry in export definition\n" if $$self{'.verbose_import'};
253             }
254             }
255 45 50       81 return $self->callback ('.&unknown_import', $module, $version, $item, $param_tail)
256             unless defined $value; # not listed in export def.
257 45         66 my $type= ref $value; # what is it? Lots of different ways to list it.
258 45 100       95 return _resolve_by_name ($item, $$self{'..home'}, $value) unless $type; # scalar is a name in the home package.
259 33 100       7386 return &_resolve_by_versionlist if $type eq 'ARRAY-seen';
260 28 100       53 if ($type eq 'ARRAY') {
261 1         4 _normalize_vstring_list ($value);
262 1         3 return &_resolve_by_versionlist;
263             }
264 27 100       78 return $value->(@_) if $type eq 'CODE';
265 14         27 return _resolve_by_hardlink ($item, $$value);
266 0         0 Err "(Exporter::VA) Invalid export definition for item $item";
267             }
268              
269             sub export_one_symbol # or pragma
270             {
271 35     35 0 79 my ($self, $module, $version, $item, $param_tail)= @_;
272 35 100       114 my $sigil= ($item =~ s /^([\$\@\%\*\&\-\<])//) ? $1 : '&';
273 35 50       89 Warn qq((Exporter::VA) warning: importing symbol "$sigil$item" which begins with an underscore)
274             if substr($item,0,1) eq '_';
275 35         98 my $X= $self->resolve ($module, $version, "$sigil$item", $param_tail);
276 35 100 66     486 if (defined $X && $sigil ne '-') { # skip the import if it's callback-only
277 31         63 my $worklist= $self->worklist();
278 31         59 my $name= "${module}::$item";
279 31         73 $$worklist{$name}= $X; # duplicates take last resolution with no errors.
280 31 100       208 print VERBOSE qq(Got It: *{"${module}::$item"}= $X\n) if $$self{'.verbose_import'};
281             }
282             }
283              
284             sub worklist
285             {
286 44     44 1 53 my $self= shift;
287 44         82 return $$self{'..worklist'};
288             }
289            
290             sub _process_worklist
291             {
292 13     13   17 my $self= shift;
293 13         24 my $worklist= $self->worklist();
294 1     1   7 no strict 'refs';
  1         2  
  1         747  
295 13         54 while (my ($left, $right)= each (%$worklist)) {
296 30         35 eval { *{$left}= $right; }; # this better be the right kind of thing!
  30         32  
  30         134  
297 30 50       118 if ($@) {
298 0         0 Err "(Exporter::VA) Could not process import for item '$left' = $right.";
299             }
300             }
301             }
302            
303             sub export_one_tag
304             {
305 5     5 0 15 my ($self, $module, $desired_version, $item, $param_tail)= @_;
306 5 50       13 Warn qq((Exporter::VA) warning: importing tag "$item" which begins with an underscore)
307             if substr($item,1,1) eq '_';
308             # my $home= $$self{'..home'}; # package I'm exporting =from=
309 6         11 RESTART:
310             my $list= $$self{$item};
311 6 50       14 Err "(Exporter::VA) no such export tag '$item'" unless defined $list;
312 6         9 my $type= ref $list;
313 6 50       14 if ($type eq 'ARRAY') {
314             # identify it, and change $type.
315 6 50       13 return if @$list == 0; # empty list is OK.
316 6 100       13 if (is_vstring($$list[0])) {
317 1         4 _normalize_vstring_list ($list);
318 1         2 $type= 'ARRAY-seen';
319             }
320             else {
321 5         7 $type= 'ARRAY-tags';
322 5         13 bless $list, $type;
323             }
324             }
325 6 100       20 if ($type eq 'ARRAY-seen') {
    50          
326 1         4 my ($got_version, $result)= _match_vstring_list ($list, $desired_version);
327 1 50       8 print VERBOSE "(Exporter::VA) wanted $item version ", _format_vstring($desired_version), ", choose ", _format_vstring($got_version), "\n"
328             if $$self{'.verbose_import'};
329 1         2 $item= $result;
330 1         9 goto RESTART;
331             }
332             elsif ($type eq 'ARRAY-tags') {
333 5         12 my @copy= @$list;
334 5         18 $self->export ($module, $desired_version, \@copy);
335             }
336             ## would add support for other types here, e.g. callbacks.
337 0         0 else { Err "(Exporter::VA) export tag '$item' is not a list ref" }
338             }
339            
340             sub callback
341             {
342 26     26 0 32 my $self= shift;
343 26         30 my $cb_name= shift;
344 26         40 my $func= $$self{$cb_name};
345             # they should be fully populated, putting in default behavior if it doesn't exist.
346             # this is not "try to callback..." so if not found it is an error.
347 26         61 $func-> ($self, @_);
348             }
349              
350             {
351             my %defaults= (
352             '.&unknown_type' => sub { Err "(Exporter::VA) import parameter is not a string" },
353             '.&unknown_feature' => sub { Err "(Exporter::VA) import parameter \"$_[3]\" is not syntactically correct" },
354             '.&unknown_import' => sub { Err "(Exporter::VA) import parameter \"$_[3]\" is not listed as an export" },
355             '.check_user_option' => sub { return "unknown option"},
356             '.warnings' => 1,
357             '.&begin' => sub {},
358             '.&end' => sub {},
359             '--verbose_import' => sub { ++$_[0]->{'.verbose_import'} },
360             '--dump' => sub { $_[0]->dump() }
361             );
362              
363              
364             sub _populate_defaults
365             { # helper for setup.
366             # populate callbacks and settings that were not specified
367 13     13   15 my $self= shift;
368 13         47 while (my ($key,$value)= each %defaults) {
369 117 100       406 $$self{$key}= $value unless exists $$self{$key};
370             }
371 1     1   6 no strict 'refs';
  1         2  
  1         393  
372 13 100       35 $$self{'.default_VERSION'}= normalize_vstring (${"$self->{'..home'}::VERSION"}) unless exists $$self{'.default_VERSION'};
  2         8  
373             }
374              
375             } # end scope for populate_defaults
376              
377              
378             sub _expand_plain
379             {
380 13     13   17 my $self= shift;
381 13         23 my $plainspec= delete $$self{'.plain'};
382 13 100       35 return unless defined $plainspec;
383 2         7 while (my $value = shift @$plainspec) {
384 11 50       47 $value =~ /^([\$\@\&\%:<])?(\w+)>?$/ or Err "(Exporter::VA) item '$value' in .plain list is not a legal symbol or tag name";
385 11         24 my ($sigil, $body)= ($1,$2);
386 11 100       29 $sigil= '&' unless defined $sigil;
387 11 100       24 if ($sigil eq ":") {
388             # this one is different
389 2         4 push @$plainspec, @{$$self{$value}};
  2         8  
390             # could do more error checking: make sure tag exists, and doesn't contain v-string list.
391             }
392 11         15 $value= "$sigil$body";
393 11 100       29 next if exists $$self{$value};
394 8 100 100     44 next if $sigil eq '&' && exists $$self{$body}; # present without the leading & for a function
395 5 50 33     14 next if $sigil eq '<' && exists $$self{"$body>"}; # present with trailing > for a handle
396             # not already present, so add it.
397 5         22 $$self{$value}=$value;
398             }
399             }
400              
401             sub _get_VERSION
402             {
403 15     15   21 my $home= shift;
404 1     1   5 no strict 'refs';
  1         2  
  1         203  
405 15         16 my $v= ${"${home}::VERSION"};
  15         50  
406 15 50       35 Err "(Exporter::VA) module $home does not contain a package global \$VERSION"
407             unless defined $v;
408 15         32 return normalize_vstring ($v);
409             }
410              
411             { # extra scope for variable local to function
412              
413             my %check_code= (
414             # could point to more detailed checking function, or just 1 for OK/allowed with no additional testing.
415             '.allowed_VERSIONS' => 1,
416             '.&begin' => 1,
417             '.check_user_option' => 1,
418             '.default_VERSION' => 1,
419             '.&end' => 1,
420             '.plain' => 1,
421             '.&unknown_feature' => 1,
422             '.&unknown_import' => 1,
423             '.&unknown_type' => 1,
424             '.verbose_import' => 1,
425             '.warnings' => 1
426             );
427            
428             sub _check_warning_option($$$)
429             {
430 161     161   239 my ($self, $item, $value)= @_;
431 1 50   1   6 if ($item =~ /^\.&?\p{IsUpper}/) {
  1         2  
  1         12  
  161         531  
432             # a user-defined option.
433 0         0 $self->check_user_option ($item);
434             }
435 161 100       483 return if $item =~ /^\.\./; # internal state information
436             # check for known options.
437 111         136 my $checker= $check_code{$item};
438 111 50       580 if (!defined $checker) { Warn qq{(Exporter::VA) unknown option present: "$item"} }
  0 50       0  
439 0         0 elsif (ref $checker) { $checker->($item,$value) }
440             # else it exists but doesn't have special checking code, so no messages.
441             }
442              
443             } # scope for _check_warning_option
444              
445             sub _check_warning_tag($$)
446             {
447 30     30   106 my ($item, $value)= @_;
448             }
449              
450             sub _check_warning_pragma($$)
451             {
452 29     29   107 my ($item, $value)= @_;
453             }
454              
455             sub _check_warning_identifier($$)
456             {
457 67     67   252 my ($item, $value)= @_;
458             }
459              
460             sub _check_for_warnings
461             {
462 13     13   17 my $self= shift;
463 13         47 while (my($key, $value)= each %$self) {
464 287         362 my $firstchar= substr($key,0,1);
465 287 100       569 if ($firstchar eq '.') { _check_warning_option ($self, $key, $value) }
  161 100       249  
  30 100       49  
466 29         48 elsif ($firstchar eq ':') {_check_warning_tag ($key, $value) }
467 67         101 elsif ($firstchar eq '-') {_check_warning_pragma ($key, $value) }
468             else {_check_warning_identifier ($key, $value) }
469             }
470             }
471              
472             sub setup
473             {
474 13     13 0 19 my ($self, $home)= @_;
475 13         28 my $existing_home= $$self{'..home'};
476 13 50 66     62 if (defined $existing_home && $existing_home ne $home) {
477 0         0 Err "(Exporter::VA) reuse of \%EXPORT in module $home is not allowed.";
478             }
479 13         25 $$self{'..home'}= $home;
480 13         28 $$self{'..worklist'}= {};
481 13         43 $$self{'..max_VERSION'}= _get_VERSION ($home);
482 13         37 $self->_expand_plain();
483 13         26 $self->_populate_defaults();
484 13 100       35 if (exists $$self{'.allowed_VERSIONS'}) {
485 3         4 $_= normalize_vstring($_) foreach (@{$$self{'.allowed_VERSIONS'}});
  3         11  
486             }
487 13 50       50 $self->_check_for_warnings() if $$self{'.warnings'};
488             }
489              
490             {
491             my $client_export_def;
492              
493             sub begin
494             {
495 4     4 0 11 my ($blessed_export_def, $caller, $version, $symbol, $param_list_tail)= @_;
496 4         10 $client_export_def= find_export_def ($caller, $param_list_tail);
497             }
498            
499             sub find_export_def
500             {
501 4     4 1 7 my ($caller, $params)= @_;
502             # first, try to locate hash ref in parameter list.
503 4         13 foreach my $index (0..scalar(@$params)-1) {
504 9         16 my $val= $$params[$index];
505 9 100       25 if (ref ($val) eq 'HASH') {
506 2         7 splice @$params, $index, 1; # remove it
507 2 100 66     18 splice @$params, $index-1, 1
508             if $index>0 && $$params[$index-1] eq '-def'; # remove optional explicit switch
509 2         9 return $val; # return it
510             }
511             }
512             # look for package variable in caller.
513 1     1   27687 no strict 'refs';
  1         3  
  1         341  
514 2         3 return \%{"$caller\::EXPORT"};
  2         16  
515             }
516              
517             sub export_import
518             # called to export a custom import function to *my* client, when Export::VA is used.
519             {
520 3     3 0 7 my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_;
521 3         8 $client_export_def= bless $client_export_def, "Exporter::VA";
522 3         8 return generate_import ($client_export_def);
523             }
524              
525              
526             sub export_VERSION
527             # called to export a custom VERSION function to *my* client, when Export::VA is used.
528             {
529             # my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_;
530             # the above line documents the parameters, but I don't need any of them so it's commented out.
531 3     3 0 9 return generate_VERSION ($client_export_def);
532             }
533              
534              
535             sub export_AUTOLOAD
536             # called to export a custom AUTOLOAD function to *my* client, when Export::VA is used.
537             {
538             # my ($VA_export_def, $caller, $version, $symbol, $param_list_tail)= @_;
539             # the above line documents the parameters, but I don't need any of them so it's commented out.
540 1     1 0 5 return _generate_AUTOLOAD ($client_export_def);
541             }
542              
543            
544             } # end scope around $client_export_def
545              
546             sub autoload_symbol
547             {
548 3     3 1 47 my ($self, $symbol, @extra)= @_;
549 3         4 my %memory;
550 3         7 my $home= $self->{'..home'};
551             my $thunk= sub {
552 4     4   209 my $retval= eval {
553 4         10 my $caller= _calling_client(); # so I don't have to figure it out multiple times
554 4         7 my $f= $memory{$caller};
555 4 50       14 unless (defined $f) {
556 4         16 $f= $memory{$caller}= $self->resolve ($caller, $home->VERSION(undef,$caller), '&'.$symbol, [@extra]);
557             }
558 4         42 goto &$f;
559             };
560 4 50       14 if ($@) {
561 4         572 carp "(Exporter::VA) Cannot redirect to versioned function ($@)";
562             }
563 4         36 return $retval;
564 3         20 };
565 1     1   6 no strict 'refs';
  1         2  
  1         202  
566 3         4 *{"${home}::$symbol"}= $thunk;
  3         17  
567             }
568              
569             sub _generate_AUTOLOAD
570             {
571 1     1   39 my $client_export_def= shift;
572             return sub { # the generated AUTOLOAD
573 3     3   184 my $AUTOLOAD= $Exporter::VA::AUTOLOAD; # save the global in case of recursion.
574 3         5 my $func= $AUTOLOAD;
575 3         13 $func =~ s/.*:://; # not checking the actual module name. Might be inherited or re-routed or something. I shouldn't care, right?
576 3 100 66     29 Err "(Exporter::VA) Generated $client_export_def->{'..home'}::AUTOLOAD can't find export definition for $func."
577             unless exists $client_export_def->{$func} || exists $client_export_def->{'&' . $func};
578 2         6 $client_export_def->autoload_symbol ($func);
579 2         7 goto &$AUTOLOAD; # try it again.
580             }
581 1         8 }
582              
583             ## main code.
584             {
585              
586             my $export_def= bless (\%EXPORT, __PACKAGE__);
587 1     1   6 use vars qw/*import *VERSION/; # silence a warning when syntax checking the .pm by itself
  1         3  
  1         122  
588             *import= generate_import ($export_def);
589             *VERSION= generate_VERSION ($export_def);
590             }
591              
592              
593             1;
594             __END__