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   2517 use Carp;
  70         117  
  70         3766  
20 70     70   379 use strict;
  70         119  
  70         1356  
21 70     70   314 use warnings;
  70         147  
  70         9411  
22             use constant {
23 70         85890 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   491 };
  70         174  
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 1221 my $self = shift;
79 696 100 66     3191 my $data = @_ == 1 && ref $_[0] eq HASH ? shift : { @_ };
80 696         911 my $handler;
81              
82             # delegate each key in $data to a handler in $HANDLERS
83 696         2776 while (my ($key, $value) = each %$data) {
84 1188   33     2968 $handler = $HANDLERS->{ $key }
85             || croak "Invalid exports key: $key\n";
86 1188         2637 $handler->($self, $value);
87             }
88             }
89              
90             sub export_all {
91 75     75 1 3115 my $self = shift;
92 75 100       365 my $args = @_ == 1 ? shift : [ @_ ];
93 75         349 my $list = $self->export_variable( EXPORT_ALL => [ ] );
94 75 100       1998 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
95             }
96              
97             sub export_any {
98 795     795 1 2528 my $self = shift;
99 795 100       4561 my $args = @_ == 1 ? shift : [ @_ ];
100 795         3308 my $list = $self->export_variable( EXPORT_ANY => [ ] );
101 795 100       7432 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
102             }
103              
104             sub export_before {
105 26     26 1 82 my $self = shift;
106 26 50       125 my $args = @_ == 1 ? shift : [ @_ ];
107 26         112 my $list = $self->export_variable( EXPORT_BEFORE => [ ] );
108 26 50       188 push( @$list, ref $args eq ARRAY ? @$args : $args );
109             }
110              
111             sub export_after {
112 72     72 1 275 my $self = shift;
113 72 50       506 my $args = @_ == 1 ? shift : [ @_ ];
114 72         421 my $list = $self->export_variable( EXPORT_AFTER => [ ] );
115 72 50       714 push( @$list, ref $args eq ARRAY ? @$args : $args );
116             }
117              
118             sub export_tags {
119 287     287 1 1869 my $self = shift;
120 287 100 66     3120 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
121 287         2890 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     2944 if (ref && ref eq HASH) {
  500         1417  
128 4         14 while (my ($key, $value) = each %$_) {
129 13 100       48 if ($value =~ s/^=//) {
130 4 50       8 _debug("export_tags() constructing constant: $key => $value\n") if $DEBUG;
131 4     0   32 $_->{ $key } = sub() { $value };
  0         0  
132             }
133             }
134             }
135 500         1486 $_;
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     12241 not (ref || /^(:|=)/);
145             }
146             map {
147             # symbols in tagset can be a list ref, hash ref or string
148 287 100       700 ref $_ eq ARRAY ? @$_ :
  500 100       4446  
149             ref $_ eq HASH ? %$_ :
150             split DELIMITER
151             }
152             values %$args
153             );
154              
155 287         1169 return $tags;
156             }
157              
158             sub export_hooks {
159 506     506 1 1362 my $self = shift;
160 506 100 66     2496 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
161 506         2103 my $hooks = $self->export_variable( EXPORT_HOOKS => { } );
162 506         2817 @$hooks{ keys %$args } = values %$args;
163 506         2080 return $hooks;
164             }
165              
166             sub export_fail {
167 166     166 1 1521 my $self = shift;
168 166   33     734 my $class = ref $self || $self;
169 70     70   555 no strict REFS;
  70         145  
  70         16124  
170              
171             # get/set $EXPORT_FAIL
172             return @_
173 166         995 ? (${$class.PKG.EXPORT_FAIL} = shift)
174 166 50       609 : ${$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 3424     3424   26667 my $class = shift;
186 3424         11038 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 3424         21242 strict->import;
193 3424         29031 warnings->import;
194              
195             # call in the heavy guns
196 3424         15345 $class->export($target, @_);
197             }
198              
199             sub export {
200 6650     6650 1 9615 my $class = shift;
201 6650         11092 my $target = shift;
202 6650 100       18082 my $imports = @_ == 1 ? shift : [ @_ ];
203 6650         16184 my ($all, $any, $tags, $hooks, $fails, $before, $after)
204             = $class->exportables;
205 6650 100       13069 my $can_hook = (%$hooks ? 1 : 0);
206 6650         7469 my $added_all = 0;
207 6650         7111 my $count = 0;
208 6650         8653 my ($symbol, $symbols, $source, $hook, $pkg, $nargs,
209             %done, @args, @errors);
210              
211 70     70   544 no strict REFS;
  70         155  
  70         2915  
212 70     70   404 no warnings ONCE;
  70         146  
  70         67581  
213              
214             # imports can be a single whitespace delimited string of symbols
215 6650 100       33493 $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 6650 100       13474 @$imports = @$all unless @$imports;
222              
223 6650         11741 foreach $hook (@$before) {
224 28         180 $hook->($class, $target, $imports);
225             }
226              
227 6650         11644 SYMBOL: while (@$imports) {
228 33813 50       59059 next unless ($symbol = shift @$imports);
229 33813 100       72605 next if $done{ $symbol }++;
230              
231             # look for :tagset symbols and expand their contents onto @$imports
232 33812 100       61664 if ($symbol =~ s/^://) {
233 455 100       1468 if ($symbols = $tags->{ $symbol }) {
    100          
    50          
234 445 100       1287 if (ref $symbols eq ARRAY) {
    100          
235             # expand list of symbols onto @$imports list
236 8         19 unshift(@$imports, @$symbols);
237             }
238             elsif (ref $symbols eq HASH) {
239             # map hash into [name => $symbol] pairs
240 4         13 unshift(@$imports, map { [$_ => $symbols->{ $_ }] } keys %$symbols);
  13         26  
241             }
242             else {
243             # string of space-delimited symbols
244 433         7021 unshift(@$imports, split(DELIMITER, $symbols));
245             }
246             }
247             elsif ($symbol eq DEFAULT) {
248 1         4 unshift(@$imports, @$all);
249             }
250             elsif ($symbol eq ALL) {
251 9         88 unshift(@$imports, keys %$any);
252 9         27 $added_all = 1;
253             }
254             else {
255 0         0 push(@errors, "Invalid import tag: $symbol\n");
256             }
257 455         1141 next SYMBOL;
258             }
259              
260 33357 100 100     115450 if (ref $symbol eq ARRAY) {
    100          
    50          
    100          
    100          
261             # a pair of [name, $symbol] expanded from a :tag hash set
262 13         20 ($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 14063 100       26734 $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 14063         31325 &$hook($class, $target, $symbol, $imports);
274              
275             # hooks can be repeated so pretend we haven't done it
276 14060         23393 $done{ $symbol }--;
277 14060         19973 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         1732 $can_hook = 0;
291 1191 50       2378 if ($symbols = shift @$imports) {
292 1191 50       7208 $symbols = [ split(DELIMITER, $symbols) ]
293             unless ref $symbols eq ARRAY;
294 1191         3408 unshift(@$imports, @$symbols, HOOKS);
295             }
296             else {
297 0         0 push(@errors, "Missing argument for $symbol hook\n");
298             }
299 1191         1879 next SYMBOL;
300             }
301             elsif ($symbol eq HOOKS) {
302             # special 'hooks' item turns hooks back on
303 1191         1558 $can_hook = 1;
304 1191         1588 next SYMBOL;
305             }
306             else {
307             # otherwise the symbol exported is the one requested
308 16899         22157 $source = $symbol;
309             }
310              
311             # check we're allowed to export the symbol requested
312 16912 100       29122 if ($pkg = $any->{ $symbol }) {
313             # _debug("exporting $symbol from $pkg to $target\n") if $DEBUG;
314             }
315             else {
316 198         515 foreach $hook (@$fails) {
317 199 100       661 if (&$hook($class, $target, $symbol, $imports)) {
318             # hooks can be repeated so pretend we haven't done it
319 196         410 $done{ $symbol }--;
320 196         436 next SYMBOL;
321             }
322             }
323 2         7 push(@errors, "$symbol is not exported by $class\n");
324 2         5 next SYMBOL;
325             }
326              
327 16714 100       21156 if (ref $source eq CODE) {
328             # patch directly into the code ref
329             # _debug("exporting $symbol from code reference\n") if $DEBUG;
330 8         9 *{ $target.PKG.$symbol } = $source;
  8         26  
331             }
332             else {
333 16706         18029 my $type = "&";
334 16706         27023 $symbol =~ s/^(\W)//;
335 16706 100       27836 $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 16706 50 33     25827 _debug("export() constructing constant: $symbol => $source\n")
342             if $DEBUG && $type eq '=';
343 16706 100 66     61945 $source = $pkg.PKG.$source unless $source =~ /::/ or $type eq '=';
344 16706 50       25569 _debug("exporting $type$symbol from $source into $target\n") if $DEBUG;
345 16706         71342 *{ $target.PKG.$symbol } =
346 16647         42454 $type eq '&' ? \&{$source} :
347 35         74 $type eq '$' ? \${$source} :
348 14         34 $type eq '@' ? \@{$source} :
349 10         29 $type eq '%' ? \%{$source} :
350 0         0 $type eq '*' ? *{$source} :
351 0     0   0 $type eq '=' ? sub(){$source} :
352 16706 0       22856 do { push(@errors, "Can't export symbol: $type$symbol\n"); next; };
  0 0       0  
  0 50       0  
    100          
    100          
    100          
353             }
354 16714         24673 $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 33810 100 100     87562 unless (@$imports or $count or $added_all) {
      66        
361 633         1368 unshift(@$imports, @$all);
362 633         1375 $added_all = 1;
363             }
364             }
365              
366 6647 100       11866 if (@errors) {
367 1         5 require Carp;
368 1         231 Carp::croak("@{errors}Can't continue after import errors");
369             }
370              
371 6646         10857 foreach $hook (@$after) {
372 102         407 $hook->($class, $target);
373             }
374              
375 6645         1567194 return 1;
376             }
377              
378             sub exportables {
379 6650     6650 1 8316 my $class = shift;
380 70     70   613 no strict REFS;
  70         151  
  70         4490  
381 70     70   2080 no warnings ONCE;
  70         1917  
  70         6033  
382              
383 6650   66     7377 my $cache = ${ $class.PKG.EXPORTABLES } ||= do {
  6650         26653  
384 1101         1936 my ($pkg, $symbols, %done, @all, %any, %tags, %hooks, @fails, @before, @after);
385 1101         2260 my @pending = ($class);
386 70     70   511 no strict REFS;
  70         131  
  70         33984  
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         2529 while ($pkg = shift @pending) {
393 3609 100       8900 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       4539 if ($symbols = ${ $pkg.PKG.EXPORT_ANY }) {
  3532         11236  
409 825 50       3672 $symbols = [ split(DELIMITER, $symbols) ]
410             unless ref $symbols eq ARRAY;
411             $any{ $_ } ||= $pkg
412 825   66     16058 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       4014 if ($symbols = ${ $pkg.PKG.EXPORT_ALL }) {
  3532         10089  
418 77 50       482 $symbols = [ split(DELIMITER, $symbols) ]
419             unless ref $symbols eq ARRAY;
420             push(
421             @all,
422 77   66     264 map { $any{ $_ } ||= $pkg; $_ }
  854         3503  
  854         1526  
423             @$symbols
424             );
425             }
426              
427             # $EXPORT_TAGS are copied into %tags unless already defined
428 3532 100       3808 if ($symbols = ${ $pkg.PKG.EXPORT_TAGS }) {
  3532         9838  
429             $tags{ $_ } ||= $symbols->{ $_ }
430 434   33     4768 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       3962 if ($symbols = ${ $pkg.PKG.EXPORT_HOOKS }) {
  3532         9363  
436             $any{ $_ } or $hooks{ $_ } ||= $symbols->{ $_ }
437 610   33     13405 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       4146 if ($symbols = ${ $pkg.PKG.EXPORT_FAIL }) {
  3532         10087  
443 267         592 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       3727 if ($symbols = ${ $pkg.PKG.EXPORT_BEFORE }) {
  3532         9896  
452 28 50       169 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       3888 if ($symbols = ${ $pkg.PKG.EXPORT_AFTER }) {
  3532         9296  
461 74 50       594 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         4290 push(@pending, @{$pkg.PKG.ISA});
  3532         11576  
474             }
475              
476 1101         6381 [\@all, \%any, \%tags, \%hooks, \@fails, \@before, \@after];
477             };
478              
479             return wantarray
480 6650 50       21917 ? @$cache
481             : $cache;
482             }
483              
484             sub export_symbol {
485 305     305 1 847 my ($self, $target, $symbol, $ref) = @_;
486 70     70   6199 no strict REFS;
  70         188  
  70         2375  
487 70     70   2039 no warnings ONCE;
  70         1800  
  70         11423  
488 305         465 *{ $target.PKG.$symbol } = $ref;
  305         2017  
489             }
490              
491             sub export_variable {
492 1761     1761 0 3510 my ($self, $name, $default) = @_;
493 1761   33     6558 my $class = ref $self || $self;
494 1761         3425 my $var = $class.PKG.$name;
495 1761         1844 my $item;
496 70     70   475 no strict REFS;
  70         181  
  70         24188  
497              
498 1761 100       1867 unless (defined ($item = ${$var})) {
  1761         7652  
499             # install the default value ref into the SCALAR $EXPORT_XXXX var
500 1548         2165 ${$var} = $item = $default;
  1548         2899  
501             # then poke the symbol table to make Perl notice it's defined
502 1548         1910 *{$var} = \${$var};
  1548         4968  
  1548         2825  
503             }
504              
505 1761         3574 return $item;
506             }
507              
508             sub export_hook_generator {
509 467     467 1 765 my $self = shift;
510 467         708 my $name = shift;
511 467 50       1002 my $hook = @_ == 1 ? shift : [ @_ ];
512              
513             # do nothing if we've already got a code ref that doesn't require args
514 467 50       1093 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       944 croak sprintf(BAD_HOOK, $name, $hook)
519             unless ref $hook eq ARRAY;
520              
521 467         937 my ($code, $nargs) = @$hook;
522              
523             # user is trying to confuse us with [$non_code_ref, ...]
524 467 50       970 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     1579 return $code
529             unless $nargs && $nargs > 0;
530              
531             # OK it's safe to proceed
532             return sub {
533 5255     5255   9233 my ($this, $target, $symbol, $symbols) = @_;
534 5255         5797 my $n = 1;
535             # check we've got enough arguments
536 5255 50       8359 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       9496 croak sprintf(MISSING, $symbol, sprintf(UNDEFINED, $n, $nargs))
  5255         8241  
545             unless defined $_;
546 5255         5332 $n++;
547 5255         13662 $_
548             }
549             splice(@$symbols, 0, $nargs)
550             ),
551             $symbols,
552             );
553             }
554 467         3105 }
555              
556             sub _debug {
557 0     0     print STDERR @_;
558             }
559              
560              
561             1;
562              
563             __END__