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   2512 use Carp;
  70         126  
  70         3532  
20 70     70   344 use strict;
  70         125  
  70         1356  
21 70     70   307 use warnings;
  70         121  
  70         9136  
22             use constant {
23 70         83340 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   498 };
  70         155  
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 1299 my $self = shift;
79 696 100 66     3024 my $data = @_ == 1 && ref $_[0] eq HASH ? shift : { @_ };
80 696         990 my $handler;
81              
82             # delegate each key in $data to a handler in $HANDLERS
83 696         2778 while (my ($key, $value) = each %$data) {
84 1188   33     3026 $handler = $HANDLERS->{ $key }
85             || croak "Invalid exports key: $key\n";
86 1188         2461 $handler->($self, $value);
87             }
88             }
89              
90             sub export_all {
91 75     75 1 3053 my $self = shift;
92 75 100       435 my $args = @_ == 1 ? shift : [ @_ ];
93 75         394 my $list = $self->export_variable( EXPORT_ALL => [ ] );
94 75 100       1851 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
95             }
96              
97             sub export_any {
98 795     795 1 2497 my $self = shift;
99 795 100       2754 my $args = @_ == 1 ? shift : [ @_ ];
100 795         4777 my $list = $self->export_variable( EXPORT_ANY => [ ] );
101 795 100       8861 push( @$list, ref $args eq ARRAY ? @$args : split(DELIMITER, $args) );
102             }
103              
104             sub export_before {
105 26     26 1 92 my $self = shift;
106 26 50       113 my $args = @_ == 1 ? shift : [ @_ ];
107 26         120 my $list = $self->export_variable( EXPORT_BEFORE => [ ] );
108 26 50       165 push( @$list, ref $args eq ARRAY ? @$args : $args );
109             }
110              
111             sub export_after {
112 72     72 1 200 my $self = shift;
113 72 50       345 my $args = @_ == 1 ? shift : [ @_ ];
114 72         389 my $list = $self->export_variable( EXPORT_AFTER => [ ] );
115 72 50       596 push( @$list, ref $args eq ARRAY ? @$args : $args );
116             }
117              
118             sub export_tags {
119 287     287 1 1839 my $self = shift;
120 287 100 66     2781 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
121 287         940 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     2498 if (ref && ref eq HASH) {
  500         2936  
128 4         22 while (my ($key, $value) = each %$_) {
129 13 100       45 if ($value =~ s/^=//) {
130 4 50       9 _debug("export_tags() constructing constant: $key => $value\n") if $DEBUG;
131 4     0   30 $_->{ $key } = sub() { $value };
  0         0  
132             }
133             }
134             }
135 500         1437 $_;
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     10438 not (ref || /^(:|=)/);
145             }
146             map {
147             # symbols in tagset can be a list ref, hash ref or string
148 287 100       655 ref $_ eq ARRAY ? @$_ :
  500 100       4410  
149             ref $_ eq HASH ? %$_ :
150             split DELIMITER
151             }
152             values %$args
153             );
154              
155 287         1125 return $tags;
156             }
157              
158             sub export_hooks {
159 506     506 1 1394 my $self = shift;
160 506 100 66     2112 my $args = (@_ == 1) && (ref $_[0] eq HASH) ? shift : { @_ };
161 506         2134 my $hooks = $self->export_variable( EXPORT_HOOKS => { } );
162 506         2755 @$hooks{ keys %$args } = values %$args;
163 506         2085 return $hooks;
164             }
165              
166             sub export_fail {
167 166     166 1 1495 my $self = shift;
168 166   33     695 my $class = ref $self || $self;
169 70     70   549 no strict REFS;
  70         136  
  70         15597  
170              
171             # get/set $EXPORT_FAIL
172             return @_
173 166         973 ? (${$class.PKG.EXPORT_FAIL} = shift)
174 166 50       602 : ${$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   25348 my $class = shift;
186 3424         8983 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         19371 strict->import;
193 3424         31432 warnings->import;
194              
195             # call in the heavy guns
196 3424         12776 $class->export($target, @_);
197             }
198              
199             sub export {
200 6650     6650 1 9266 my $class = shift;
201 6650         9097 my $target = shift;
202 6650 100       20716 my $imports = @_ == 1 ? shift : [ @_ ];
203 6650         15724 my ($all, $any, $tags, $hooks, $fails, $before, $after)
204             = $class->exportables;
205 6650 100       12743 my $can_hook = (%$hooks ? 1 : 0);
206 6650         7223 my $added_all = 0;
207 6650         7045 my $count = 0;
208 6650         8407 my ($symbol, $symbols, $source, $hook, $pkg, $nargs,
209             %done, @args, @errors);
210              
211 70     70   498 no strict REFS;
  70         125  
  70         2870  
212 70     70   406 no warnings ONCE;
  70         137  
  70         66288  
213              
214             # imports can be a single whitespace delimited string of symbols
215 6650 100       32667 $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       13165 @$imports = @$all unless @$imports;
222              
223 6650         11936 foreach $hook (@$before) {
224 28         120 $hook->($class, $target, $imports);
225             }
226              
227 6650         11481 SYMBOL: while (@$imports) {
228 33813 50       58193 next unless ($symbol = shift @$imports);
229 33813 100       71337 next if $done{ $symbol }++;
230              
231             # look for :tagset symbols and expand their contents onto @$imports
232 33812 100       60135 if ($symbol =~ s/^://) {
233 455 100       1327 if ($symbols = $tags->{ $symbol }) {
    100          
    50          
234 445 100       1310 if (ref $symbols eq ARRAY) {
    100          
235             # expand list of symbols onto @$imports list
236 8         21 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         30  
241             }
242             else {
243             # string of space-delimited symbols
244 433         7070 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         109 unshift(@$imports, keys %$any);
252 9         29 $added_all = 1;
253             }
254             else {
255 0         0 push(@errors, "Invalid import tag: $symbol\n");
256             }
257 455         1146 next SYMBOL;
258             }
259              
260 33357 100 100     114556 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       26255 $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         29984 &$hook($class, $target, $symbol, $imports);
274              
275             # hooks can be repeated so pretend we haven't done it
276 14060         23077 $done{ $symbol }--;
277 14060         19805 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         1664 $can_hook = 0;
291 1191 50       2168 if ($symbols = shift @$imports) {
292 1191 50       7020 $symbols = [ split(DELIMITER, $symbols) ]
293             unless ref $symbols eq ARRAY;
294 1191         3290 unshift(@$imports, @$symbols, HOOKS);
295             }
296             else {
297 0         0 push(@errors, "Missing argument for $symbol hook\n");
298             }
299 1191         1862 next SYMBOL;
300             }
301             elsif ($symbol eq HOOKS) {
302             # special 'hooks' item turns hooks back on
303 1191         1503 $can_hook = 1;
304 1191         1637 next SYMBOL;
305             }
306             else {
307             # otherwise the symbol exported is the one requested
308 16899         21584 $source = $symbol;
309             }
310              
311             # check we're allowed to export the symbol requested
312 16912 100       28429 if ($pkg = $any->{ $symbol }) {
313             # _debug("exporting $symbol from $pkg to $target\n") if $DEBUG;
314             }
315             else {
316 198         441 foreach $hook (@$fails) {
317 199 100       580 if (&$hook($class, $target, $symbol, $imports)) {
318             # hooks can be repeated so pretend we haven't done it
319 196         394 $done{ $symbol }--;
320 196         492 next SYMBOL;
321             }
322             }
323 2         6 push(@errors, "$symbol is not exported by $class\n");
324 2         5 next SYMBOL;
325             }
326              
327 16714 100       20903 if (ref $source eq CODE) {
328             # patch directly into the code ref
329             # _debug("exporting $symbol from code reference\n") if $DEBUG;
330 8         7 *{ $target.PKG.$symbol } = $source;
  8         30  
331             }
332             else {
333 16706         18621 my $type = "&";
334 16706         26672 $symbol =~ s/^(\W)//;
335 16706 100       27549 $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     26313 _debug("export() constructing constant: $symbol => $source\n")
342             if $DEBUG && $type eq '=';
343 16706 100 66     60576 $source = $pkg.PKG.$source unless $source =~ /::/ or $type eq '=';
344 16706 50       25239 _debug("exporting $type$symbol from $source into $target\n") if $DEBUG;
345 16706         69354 *{ $target.PKG.$symbol } =
346 16647         40701 $type eq '&' ? \&{$source} :
347 35         72 $type eq '$' ? \${$source} :
348 14         34 $type eq '@' ? \@{$source} :
349 10         19 $type eq '%' ? \%{$source} :
350 0         0 $type eq '*' ? *{$source} :
351 0     0   0 $type eq '=' ? sub(){$source} :
352 16706 0       22476 do { push(@errors, "Can't export symbol: $type$symbol\n"); next; };
  0 0       0  
  0 50       0  
    100          
    100          
    100          
353             }
354 16714         24257 $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     86427 unless (@$imports or $count or $added_all) {
      66        
361 633         1379 unshift(@$imports, @$all);
362 633         1344 $added_all = 1;
363             }
364             }
365              
366 6647 100       11940 if (@errors) {
367 1         5 require Carp;
368 1         199 Carp::croak("@{errors}Can't continue after import errors");
369             }
370              
371 6646         10477 foreach $hook (@$after) {
372 102         443 $hook->($class, $target);
373             }
374              
375 6645         1523653 return 1;
376             }
377              
378             sub exportables {
379 6650     6650 1 8203 my $class = shift;
380 70     70   607 no strict REFS;
  70         140  
  70         2888  
381 70     70   2086 no warnings ONCE;
  70         153  
  70         9490  
382              
383 6650   66     7143 my $cache = ${ $class.PKG.EXPORTABLES } ||= do {
  6650         25858  
384 1101         1926 my ($pkg, $symbols, %done, @all, %any, %tags, %hooks, @fails, @before, @after);
385 1101         2196 my @pending = ($class);
386 70     70   497 no strict REFS;
  70         118  
  70         31705  
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         2499 while ($pkg = shift @pending) {
393 3609 100       8935 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       3723 if ($symbols = ${ $pkg.PKG.EXPORT_ANY }) {
  3532         10855  
409 825 50       4023 $symbols = [ split(DELIMITER, $symbols) ]
410             unless ref $symbols eq ARRAY;
411             $any{ $_ } ||= $pkg
412 825   66     15357 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       3997 if ($symbols = ${ $pkg.PKG.EXPORT_ALL }) {
  3532         9774  
418 77 50       405 $symbols = [ split(DELIMITER, $symbols) ]
419             unless ref $symbols eq ARRAY;
420             push(
421             @all,
422 77   66     243 map { $any{ $_ } ||= $pkg; $_ }
  854         3310  
  854         1400  
423             @$symbols
424             );
425             }
426              
427             # $EXPORT_TAGS are copied into %tags unless already defined
428 3532 100       3800 if ($symbols = ${ $pkg.PKG.EXPORT_TAGS }) {
  3532         9666  
429             $tags{ $_ } ||= $symbols->{ $_ }
430 434   33     4392 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       3936 if ($symbols = ${ $pkg.PKG.EXPORT_HOOKS }) {
  3532         9226  
436             $any{ $_ } or $hooks{ $_ } ||= $symbols->{ $_ }
437 610   33     13140 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       4062 if ($symbols = ${ $pkg.PKG.EXPORT_FAIL }) {
  3532         9923  
443 267         533 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       3767 if ($symbols = ${ $pkg.PKG.EXPORT_BEFORE }) {
  3532         9968  
452 28 50       162 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       3795 if ($symbols = ${ $pkg.PKG.EXPORT_AFTER }) {
  3532         9226  
461 74 50       537 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         3883 push(@pending, @{$pkg.PKG.ISA});
  3532         11516  
474             }
475              
476 1101         6328 [\@all, \%any, \%tags, \%hooks, \@fails, \@before, \@after];
477             };
478              
479             return wantarray
480 6650 50       21240 ? @$cache
481             : $cache;
482             }
483              
484             sub export_symbol {
485 305     305 1 779 my ($self, $target, $symbol, $ref) = @_;
486 70     70   4536 no strict REFS;
  70         1895  
  70         2344  
487 70     70   2058 no warnings ONCE;
  70         3391  
  70         11044  
488 305         399 *{ $target.PKG.$symbol } = $ref;
  305         2017  
489             }
490              
491             sub export_variable {
492 1761     1761 0 3380 my ($self, $name, $default) = @_;
493 1761   33     4810 my $class = ref $self || $self;
494 1761         3191 my $var = $class.PKG.$name;
495 1761         3535 my $item;
496 70     70   475 no strict REFS;
  70         141  
  70         23117  
497              
498 1761 100       1833 unless (defined ($item = ${$var})) {
  1761         7506  
499             # install the default value ref into the SCALAR $EXPORT_XXXX var
500 1548         2121 ${$var} = $item = $default;
  1548         2852  
501             # then poke the symbol table to make Perl notice it's defined
502 1548         1847 *{$var} = \${$var};
  1548         3071  
  1548         2686  
503             }
504              
505 1761         3535 return $item;
506             }
507              
508             sub export_hook_generator {
509 467     467 1 716 my $self = shift;
510 467         718 my $name = shift;
511 467 50       998 my $hook = @_ == 1 ? shift : [ @_ ];
512              
513             # do nothing if we've already got a code ref that doesn't require args
514 467 50       1067 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       1050 croak sprintf(BAD_HOOK, $name, $hook)
519             unless ref $hook eq ARRAY;
520              
521 467         931 my ($code, $nargs) = @$hook;
522              
523             # user is trying to confuse us with [$non_code_ref, ...]
524 467 50       946 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     1550 return $code
529             unless $nargs && $nargs > 0;
530              
531             # OK it's safe to proceed
532             return sub {
533 5255     5255   8983 my ($this, $target, $symbol, $symbols) = @_;
534 5255         5685 my $n = 1;
535             # check we've got enough arguments
536 5255 50       8207 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       9320 croak sprintf(MISSING, $symbol, sprintf(UNDEFINED, $n, $nargs))
  5255         7853  
545             unless defined $_;
546 5255         5237 $n++;
547 5255         13741 $_
548             }
549             splice(@$symbols, 0, $nargs)
550             ),
551             $symbols,
552             );
553             }
554 467         3146 }
555              
556             sub _debug {
557 0     0     print STDERR @_;
558             }
559              
560              
561             1;
562              
563             __END__