| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Code::Style::Kit; |
|
2
|
2
|
|
|
2
|
|
95414
|
use strict; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
49
|
|
|
3
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
51
|
|
|
4
|
2
|
|
|
2
|
|
706
|
use Data::OptList; |
|
|
2
|
|
|
|
|
10300
|
|
|
|
2
|
|
|
|
|
13
|
|
|
5
|
2
|
|
|
2
|
|
875
|
use Import::Into; |
|
|
2
|
|
|
|
|
2567
|
|
|
|
2
|
|
|
|
|
47
|
|
|
6
|
2
|
|
|
2
|
|
11
|
use Carp; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
105
|
|
|
7
|
2
|
|
|
2
|
|
17
|
use mro (); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
46
|
|
|
8
|
2
|
|
|
2
|
|
724
|
use Package::Stash qw(get_all_symbols); |
|
|
2
|
|
|
|
|
8929
|
|
|
|
2
|
|
|
|
|
67
|
|
|
9
|
2
|
|
|
2
|
|
14
|
use Module::Runtime qw(use_module); |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
11
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
2
|
|
|
2
|
|
98
|
use constant DEBUG => $ENV{CODE_STYLE_KIT_DEBUG}; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
2396
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '1.0.1'; # VERSION |
|
14
|
|
|
|
|
|
|
# ABSTRACT: build composable bulk exporters |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub import { |
|
18
|
11
|
|
|
11
|
|
38642
|
my $class = shift; |
|
19
|
11
|
|
|
|
|
24
|
my $caller = caller(); |
|
20
|
|
|
|
|
|
|
|
|
21
|
11
|
|
|
|
|
49
|
my $self = $class->_new($caller,@_); |
|
22
|
11
|
|
|
|
|
40
|
$self->_export_features; |
|
23
|
9
|
|
|
|
|
393
|
return; |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub is_feature_requested { |
|
28
|
4
|
|
|
4
|
1
|
20
|
my ($self, $feature) = @_; |
|
29
|
4
|
|
|
|
|
17
|
return !! $self->{requested_features}{$feature}; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub also_export { |
|
34
|
1
|
|
|
1
|
1
|
18
|
my ($self, $feature, $args) = @_; |
|
35
|
1
|
|
50
|
|
|
6
|
local $self->{requested_features}{$feature} = $args || []; |
|
36
|
1
|
|
|
|
|
6
|
$self->_export_one_feature($feature, 1); |
|
37
|
1
|
|
|
|
|
2
|
return; |
|
38
|
|
|
|
|
|
|
} |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub maybe_also_export { |
|
42
|
5
|
|
|
5
|
1
|
22
|
my ($self, $feature, $args) = @_; |
|
43
|
5
|
|
100
|
|
|
29
|
local $self->{requested_features}{$feature} = $args || []; |
|
44
|
5
|
|
|
|
|
17
|
$self->_export_one_feature($feature, 0); |
|
45
|
5
|
|
|
|
|
11
|
return; |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# private constructor, invoked from C |
|
49
|
|
|
|
|
|
|
sub _new { |
|
50
|
11
|
|
|
11
|
|
27
|
my ($class,$caller,@args) = @_; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# get all the parts in C<@ISA> order |
|
53
|
11
|
|
|
|
|
42
|
my $parent_stashes = $class->_get_parent_stashes; |
|
54
|
|
|
|
|
|
|
# get the features they provide by default |
|
55
|
11
|
|
|
|
|
35
|
my %feature_set = map { $_ => [] } $class->_default_features($parent_stashes); |
|
|
8
|
|
|
|
|
56
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
11
|
|
|
|
|
59
|
my $args = Data::OptList::mkopt(\@args,{ |
|
58
|
|
|
|
|
|
|
moniker => $class, |
|
59
|
|
|
|
|
|
|
must_be => 'ARRAY', |
|
60
|
|
|
|
|
|
|
require_unique => 1, |
|
61
|
|
|
|
|
|
|
}); |
|
62
|
|
|
|
|
|
|
# interpret the import arguments |
|
63
|
11
|
|
|
|
|
549
|
for my $arg (@{$args}) { |
|
|
11
|
|
|
|
|
20
|
|
|
64
|
10
|
|
|
|
|
13
|
my ($key, $value) = @{$arg}; |
|
|
10
|
|
|
|
|
19
|
|
|
65
|
|
|
|
|
|
|
|
|
66
|
10
|
50
|
|
|
|
52
|
if ($key =~ /^-(\w+)$/) { |
|
|
|
50
|
|
|
|
|
|
|
67
|
0
|
0
|
|
|
|
0
|
if ($value) { |
|
68
|
0
|
|
|
|
|
0
|
croak "providing import arguments (@{$value}) when removing a feature ($1) makes no sense"; |
|
|
0
|
|
|
|
|
0
|
|
|
69
|
|
|
|
|
|
|
} |
|
70
|
0
|
|
|
|
|
0
|
print STDERR "$class - removing feature $1\n" if DEBUG; |
|
71
|
0
|
|
|
|
|
0
|
delete $feature_set{$1}; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
elsif ($key =~ /^\w+$/) { |
|
74
|
10
|
|
|
|
|
20
|
print STDERR "$class - adding feature $key\n" if DEBUG; |
|
75
|
10
|
|
100
|
|
|
42
|
$feature_set{$key} = $value // []; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
else { |
|
78
|
0
|
|
|
|
|
0
|
croak "malformed feature <$key> when importing $class"; |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# build the instance |
|
83
|
11
|
|
|
|
|
47
|
return bless { |
|
84
|
|
|
|
|
|
|
caller => $caller, |
|
85
|
|
|
|
|
|
|
feature_list => [ $class->_sort_features(keys %feature_set) ], |
|
86
|
|
|
|
|
|
|
requested_features => \%feature_set, |
|
87
|
|
|
|
|
|
|
exported_features => {}, |
|
88
|
|
|
|
|
|
|
# we save this, so ->_export_one_feature doesn't have to scan |
|
89
|
|
|
|
|
|
|
# the parts again |
|
90
|
|
|
|
|
|
|
parent_stashes => $parent_stashes, |
|
91
|
|
|
|
|
|
|
}, $class; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub _export_features { |
|
95
|
11
|
|
|
11
|
|
16
|
my ($self) = @_; |
|
96
|
|
|
|
|
|
|
|
|
97
|
11
|
|
|
|
|
14
|
for my $feature (@{$self->{feature_list}}) { |
|
|
11
|
|
|
|
|
30
|
|
|
98
|
15
|
|
|
|
|
49
|
$self->_export_one_feature($feature, 1); |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# all the magic is from here to the end |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# the actual exporting |
|
105
|
|
|
|
|
|
|
sub _export_one_feature { |
|
106
|
21
|
|
|
21
|
|
34
|
my ($self, $feature, $croak_if_not_implemented) = @_; |
|
107
|
21
|
|
|
|
|
31
|
my $class = ref($self); |
|
108
|
21
|
50
|
|
|
|
25
|
my @import_args = @{ $self->{requested_features}{$feature} || [] }; |
|
|
21
|
|
|
|
|
56
|
|
|
109
|
|
|
|
|
|
|
|
|
110
|
21
|
|
|
|
|
27
|
print STDERR "$class - exporting $feature to $self->{caller} with arguments (@import_args)\n" |
|
111
|
|
|
|
|
|
|
if DEBUG; |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# do nothing if we've exported it already |
|
114
|
21
|
50
|
|
|
|
41
|
return if $self->{exported_features}{$feature}; |
|
115
|
|
|
|
|
|
|
|
|
116
|
21
|
|
|
|
|
35
|
my $list_method = "feature_${feature}_export_list"; |
|
117
|
21
|
|
|
|
|
33
|
my $direct_method = "feature_${feature}_export"; |
|
118
|
|
|
|
|
|
|
|
|
119
|
21
|
|
|
|
|
29
|
my $arguments_method = "feature_${feature}_takes_arguments"; |
|
120
|
21
|
|
66
|
|
|
94
|
my $takes_arguments = $self->can($arguments_method) && $self->$arguments_method; |
|
121
|
21
|
100
|
100
|
|
|
60
|
if (@import_args && !$takes_arguments) { |
|
122
|
1
|
|
|
|
|
202
|
croak "feature $feature does not take arguments, but (@import_args) were provided"; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
20
|
|
|
|
|
28
|
my $provided = 0; |
|
126
|
|
|
|
|
|
|
# loop over the parts in @ISA order |
|
127
|
20
|
|
|
|
|
23
|
for my $parent_stash (@{$self->{parent_stashes}}) { |
|
|
20
|
|
|
|
|
34
|
|
|
128
|
104
|
|
|
|
|
179
|
my $parent_class = $parent_stash->name; |
|
129
|
104
|
|
|
|
|
103
|
my $method_ref; |
|
130
|
|
|
|
|
|
|
# does this part provide a *_export_list sub? |
|
131
|
104
|
100
|
|
|
|
555
|
if ($method_ref = $parent_stash->get_symbol("&$list_method")) { |
|
|
|
100
|
|
|
|
|
|
|
132
|
1
|
|
|
|
|
1
|
print STDERR " calling ${parent_class}->$list_method\n" |
|
133
|
|
|
|
|
|
|
if DEBUG; |
|
134
|
|
|
|
|
|
|
# import all the packages that the sub returns |
|
135
|
1
|
|
|
|
|
4
|
for my $module ($self->$method_ref) { |
|
136
|
1
|
|
|
|
|
7
|
use_module($module)->import::into($self->{caller}, @import_args); |
|
137
|
|
|
|
|
|
|
} |
|
138
|
1
|
|
|
|
|
612
|
$provided = 1; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
# does this part provide a *_export sub? |
|
141
|
|
|
|
|
|
|
elsif ($method_ref = $parent_stash->get_symbol("&$direct_method")) { |
|
142
|
17
|
|
|
|
|
20
|
print STDERR " calling ${parent_class}->$direct_method\n" |
|
143
|
|
|
|
|
|
|
if DEBUG; |
|
144
|
|
|
|
|
|
|
# call it and let it do whatever it needs to |
|
145
|
17
|
|
|
|
|
50
|
$self->$method_ref($self->{caller},@import_args); |
|
146
|
16
|
|
|
|
|
6376
|
$provided = 1; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# did we find the feature? |
|
151
|
19
|
100
|
|
|
|
42
|
if ($provided) { |
|
|
|
50
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# mark it as exported |
|
153
|
17
|
|
|
|
|
28
|
$self->{exported_features}{$feature} = 1; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
elsif ($croak_if_not_implemented) { |
|
156
|
|
|
|
|
|
|
# croak if asked to |
|
157
|
0
|
|
|
|
|
0
|
croak "feature <$feature> is not implemented by $class"; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
19
|
|
|
|
|
39
|
return; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# use the *_order subs |
|
164
|
|
|
|
|
|
|
sub _sort_features { |
|
165
|
11
|
|
|
11
|
|
25
|
my ($class, @features) = @_; |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my %feature_sort_key = map { |
|
168
|
11
|
|
|
|
|
19
|
my $method = "feature_${_}_order"; |
|
|
18
|
|
|
|
|
37
|
|
|
169
|
18
|
100
|
|
|
|
103
|
$_ => ( $class->can($method) ? $class->$method : 100 ) |
|
170
|
|
|
|
|
|
|
} @features; |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
@features = sort { |
|
173
|
11
|
|
|
|
|
38
|
$feature_sort_key{$a} <=> $feature_sort_key{$b} |
|
|
8
|
|
|
|
|
19
|
|
|
174
|
|
|
|
|
|
|
} @features; |
|
175
|
|
|
|
|
|
|
|
|
176
|
11
|
|
|
|
|
18
|
print "$class - sorted features: (@features)\n" if DEBUG; |
|
177
|
|
|
|
|
|
|
|
|
178
|
11
|
|
|
|
|
105
|
return @features; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
# use the *_default subs |
|
182
|
|
|
|
|
|
|
sub _default_features { |
|
183
|
11
|
|
|
11
|
|
20
|
my ($class, $parent_stashes) = @_; |
|
184
|
|
|
|
|
|
|
|
|
185
|
11
|
|
|
|
|
17
|
my @features; |
|
186
|
|
|
|
|
|
|
# loop over the parts in @ISA order |
|
187
|
11
|
|
|
|
|
13
|
for my $parent_stash (@{$parent_stashes}) { |
|
|
11
|
|
|
|
|
20
|
|
|
188
|
59
|
|
|
|
|
341
|
my @subs = $parent_stash->list_all_symbols('CODE'); |
|
189
|
59
|
|
|
|
|
100
|
for my $sub (@subs) { |
|
190
|
|
|
|
|
|
|
# we only care about sub names of this form |
|
191
|
286
|
100
|
|
|
|
490
|
next unless $sub =~ /^feature_(\w+)_default$/; |
|
192
|
|
|
|
|
|
|
|
|
193
|
32
|
|
|
|
|
55
|
my $feature = $1; |
|
194
|
32
|
|
|
|
|
98
|
my $is_default = $class->$sub; |
|
195
|
|
|
|
|
|
|
|
|
196
|
32
|
|
|
|
|
67
|
if (DEBUG) { |
|
197
|
|
|
|
|
|
|
my $parent_class = $parent_stash->name; |
|
198
|
|
|
|
|
|
|
print STDERR "$class - $parent_class provides $feature, by default ", |
|
199
|
|
|
|
|
|
|
($is_default ? 'enabled' : 'disabled' ), |
|
200
|
|
|
|
|
|
|
"\n"; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
32
|
100
|
|
|
|
71
|
push @features, $feature if $is_default; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
11
|
|
|
|
|
28
|
return @features; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub _get_parent_stashes { |
|
211
|
11
|
|
|
11
|
|
35
|
my ($class) = @_; |
|
212
|
|
|
|
|
|
|
|
|
213
|
11
|
|
33
|
|
|
47
|
$class = ref($class) || $class; |
|
214
|
11
|
|
|
|
|
16
|
return [ map { Package::Stash->new($_) } @{ mro::get_linear_isa($class) } ]; |
|
|
59
|
|
|
|
|
288
|
|
|
|
11
|
|
|
|
|
46
|
|
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
1; |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
__END__ |