File Coverage

blib/lib/Perl6/Export/Attrs.pm
Criterion Covered Total %
statement 39 108 36.1
branch 0 26 0.0
condition 0 17 0.0
subroutine 12 15 80.0
pod n/a
total 51 166 30.7


line stmt bran cond sub pod time code
1             package Perl6::Export::Attrs;
2              
3 1     1   33850 use version; $VERSION = qv('0.0.3');
  1         3657  
  1         7  
4              
5 1     1   101 use warnings;
  1         3  
  1         38  
6 1     1   5 use strict;
  1         13  
  1         31  
7 1     1   6 use Carp;
  1         2  
  1         211  
8 1     1   1602 use Attribute::Handlers;
  1         8933  
  1         7  
9              
10             sub import {
11 1     1   12 my $caller = caller;
12 1     1   59 no strict 'refs';
  1         3  
  1         627  
13 1         3 *{$caller.'::import'} = \&_generic_import;
  1         16  
14 1         2 *{$caller.'::MODIFY_CODE_ATTRIBUTES'} = \&_generic_MCA;
  1         7  
15 1         11 return;
16             }
17              
18             my %tagsets_for;
19             my %is_exported_from;
20             my %named_tagsets_for;
21              
22             my $IDENT = '[^\W\d]\w*';
23              
24             sub _generic_MCA {
25 0     0     my ($package, $referent, @attrs) = @_;
26              
27             ATTR:
28 0           for my $attr (@attrs) {
29              
30 0 0 0       ($attr||=q{}) =~ s/\A Export (?: \( (.*) \) )? \z/$1||q{}/exms
  0 0          
31             or next ATTR;
32              
33 0           my @tagsets = grep {length $_} split m/ \s+,?\s* | ,\s* /xms, $attr;
  0            
34              
35 0           my (undef, $file, $line) = caller();
36 0           $file =~ s{.*/}{}xms;
37              
38 0 0         if (my @bad_tags = grep {!m/\A :$IDENT \z/xms} @tagsets) {
  0            
39 0 0         die 'Bad tagset',
40             (@bad_tags==1?' ':'s '),
41             "in :Export attribute at '$file' line $line: [@bad_tags]\n";
42             }
43              
44 0   0       my $tagsets = $tagsets_for{$package} ||= {};
45              
46 0           for my $tagset (@tagsets) {
47 0           push @{ $tagsets->{$tagset} }, $referent;
  0            
48             }
49 0           push @{ $tagsets->{':ALL'} }, $referent;
  0            
50              
51 0           $is_exported_from{$package}{$referent} = 1;
52              
53 0           undef $attr
54             }
55              
56 0           return grep {defined $_} @attrs;
  0            
57             }
58              
59             sub _invert_tagset {
60 0     0     my ($package, $tagset) = @_;
61 0           my %inverted_tagset;
62              
63 0           for my $tag (keys %{$tagset}) {
  0            
64 0           for my $sub_ref (@{$tagset->{$tag}}) {
  0            
65 0 0         my $sym = Attribute::Handlers::findsym($package, $sub_ref, 'CODE')
66             or die "Internal error: missing symbol for $sub_ref";
67 0           $inverted_tagset{$tag}{*{$sym}{NAME}} = $sub_ref;;
  0            
68             }
69             }
70              
71 0           return \%inverted_tagset;
72             }
73              
74             # Reusable import() subroutine for all packages...
75             sub _generic_import {
76 0     0     my $package = shift;
77              
78 0   0       my $tagset
79             = $named_tagsets_for{$package}
80             ||= _invert_tagset($package, $tagsets_for{$package});
81              
82 0           my $is_exported = $is_exported_from{$package};
83              
84 0           my $errors;
85              
86             my %request;
87 0           my @pass_on_list;
88 0           my $subs_ref;
89              
90             REQUEST:
91 0           for my $request (@_) {
92 0 0 0       if (my ($sub_name) = $request =~ m/\A &? ($IDENT) (?:\(\))? \z/xms) {
    0          
93 0 0         next REQUEST if exists $request{$sub_name};
94 1     1   5 no strict 'refs';
  1         2  
  1         35  
95 1     1   5 no warnings 'once';
  1         2  
  1         158  
96 0 0         if (my $sub_ref = *{$package.'::'.$sub_name}{CODE}) {
  0            
97 0 0         if ($is_exported->{$sub_ref}) {
98 0           $request{$sub_name} = $sub_ref;
99 0           next REQUEST;
100             }
101             }
102             }
103             elsif ($request =~ m/\A :$IDENT \z/xms
104             and $subs_ref = $tagset->{$request}) {
105 0           @request{keys %{$subs_ref}} = values %{$subs_ref};
  0            
  0            
106 0           next REQUEST;
107             }
108 0           $errors .= " $request";
109 0           push @pass_on_list, $request;
110             }
111              
112             # Report unexportable requests...
113 0           my $real_import = do{
114 1     1   6 no strict 'refs';
  1         2  
  1         34  
115 1     1   5 no warnings 'once';
  1         3  
  1         164  
116 0           *{$package.'::IMPORT'}{CODE};
  0            
117             };
118 0 0 0       croak "$package does not export:$errors\nuse $package failed"
119             if $errors && !$real_import;
120              
121 0 0         if (!@_) {
122 0   0       %request = %{$tagset->{':DEFAULT'}||={}}
  0            
123             }
124              
125 0   0       my $mandatory = $tagset->{':MANDATORY'} ||= {};
126 0           @request{ keys %{$mandatory} } = values %{$mandatory};
  0            
  0            
127              
128 0           my $caller = caller;
129 0           for my $sub_name (keys %request) {
130 1     1   5 no strict 'refs';
  1         1  
  1         103  
131 0           *{$caller.'::'.$sub_name} = $request{$sub_name};
  0            
132             }
133              
134 0 0         goto &{$real_import} if $real_import;
  0            
135 0           return;
136             }
137              
138             1; # Magic true value required at end of module
139             __END__