File Coverage

blib/lib/Exporter/Declare/Specs.pm
Criterion Covered Total %
statement 102 131 77.8
branch 32 38 84.2
condition 7 9 77.7
subroutine 17 24 70.8
pod 13 13 100.0
total 171 215 79.5


line stmt bran cond sub pod time code
1             package Exporter::Declare::Specs;
2 5     5   2444 use strict;
  5         7  
  5         143  
3 5     5   18 use warnings;
  5         8  
  5         121  
4              
5 5     5   17 use Carp qw/croak/;
  5         7  
  5         5045  
6             our @CARP_NOT = qw/Exporter::Declare/;
7              
8             sub new {
9 19     19 1 5913 my $class = shift;
10 19         44 my ( $package, @args ) = @_;
11 19         60 my $self = bless( [$package,{},{},[]], $class );
12 19 100       48 @args = (':default') unless @args;
13 19         51 $self->_process( "import list", @args );
14 19         78 return $self;
15             }
16              
17 129     129 1 545 sub package { shift->[0] }
18 168     168 1 8088 sub config { shift->[1] }
19 275     275 1 505 sub exports { shift->[2] }
20 41     41 1 107 sub excludes { shift->[3] }
21              
22             sub export {
23 15     15 1 17 my $self = shift;
24 15         20 my ( $dest ) = @_;
25 15         20 for my $item ( keys %{ $self->exports }) {
  15         25  
26 77         60 my ( $export, $conf, $args ) = @{ $self->exports->{$item} };
  77         95  
27 77         234 my ( $sigil, $name ) = ( $item =~ m/^([\&\%\$\@])(.*)$/ );
28 77   66     256 $name = $conf->{as} || join(
29             '',
30             $conf->{prefix} || $self->config->{prefix} || '',
31             $name,
32             $conf->{suffix} || $self->config->{suffix} || '',
33             );
34 77         192 $export->inject( $dest, $name, @$args );
35             }
36             }
37              
38             sub add_export {
39 3     3 1 18 my $self = shift;
40 3         5 my ( $name, $value, $config ) = @_;
41 3 50       10 my $type = ref $value eq 'CODE' ? 'Sub' : 'Variable';
42 3         13 "Exporter::Declare::Export::$type"->new( $value, exported_by => scalar caller() );
43 3   50     18 $self->exports->{$name} = [
44             $value,
45             $config || {},
46             [],
47             ];
48             }
49              
50             sub arguments {
51 0     0 1 0 my $self = shift;
52 0         0 my $meta = $self->package->export_meta;
53 0         0 return grep { $meta->is_argument($_) } keys %{$self->config};
  0         0  
  0         0  
54             }
55              
56             sub options {
57 0     0 1 0 my $self = shift;
58 0         0 my $meta = $self->package->export_meta;
59 0         0 return grep { $meta->is_option($_) } keys %{$self->config};
  0         0  
  0         0  
60             }
61              
62             sub tags {
63 0     0 1 0 my $self = shift;
64 0         0 my $meta = $self->package->export_meta;
65 0         0 return grep { $meta->is_tag($_) } keys %{$self->config};
  0         0  
  0         0  
66             }
67              
68             sub _make_info {
69 0     0   0 my $self = shift;
70 0         0 my $config = $self->config;
71 0         0 return { map { $_, $config->{$_} } @_ };
  0         0  
72             }
73              
74             sub argument_info {
75 0     0 1 0 my $self = shift;
76 0         0 return $self->_make_info($self->arguments);
77             }
78              
79             sub option_info {
80 0     0 1 0 my $self = shift;
81 0         0 return $self->_make_info($self->options);
82             }
83              
84             sub tag_info {
85 0     0 1 0 my $self = shift;
86 0         0 my $all_tags = $self->package->export_meta->export_tags;
87 0         0 return { map { $_, $all_tags->{$_} } $self->tags };
  0         0  
88             }
89              
90              
91             sub _process {
92 19     19   23 my $self = shift;
93 19         32 my ( $tag, @args ) = @_;
94 19         16 my $argnum = 0;
95 19         48 while ( my $item = shift( @args )) {
96 47 50       68 croak "not sure what to do with $item ($tag argument: $argnum)"
97             if ref $item;
98 47         39 $argnum++;
99              
100 47 100       135 if ( $item =~ m/^(!?)[:-](.*)$/ ) {
101 16         41 my ( $neg, $param ) = ( $1, $2 );
102 16 100       29 if ( $self->package->export_meta->arguments_has( $param )) {
103 2         37 $self->config->{$param} = shift( @args );
104 2         2 $argnum++;
105 2         6 next;
106             }
107             else {
108 14 100       386 $self->config->{$param} = ref( $args[0] ) ? $args[0] : !$neg;
109             }
110             }
111              
112 45 100       113 if ( $item =~ m/^!(.*)$/ ) {
    100          
113 2         3 $self->_exclude_item( $1 )
114             }
115             elsif ( my $type = ref( $args[0] )) {
116 4         6 my $arg = shift( @args );
117 4         4 $argnum++;
118 4 100       13 if ( $type eq 'ARRAY' ) {
    50          
119 1         1 $self->_include_item( $item, undef, $arg );
120             }
121             elsif ( $type eq 'HASH' ) {
122 3         6 $self->_include_item( $item, $arg, undef );
123             }
124             else {
125 0         0 croak "Not sure what to do with $item => $arg ($tag arguments: "
126             . ($argnum - 1) . " and $argnum)";
127             }
128             }
129             else {
130 39         65 $self->_include_item( $item )
131             }
132             }
133 19         18 delete $self->exports->{$_} for @{ $self->excludes };
  19         36  
134             }
135              
136 108 100   108   95 sub _item_name { my $in = shift; $in =~ m/^[\&\$\%\@]/ ? $in : "\&$in" }
  108         311  
137              
138             sub _exclude_item {
139 20     20   88 my $self = shift;
140 20         21 my ( $item ) = @_;
141              
142 20 100       40 if ( $item =~ m/^[:-](.*)$/ ) {
143             $self->_exclude_item( $_ )
144 3         5 for $self->_export_tags_get( $1 );
145 3         7 return;
146             }
147              
148 17         10 push @{ $self->excludes } => _item_name($item);
  17         21  
149             }
150              
151             sub _include_item {
152 100     100   3848 my $self = shift;
153 100         101 my ( $item, $conf, $args ) = @_;
154 100   100     212 $conf ||= {};
155 100   100     191 $args ||= [];
156              
157 5     5   25 use Carp qw/confess/;
  5         9  
  5         2020  
158 100 50       161 confess $item if $item =~ m/^&?aaa_/;
159              
160 100 100       165 push @$args => @{ delete $conf->{'-args'} }
  1         2  
161             if defined $conf->{'-args'};
162              
163 100         168 for my $key ( keys %$conf ) {
164 20 100       51 next if $key =~ m/^[:-]/;
165 3         7 push @$args => ( $key, delete $conf->{$key} );
166             }
167              
168 100 100       209 if ( $item =~ m/^[:-](.*)$/ ) {
169 14         25 my $name = $1;
170 14 50       23 return if $self->package->export_meta->options_has( $name );
171 14         278 for my $tagitem ( $self->_export_tags_get( $name ) ) {
172 52         339 my ( $negate, $name ) = ( $tagitem =~ m/^(!)?(.*)$/ );
173 52 50       78 if ( $negate ) {
174 0         0 $self->_exclude_item( $name );
175             }
176             else {
177 52         80 $self->_include_item( $tagitem, $conf, $args );
178             }
179             }
180 14         158 return;
181             }
182              
183 86         107 $item = _item_name($item);
184              
185 86         113 my $existing = $self->exports->{ $item };
186              
187 86 100       139 unless ( $existing ) {
188 79         99 $existing = [ $self->_get_item( $item ), {}, []];
189 79         1595 $self->exports->{ $item } = $existing;
190             }
191              
192 86         81 push @{ $existing->[2] } => @$args;
  86         102  
193 86         233 for my $param ( keys %$conf ) {
194 14         35 my ( $name ) = ( $param =~ m/^[-:](.*)$/ );
195 14         43 $existing->[1]->{$name} = $conf->{$param};
196             }
197             }
198              
199             sub _get_item {
200 80     80   68 my $self = shift;
201 80         75 my ( $name ) = @_;
202 80         110 $self->package->export_meta->exports_get( $name );
203             }
204              
205             sub _export_tags_get {
206 18     18   308 my $self = shift;
207 18         22 my ( $name ) = @_;
208 18         30 $self->package->export_meta->export_tags_get( $name );
209             }
210              
211             1;
212              
213             =head1 NAME
214              
215             Exporter::Declare::Specs - Import argument parser for Exporter::Declare
216              
217             =head1 DESCRIPTION
218              
219             Import arguments can get complicated. All arguments are assumed to be exports
220             unless they have a - or : prefix. The prefix may denote a tag, a boolean
221             option, or an option that takes the next argument as a value. In addition
222             almost all these can be negated with the ! prefix.
223              
224             This class takes care of parsing the import arguments and generating data
225             structures that can be used to find what the exporter needs to know.
226              
227             =head1 METHODS
228              
229             =over 4
230              
231             =item $class->new( $package, @args )
232              
233             Create a new instance and parse @args.
234              
235             =item $specs->package()
236              
237             Get the name of the package that should do the exporting.
238              
239             =item $hashref = $specs->config()
240              
241             Get the configuration hash, All specified options and tags are the keys. The
242             value will be true/false/undef for tags/boolean options. For options that take
243             arguments the value will be that argument. When a config hash is provided to a
244             tag it will be the value.
245              
246             =item @names = $specs->arguments()
247              
248             =item @names = $specs->options()
249              
250             =item @names = $specs->tags()
251              
252             Get the argument, option, or tag names that were specified for the import.
253              
254             =item $hashref = $specs->argument_info()
255              
256             Get the arguments that were specified for the import. The key is the name of the
257             argument and the value is what the user supplied during import.
258              
259             =item $hashref = $specs->option_info()
260              
261             Get the options that were specified for the import. The key is the name of the user
262             supplied option and the value will evaluate to true.
263              
264             =item $hashref = $specs->tag_info()
265              
266             Get the values associated with the tags used during import. The key is the name of the tag
267             and the value is an array ref containing the values given to export_tag() for the associated
268             name.
269              
270             =item $hashref = $specs->exports()
271              
272             Get the exports hash. The keys are names of the exports. Values are an array
273             containing the export, item specific config hash, and arguments array. This is
274             generally not intended for direct consumption.
275              
276             =item $arrayref = $specs->excludes()
277              
278             Get the arrayref containing the names of all excluded exports.
279              
280             =item $specs->export( $package )
281              
282             Do the actual exporting. All exports will be injected into $package.
283              
284             =item $specs->add_export( $name, $value )
285              
286             =item $specs->add_export( $name, $value, \%config )
287              
288             Add an export. Name is required, including sigil. Value is required, if it is a
289             sub it will be blessed as a ::Sub, otherwise blessed as a ::Variable.
290              
291             $specs->add_export( '&foo' => sub { return 'foo' });
292              
293             =back
294              
295             =head1 AUTHORS
296              
297             Chad Granum L
298              
299             =head1 COPYRIGHT
300              
301             Copyright (C) 2010 Chad Granum
302              
303             Exporter-Declare is free software; Standard perl licence.
304              
305             Exporter-Declare is distributed in the hope that it will be useful, but
306             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
307             FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.