File Coverage

lib/Badger/Exporter.pm
Criterion Covered Total %
statement 228 239 95.4
branch 120 150 80.0
condition 36 59 61.0
subroutine 28 31 90.3
pod 12 13 92.3
total 424 492 86.1


line stmt bran cond sub pod time code
1             #========================================================================
2             #
3             # Badger::Exporter
4             #
5             # DESCRIPTION
6             # This module is an OO version of the Exporter module. It
7             # does the same kind of thing but with an OO interface that means
8             # you don't have to go messing around with package variables. It
9             # correctly handles inheritance, exporting not only those symbols
10             # defined by a subclass, but also those of its base classes.
11             #
12             # AUTHOR
13             # Andy Wardley
14             #
15             #========================================================================
16              
17             package Badger::Exporter;
18              
19 70     70   2144 use Carp;
  70         114  
  70         3140  
20 70     70   314 use strict;
  70         99  
  70         1210  
21 70     70   249 use warnings;
  70         95  
  70         8323  
22             use constant {
23 70         73189 ALL => 'all', # Alas, we can't pull these in from
24             NONE => 'none', # Badger::Constants because it's a
25             DEFAULT => 'default', # subclass of Badger::Exporter which
26             IMPORT => 'import', # gives us a chicken-and-egg dependency
27             IMPORTS => 'imports', # problem.We could pull them into
28             HOOKS => 'hooks', # Badger::Constants though because
29             ARRAY => 'ARRAY', # that's a subclass... hmmm....
30             HASH => 'HASH',
31             CODE => 'CODE',
32             EXPORT_ALL => 'EXPORT_ALL',
33             EXPORT_ANY => 'EXPORT_ANY',
34             EXPORT_TAGS => 'EXPORT_TAGS',
35             EXPORT_FAIL => 'EXPORT_FAIL',
36             EXPORT_HOOKS => 'EXPORT_HOOKS',
37             EXPORT_BEFORE => 'EXPORT_BEFORE',
38             EXPORT_AFTER => 'EXPORT_AFTER',
39             EXPORTABLES => 'EXPORTABLES',
40             ISA => 'ISA',
41             REFS => 'refs',
42             ONCE => 'once',
43             PKG => '::',
44             DELIMITER => qr/(?:,\s*)|\s+/, # match a comma or whitespace
45             MISSING => "Missing value for the '%s' option%s",
46             BAD_HANDLER => "Invalid export %s handler specified: %s",
47             BAD_HOOK => "Invalid export hook handler specified for the '%s' option: %s",
48             WANTED => " (%s wanted, %s specified)",
49             UNDEFINED => " (argument %s of %s is undefined)",
50 70     70   456 };
  70         125  
51              
52             our $VERSION = 0.01;
53             our $DEBUG = 0 unless defined $DEBUG;
54             our $HANDLERS = {
55             all => \&export_all,
56             any => \&export_any,
57             tags => \&export_tags,
58             hooks => \&export_hooks,
59             fail => \&export_fail,
60             before => \&export_before,
61             after => \&export_after,
62             };
63              
64              
65             #-----------------------------------------------------------------------
66             # export declaration methods:
67             # exports( all => [...], any => [...], ...etc... )
68             # export_all('foo bar baz')
69             # export_any('foo bar baz')
70             # export_before( sub { ... } )
71             # export_after( sub { ... } )
72             # export_tags( set1 => 'foo bar baz', set2 => 'wam bam' )
73             # export_hooks( foo => sub { ... }, bar => sub { ... } )
74             # export_fail( sub { ... } )
75             #-----------------------------------------------------------------------
76              
77             sub exports {
78 696     696 1 1124 my $self = shift;
79 696 100 66     2823 my $data = @_ == 1 && ref $_[0] eq HASH ? shift : { @_ };
80 696         814 my $handler;
81              
82             # delegate each key in $data to a handler in $HANDLERS
83 696         2548 while (my ($key, $value) = each %$data) {
84 1188   33     2743 $handler = $HANDLERS->{ $key }
85             || croak "Invalid exports key: $key\n";
86 1188         2212 $handler->($self, $value);
87             }
88             }
89              
90             sub export_all {
91 75     75 1 2670 my $self = shift;
92 75 100       396 my $args = @_ == 1 ? shift : [ @_ ];
93 75         290 my $list = $self->export_variable( EXPORT_ALL => [ ] );
94 75 100       1504 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
95             }
96              
97             sub export_any {
98 795     795 1 2228 my $self = shift;
99 795 100       2848 my $args = @_ == 1 ? shift : [ @_ ];
100 795         4642 my $list = $self->export_variable( EXPORT_ANY => [ ] );
101 795 100       6180 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
102             }
103              
104             sub export_before {
105 26     26 1 72 my $self = shift;
106 26 50       97 my $args = @_ == 1 ? shift : [ @_ ];
107 26         78 my $list = $self->export_variable( EXPORT_BEFORE => [ ] );
108 26 50       130 push( @$list, ref $args eq ARRAY ? @$args : $args );
109             }
110              
111             sub export_after {
112 72     72 1 185 my $self = shift;
113 72 50       371 my $args = @_ == 1 ? shift : [ @_ ];
114 72         379 my $list = $self->export_variable( EXPORT_AFTER => [ ] );
115 72 50       491 push( @$list, ref $args eq ARRAY ? @$args : $args );
116             }
117              
118             sub export_tags {
119 287     287 1 1634 my $self = shift;
120 287 100 66     2590 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
121 287         911 my $tags = $self->export_variable( EXPORT_TAGS => { } );
122              
123             # Add new tags into $EXPORT_TAGS hash ref
124             @$tags{ keys %$args } = map {
125             # Tags can be defined as hash arrays containing (key => '=value')
126             # declarataions. We upgrade each '=value' to a constant subroutine.
127 287 100 100     808 if (ref && ref eq HASH) {
  500         3929  
128 4         28 while (my ($key, $value) = each %$_) {
129 13 100       40 if ($value =~ s/^=//) {
130 4 50       10 _debug("export_tags() constructing constant: $key => $value\n") if $DEBUG;
131 4     0   31 $_->{ $key } = sub() { $value };
  0         0  
132             }
133             }
134             }
135 500         1329 $_;
136             }
137             values %$args;
138              
139             # all symbols referenced in tagsets (except other tag sets) must be
140             # flagged as exportable
141             $self->export_any(
142             grep {
143             # ignore references to code or other tag sets
144 2702   66     8883 not (ref || /^(:|=)/);
145             }
146             map {
147             # symbols in tagset can be a list ref, hash ref or string
148 287 100       637 ref $_ eq ARRAY ? @$_ :
  500 100       3910  
149             ref $_ eq HASH ? %$_ :
150             split DELIMITER
151             }
152             values %$args
153             );
154              
155 287         977 return $tags;
156             }
157              
158             sub export_hooks {
159 506     506 1 1270 my $self = shift;
160 506 100 66     1919 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
161 506         1965 my $hooks = $self->export_variable( EXPORT_HOOKS => { } );
162 506         2453 @$hooks{ keys %$args } = values %$args;
163 506         1768 return $hooks;
164             }
165              
166             sub export_fail {
167 166     166 1 1270 my $self = shift;
168 166   33     629 my $class = ref $self || $self;
169 70     70   528 no strict REFS;
  70         116  
  70         13813  
170              
171             # get/set $EXPORT_FAIL
172             return @_
173 166         853 ? (${$class.PKG.EXPORT_FAIL} = shift)
174 166 50       568 : ${$class.PKG.EXPORT_FAIL};
  0         0  
175             }
176              
177              
178             #------------------------------------------------------------------------
179             # import/export methods:
180             # import(@imports)
181             # export($target, @exports)
182             #------------------------------------------------------------------------
183              
184             sub import {
185 3423     3423   23547 my $class = shift;
186 3423         8454 my $target = (caller())[0];
187              
188             # enable strict and warnings in the caller - this ensures that every
189             # Badger module (that calls this method - which is pretty much all of
190             # them) has strict/warnings enabled, without having to explicitly write
191             # it. Thx Moose!
192 3423         19178 strict->import;
193 3423         30811 warnings->import;
194              
195             # call in the heavy guns
196 3423         12079 $class->export($target, @_);
197             }
198              
199             sub export {
200 6648     6648 1 8232 my $class = shift;
201 6648         9381 my $target = shift;
202 6648 100       16932 my $imports = @_ == 1 ? shift : [ @_ ];
203 6648         14096 my ($all, $any, $tags, $hooks, $fails, $before, $after)
204             = $class->exportables;
205 6648 100       11141 my $can_hook = (%$hooks ? 1 : 0);
206 6648         6234 my $added_all = 0;
207 6648         5910 my $count = 0;
208 6648         7316 my ($symbol, $symbols, $source, $hook, $pkg, $nargs,
209             %done, @args, @errors);
210              
211 70     70   467 no strict REFS;
  70         112  
  70         2582  
212 70     70   360 no warnings ONCE;
  70         124  
  70         57563  
213              
214             # imports can be a single whitespace delimited string of symbols
215 6648 100       28701 $imports = [ split(DELIMITER, $imports) ]
216             unless ref $imports eq ARRAY;
217              
218             # default to export_all if list of exports not specified
219             # TODO: what about: use Badger::Example qw(); ? perhaps we should
220             # return unless @_ up above?
221 6648 100       11432 @$imports = @$all unless @$imports;
222              
223 6648         10095 foreach $hook (@$before) {
224 28         106 $hook->($class, $target, $imports);
225             }
226              
227 6648         10076 SYMBOL: while (@$imports) {
228 33810 50       48713 next unless ($symbol = shift @$imports);
229 33810 100       61948 next if $done{ $symbol }++;
230              
231             # look for :tagset symbols and expand their contents onto @$imports
232 33809 100       50357 if ($symbol =~ s/^://) {
233 455 100       1254 if ($symbols = $tags->{ $symbol }) {
    100          
    50          
234 445 100       1091 if (ref $symbols eq ARRAY) {
    100          
235             # expand list of symbols onto @$imports list
236 8         16 unshift(@$imports, @$symbols);
237             }
238             elsif (ref $symbols eq HASH) {
239             # map hash into [name => $symbol] pairs
240 4         9 unshift(@$imports, map { [$_ => $symbols->{ $_ }] } keys %$symbols);
  13         23  
241             }
242             else {
243             # string of space-delimited symbols
244 433         5902 unshift(@$imports, split(DELIMITER, $symbols));
245             }
246             }
247             elsif ($symbol eq DEFAULT) {
248 1         3 unshift(@$imports, @$all);
249             }
250             elsif ($symbol eq ALL) {
251 9         100 unshift(@$imports, keys %$any);
252 9         25 $added_all = 1;
253             }
254             else {
255 0         0 push(@errors, "Invalid import tag: $symbol\n");
256             }
257 455         1058 next SYMBOL;
258             }
259              
260 33354 100 100     98024 if (ref $symbol eq ARRAY) {
    100          
    50          
    100          
    100          
261             # a pair of [name, $symbol] expanded from a :tag hash set
262 13         16 ($symbol, $source) = @$symbol;
263             # _debug("expanded export pair: $symbol => $source\n") if $DEBUG;
264             }
265             elsif ($can_hook && ($hook = $hooks->{ $symbol })) {
266             # a hook can be specified as [$code,$nargs] in which case we
267             # generate a closure around the $code which shifts $nargs off
268             # the symbols list and passes them as arguments to $code
269 14062 100       22717 $hook = $hooks->{ $symbol } = $class->export_hook_generator($symbol, $hook)
270             unless ref $hook eq CODE;
271              
272             # fire off handler hooked to this import item
273 14062         26608 &$hook($class, $target, $symbol, $imports);
274              
275             # hooks can be repeated so pretend we haven't done it
276 14059         19900 $done{ $symbol }--;
277 14059         17085 next SYMBOL;
278             }
279             elsif ($symbol eq IMPORTS) {
280             # special 'imports' hook disables any more hooks causing
281             # all remaining arguments to be imported as symbols
282 0         0 $can_hook = 0;
283 0         0 next SYMBOL;
284             }
285             elsif ($symbol eq IMPORT) {
286             # 'import' hook accepts the next item as an import list/string
287             # and unpacks it onto the front of the imports list. We disable
288             # hooks for the duration of the import and insert a dummy HOOKS
289             # symbol at the end to re-enable hooks
290 1191         1541 $can_hook = 0;
291 1191 50       1992 if ($symbols = shift @$imports) {
292 1191 50       6398 $symbols = [ split(DELIMITER, $symbols) ]
293             unless ref $symbols eq ARRAY;
294 1191         3009 unshift(@$imports, @$symbols, HOOKS);
295             }
296             else {
297 0         0 push(@errors, "Missing argument for $symbol hook\n");
298             }
299 1191         1572 next SYMBOL;
300             }
301             elsif ($symbol eq HOOKS) {
302             # special 'hooks' item turns hooks back on
303 1191         1377 $can_hook = 1;
304 1191         1406 next SYMBOL;
305             }
306             else {
307             # otherwise the symbol exported is the one requested
308 16897         18347 $source = $symbol;
309             }
310              
311             # check we're allowed to export the symbol requested
312 16910 100       25686 if ($pkg = $any->{ $symbol }) {
313             # _debug("exporting $symbol from $pkg to $target\n") if $DEBUG;
314             }
315             else {
316 197         416 foreach $hook (@$fails) {
317 198 100       530 if (&$hook($class, $target, $symbol, $imports)) {
318             # hooks can be repeated so pretend we haven't done it
319 195         353 $done{ $symbol }--;
320 195         380 next SYMBOL;
321             }
322             }
323 2         6 push(@errors, "$symbol is not exported by $class\n");
324 2         4 next SYMBOL;
325             }
326              
327 16713 100       17648 if (ref $source eq CODE) {
328             # patch directly into the code ref
329             # _debug("exporting $symbol from code reference\n") if $DEBUG;
330 8         8 *{ $target.PKG.$symbol } = $source;
  8         21  
331             }
332             else {
333 16705         14977 my $type = "&";
334 16705         22730 $symbol =~ s/^(\W)//;
335 16705 100       23105 $source =~ s/^(\W)// and $type = $1;
336             # NOTE: '=value' should *probably* never be found at this point
337             # because we're now upgrading them to constant subroutines in
338             # the import_tags() method. However, I'm leaving this in here
339             # until I've had a chance to properly review the code and convince
340             # myself that this assumption is correct.
341 16705 50 33     21525 _debug("export() constructing constant: $symbol => $source\n")
342             if $DEBUG && $type eq '=';
343 16705 100 66     51717 $source = $pkg.PKG.$source unless $source =~ /::/ or $type eq '=';
344 16705 50       21103 _debug("exporting $type$symbol from $source into $target\n") if $DEBUG;
345 16705         59561 *{ $target.PKG.$symbol } =
346 16646         35712 $type eq '&' ? \&{$source} :
347 35         62 $type eq '$' ? \${$source} :
348 14         24 $type eq '@' ? \@{$source} :
349 10         15 $type eq '%' ? \%{$source} :
350 0         0 $type eq '*' ? *{$source} :
351 0     0   0 $type eq '=' ? sub(){$source} :
352 16705 0       18951 do { push(@errors, "Can't export symbol: $type$symbol\n"); next; };
  0 0       0  
  0 50       0  
    100          
    100          
    100          
353             }
354 16713         20519 $count++;
355             }
356             continue {
357             # if we're on the last item and we've only processed hooks
358             # (i.e. no real symbols were specified then we export the
359             # default set of symbols instead
360 33807 100 100     72709 unless (@$imports or $count or $added_all) {
      66        
361 632         1171 unshift(@$imports, @$all);
362 632         1224 $added_all = 1;
363             }
364             }
365              
366 6645 100       10026 if (@errors) {
367 1         4 require Carp;
368 1         172 Carp::croak("@{errors}Can't continue after import errors");
369             }
370              
371 6644         9007 foreach $hook (@$after) {
372 102         345 $hook->($class, $target);
373             }
374              
375 6643         1352451 return 1;
376             }
377              
378             sub exportables {
379 6648     6648 1 7372 my $class = shift;
380 70     70   566 no strict REFS;
  70         145  
  70         2727  
381 70     70   1661 no warnings ONCE;
  70         111  
  70         6739  
382              
383 6648   66     6288 my $cache = ${ $class.PKG.EXPORTABLES } ||= do {
  6648         24145  
384 1101         1874 my ($pkg, $symbols, %done, @all, %any, %tags, %hooks, @fails, @before, @after);
385 1101         2135 my @pending = ($class);
386 70     70   1822 no strict REFS;
  70         111  
  70         27445  
387              
388             # walk up inheritance tree collecting values from the @$EXPORT_ALL,
389             # @$EXPORT_ANY, %$EXPORT_TAGS, %$EXPORT_HOOKS and $EXPORT_FAIL pkg
390             # variables, then cache them in $EXPORT_CACHE for subsequent use
391              
392 1101         2429 while ($pkg = shift @pending) {
393 3609 100       7944 next if $done{ $pkg }++;
394              
395             # TODO: we could optimise here by looking for a previously
396             # computed EXPORTABLES in the base class and merging it in...
397              
398             # $EXPORT_ANY package vars are list references containing symbols,
399             # which we use to populate the %any hash which maps symbols to
400             # their source packages. e.g. { foo => 'My::Package' }
401             # The presence of an entry in this table indicates that the symbol
402             # key can be exported. The corresponding value indicates the
403             # package that it must be exported from. We don't replace any
404             # existing entries in the %any hash because we're working from
405             # sub-class upwards to super-class, . This ensures that the
406             # entries put in first by more specialised sub-classes are used
407             # in preference to those defined by more general super-classes.
408 3532 100       3255 if ($symbols = ${ $pkg.PKG.EXPORT_ANY }) {
  3532         11373  
409 825 50       3404 $symbols = [ split(DELIMITER, $symbols) ]
410             unless ref $symbols eq ARRAY;
411             $any{ $_ } ||= $pkg
412 825   66     13455 for @$symbols;
413             }
414              
415             # $EXPORT_ALL is merged into @all and all symbols are mapped
416             # to their packages in %any
417 3532 100       3449 if ($symbols = ${ $pkg.PKG.EXPORT_ALL }) {
  3532         9010  
418 77 50       336 $symbols = [ split(DELIMITER, $symbols) ]
419             unless ref $symbols eq ARRAY;
420             push(
421             @all,
422 77   66     198 map { $any{ $_ } ||= $pkg; $_ }
  854         2952  
  854         1207  
423             @$symbols
424             );
425             }
426              
427             # $EXPORT_TAGS are copied into %tags unless already defined
428 3532 100       3279 if ($symbols = ${ $pkg.PKG.EXPORT_TAGS }) {
  3532         8685  
429             $tags{ $_ } ||= $symbols->{ $_ }
430 434   33     3970 for keys %$symbols;
431             }
432              
433             # $EXPORT_HOOKS are copied into %hooks unless already defined
434             # (by a more specific subclass) either as hooks or any/all items
435 3532 100       3456 if ($symbols = ${ $pkg.PKG.EXPORT_HOOKS }) {
  3532         8279  
436             $any{ $_ } or $hooks{ $_ } ||= $symbols->{ $_ }
437 610   33     11618 for keys %$symbols;
      100        
438             }
439              
440             # $EXPORT_FAIL has only one value per package, but we can have
441             # several packages in the class ancestry
442 3532 100       3500 if ($symbols = ${ $pkg.PKG.EXPORT_FAIL }) {
  3532         8823  
443 267         502 push(@fails, $symbols);
444             }
445              
446             # $EXPORT_BEFORE and $EXPORT_AFTER are references to CODE or
447             # ARRAY refs (of CODE refs, we assume). As we travel up from
448             # subclass to superclass, we unshift() the handlers onto the
449             # start of the @before/@after arrays. This ensures that the base
450             # class handlers get called before subclass handlers.
451 3532 100       3344 if ($symbols = ${ $pkg.PKG.EXPORT_BEFORE }) {
  3532         8701  
452 28 50       140 unshift(
    100          
453             @before,
454             ref $symbols eq CODE ? $symbols :
455             ref $symbols eq ARRAY ? @$symbols :
456             croak sprintf(BAD_HANDLER, before => $symbols)
457             );
458             }
459              
460 3532 100       3305 if ($symbols = ${ $pkg.PKG.EXPORT_AFTER }) {
  3532         8286  
461 74 50       471 unshift(
    100          
462             @after,
463             ref $symbols eq CODE ? $symbols :
464             ref $symbols eq ARRAY ? @$symbols :
465             croak sprintf(BAD_HANDLER, after => $symbols)
466             );
467             }
468              
469             # This is the same depth-first inheritance resolution algorithm
470             # that Perl uses. We can't use the fancy heritage() method in
471             # Badger::Class because of the Chicken-and-Egg dependency problem
472             # between Badger::Exporter and Badger::Class
473 3532         3379 push(@pending, @{$pkg.PKG.ISA});
  3532         10266  
474             }
475              
476 1101         5908 [\@all, \%any, \%tags, \%hooks, \@fails, \@before, \@after];
477             };
478              
479             return wantarray
480 6648 50       19024 ? @$cache
481             : $cache;
482             }
483              
484             sub export_symbol {
485 304     304 1 683 my ($self, $target, $symbol, $ref) = @_;
486 70     70   2542 no strict REFS;
  70         2962  
  70         3399  
487 70     70   355 no warnings ONCE;
  70         1462  
  70         9535  
488 304         440 *{ $target.PKG.$symbol } = $ref;
  304         1732  
489             }
490              
491             sub export_variable {
492 1761     1761 0 3160 my ($self, $name, $default) = @_;
493 1761   33     4476 my $class = ref $self || $self;
494 1761         4286 my $var = $class.PKG.$name;
495 1761         1638 my $item;
496 70     70   392 no strict REFS;
  70         1743  
  70         20781  
497              
498 1761 100       1645 unless (defined ($item = ${$var})) {
  1761         6862  
499             # install the default value ref into the SCALAR $EXPORT_XXXX var
500 1548         1837 ${$var} = $item = $default;
  1548         2422  
501             # then poke the symbol table to make Perl notice it's defined
502 1548         1724 *{$var} = \${$var};
  1548         2634  
  1548         2409  
503             }
504              
505 1761         4408 return $item;
506             }
507              
508             sub export_hook_generator {
509 467     467 1 1009 my $self = shift;
510 467         586 my $name = shift;
511 467 50       886 my $hook = @_ == 1 ? shift : [ @_ ];
512              
513             # do nothing if we've already got a code ref that doesn't require args
514 467 50       893 return $hook
515             if ref $hook eq CODE;
516              
517             # anything else must be a list ref containing [$code_ref, $n_args]
518 467 50       892 croak sprintf(BAD_HOOK, $name, $hook)
519             unless ref $hook eq ARRAY;
520              
521 467         764 my ($code, $nargs) = @$hook;
522              
523             # user is trying to confuse us with [$non_code_ref, ...]
524 467 50       807 croak sprintf(BAD_HOOK, $name, $code)
525             unless ref $code eq CODE;
526              
527             # [$code, 0] or [$code] is fine as just $code, also reject $nargs < 0
528 467 50 33     1363 return $code
529             unless $nargs && $nargs > 0;
530              
531             # OK it's safe to proceed
532             return sub {
533 5255     5255   7877 my ($this, $target, $symbol, $symbols) = @_;
534 5255         4916 my $n = 1;
535             # check we've got enough arguments
536 5255 50       7221 croak sprintf(MISSING, $symbol, sprintf(WANTED, $nargs, scalar @$symbols))
537             if @$symbols < $nargs;
538              
539             # call the code ref with the first $nargs arguments, making sure
540             # they all have defined values
541             $code->(
542             $this, $target, $symbol,
543             ( map {
544 5255 50       8062 croak sprintf(MISSING, $symbol, sprintf(UNDEFINED, $n, $nargs))
  5255         6753  
545             unless defined $_;
546 5255         4559 $n++;
547 5255         12349 $_
548             }
549             splice(@$symbols, 0, $nargs)
550             ),
551             $symbols,
552             );
553             }
554 467         2473 }
555              
556             sub _debug {
557 0     0     print STDERR @_;
558             }
559              
560              
561             1;
562              
563             __END__