line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Module::Overview; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Module::Overview - print/graph module(s) information |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Module::Overview; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my $mo = Module::Overview->new({ |
12
|
|
|
|
|
|
|
'module_name' => 'Module::Overview', |
13
|
|
|
|
|
|
|
}); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
print $mo->text_simpletable; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $graph = $mo->graph; # Graph::Easy |
18
|
|
|
|
|
|
|
open my $DOT, '|dot -Tpng -o graph.png' or die ("Cannot open pipe to dot: $!"); |
19
|
|
|
|
|
|
|
print $DOT $graph->as_graphviz; |
20
|
|
|
|
|
|
|
close $DOT; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=cut |
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
86940
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
36
|
|
25
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
32
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
|
586
|
use Class::Sniff; |
|
1
|
|
|
|
|
145300
|
|
|
1
|
|
|
|
|
32
|
|
30
|
1
|
|
|
1
|
|
8
|
use Text::SimpleTable; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
17
|
|
31
|
1
|
|
|
1
|
|
477
|
use Module::ExtractUse; |
|
1
|
|
|
|
|
135343
|
|
|
1
|
|
|
|
|
38
|
|
32
|
1
|
|
|
1
|
|
10
|
use Graph::Easy; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
29
|
|
33
|
1
|
|
|
1
|
|
6
|
use Carp 'confess'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
34
|
1
|
|
|
1
|
|
6
|
use List::MoreUtils qw(none); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
15
|
|
35
|
|
|
|
|
|
|
|
36
|
1
|
|
|
1
|
|
1093
|
use base 'Class::Accessor::Fast'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
570
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(qw{ |
39
|
|
|
|
|
|
|
module_name |
40
|
|
|
|
|
|
|
recursive |
41
|
|
|
|
|
|
|
recursion_filter |
42
|
|
|
|
|
|
|
hide_methods |
43
|
|
|
|
|
|
|
}); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub new { |
46
|
2
|
|
|
2
|
1
|
7766
|
my $class = shift; |
47
|
2
|
|
|
|
|
23
|
my $self = $class->SUPER::new(@_); |
48
|
|
|
|
|
|
|
|
49
|
2
|
50
|
|
|
|
78
|
confess('module_name is mandatory property') |
50
|
|
|
|
|
|
|
if not $self->module_name; |
51
|
|
|
|
|
|
|
|
52
|
2
|
|
|
|
|
25
|
return $self; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub get { |
56
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
57
|
2
|
|
33
|
|
|
7
|
my $module_name = shift || $self->{'module_name'}; |
58
|
|
|
|
|
|
|
|
59
|
2
|
|
|
|
|
5
|
my $recursion_filter = $self->{'recursion_filter'}; |
60
|
|
|
|
|
|
|
|
61
|
2
|
|
|
|
|
3
|
my %overview; |
62
|
|
|
|
|
|
|
|
63
|
1
|
|
|
1
|
|
25
|
eval qq{ use $module_name }; |
|
1
|
|
|
1
|
|
7
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
168
|
|
64
|
2
|
50
|
|
|
|
47
|
warn 'error loading "'.$module_name.'" - '.$@ if $@; |
65
|
|
|
|
|
|
|
|
66
|
2
|
|
|
|
|
18
|
my $sniff = Class::Sniff->new({class => $module_name}); |
67
|
2
|
|
|
|
|
1605917
|
my $euse = Module::ExtractUse->new; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
#my $graph = $sniff->graph; # Graph::Easy |
70
|
|
|
|
|
|
|
#print $sniff->report; |
71
|
|
|
|
|
|
|
#print join("\n", $sniff->methods), "\n"; |
72
|
2
|
|
|
|
|
22
|
$overview{'class'} = $module_name; |
73
|
|
|
|
|
|
|
$overview{'parents'} = [ |
74
|
2
|
|
|
|
|
8
|
grep { $_ ne 'Exporter' } # skip uninteresting |
75
|
2
|
|
|
|
|
10
|
grep { $_ !~ m{^[0-9._]+$} } # skip perl versions |
|
2
|
|
|
|
|
38
|
|
76
|
|
|
|
|
|
|
$sniff->parents |
77
|
|
|
|
|
|
|
]; |
78
|
|
|
|
|
|
|
delete $overview{'parents'} |
79
|
2
|
50
|
|
|
|
6
|
if not @{$overview{'parents'}}; |
|
2
|
|
|
|
|
9
|
|
80
|
|
|
|
|
|
|
$overview{'classes'} = [ |
81
|
5
|
|
|
5
|
|
11
|
grep { my $s = $_; none { $_ eq $s } @{$overview{'parents'}} } # skip parents |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
22
|
|
|
5
|
|
|
|
|
18
|
|
82
|
5
|
|
|
|
|
11
|
grep { $_ ne 'Exporter' } # skip uninteresting |
83
|
5
|
|
|
|
|
14
|
grep { $_ !~ m{^[0-9._]+$} } # skip perl versions |
84
|
2
|
|
|
|
|
8
|
grep { $_ ne $module_name } # skip self |
|
7
|
|
|
|
|
23
|
|
85
|
|
|
|
|
|
|
$sniff->classes |
86
|
|
|
|
|
|
|
]; |
87
|
|
|
|
|
|
|
delete $overview{'classes'} |
88
|
2
|
50
|
|
|
|
5
|
if not @{$overview{'classes'}}; |
|
2
|
|
|
|
|
8
|
|
89
|
|
|
|
|
|
|
|
90
|
2
|
|
|
|
|
6
|
my $module_name_path = $module_name.'.pm'; |
91
|
2
|
|
|
|
|
10
|
$module_name_path =~ s{::}{/}g; |
92
|
2
|
50
|
33
|
|
|
114
|
if (exists $INC{$module_name_path} and (-r $INC{$module_name_path})) { |
93
|
2
|
|
|
|
|
20
|
$euse->extract_use($INC{$module_name_path}); |
94
|
2
|
|
|
|
|
62597
|
$DB::single=1; |
95
|
2
|
|
|
|
|
9
|
my %skip_kw = map {$_ => 1} qw(strict warnings constant vars Exporter); |
|
10
|
|
|
|
|
28
|
|
96
|
|
|
|
|
|
|
$overview{'uses'} = [ |
97
|
6
|
50
|
|
|
|
22
|
grep { (not $recursion_filter) or ($_ =~ m/$recursion_filter/) } # filter modules |
98
|
8
|
|
|
8
|
|
13
|
grep { my $s = $_; none { $_ eq $s } @{$overview{'parents'}} } # skip parents |
|
8
|
|
|
|
|
22
|
|
|
8
|
|
|
|
|
28
|
|
|
8
|
|
|
|
|
22
|
|
99
|
10
|
|
|
|
|
19
|
grep { !$skip_kw{$_} } # skip uninteresting |
100
|
2
|
|
|
|
|
13
|
grep { $_ !~ m{^[0-9._]+$} } # skip perl versions |
|
10
|
|
|
|
|
44
|
|
101
|
|
|
|
|
|
|
sort |
102
|
|
|
|
|
|
|
$euse->array |
103
|
|
|
|
|
|
|
]; |
104
|
|
|
|
|
|
|
delete $overview{'uses'} |
105
|
2
|
100
|
|
|
|
7
|
if not @{$overview{'uses'}}; |
|
2
|
|
|
|
|
9
|
|
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
2
|
|
|
|
|
5
|
my (@methods, @methods_imported); |
109
|
2
|
|
|
|
|
4
|
while (my ($method, $classes) = each %{$sniff->{methods}}) { |
|
64
|
|
|
|
|
183
|
|
110
|
62
|
|
|
|
|
79
|
my $class = ${$classes}[0]; |
|
62
|
|
|
|
|
114
|
|
111
|
62
|
100
|
|
|
|
169
|
my $method_desc = $method.'()'.($class ne $module_name ? ' ['.$class.']' : ''); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# source - Pod::Coverage _get_syms() |
114
|
|
|
|
|
|
|
# see if said method wasn't just imported from elsewhere |
115
|
1
|
|
|
1
|
|
3176
|
my $glob = do { no strict 'refs'; \*{$class.'::'.$method} }; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
827
|
|
|
62
|
|
|
|
|
78
|
|
|
62
|
|
|
|
|
73
|
|
|
62
|
|
|
|
|
190
|
|
116
|
62
|
|
|
|
|
140
|
my $o = B::svref_2object($glob); |
117
|
|
|
|
|
|
|
# in 5.005 this flag is not exposed via B, though it exists |
118
|
62
|
|
50
|
|
|
93
|
my $imported_cv = eval { B::GVf_IMPORTED_CV() } || 0x80; |
119
|
62
|
|
|
|
|
119
|
my $imported = $o->GvFLAGS & $imported_cv; |
120
|
|
|
|
|
|
|
|
121
|
62
|
100
|
|
|
|
112
|
if ($imported) { |
122
|
22
|
|
|
|
|
28
|
push @methods_imported, $method_desc; |
123
|
22
|
|
|
|
|
48
|
next; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
40
|
|
|
|
|
93
|
push @methods, $method_desc; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
$overview{'methods'} = [ sort @methods ] |
129
|
2
|
100
|
66
|
|
|
43
|
if @methods and (not $self->{'hide_methods'}); |
130
|
|
|
|
|
|
|
$overview{'methods_imported'} = [ sort @methods_imported ] |
131
|
2
|
100
|
66
|
|
|
15
|
if @methods_imported and (not $self->{'hide_methods'}); |
132
|
|
|
|
|
|
|
|
133
|
2
|
|
|
|
|
73
|
return \%overview; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub text_simpletable { |
137
|
2
|
|
|
2
|
1
|
2066
|
my $self = shift; |
138
|
2
|
|
33
|
|
|
12
|
my $module_name = shift || $self->{'module_name'}; |
139
|
|
|
|
|
|
|
|
140
|
2
|
|
|
|
|
7
|
my $module_overview = $self->get($module_name); |
141
|
2
|
|
|
|
|
287
|
my $table = Text::SimpleTable->new(16, 60); |
142
|
|
|
|
|
|
|
|
143
|
2
|
|
|
|
|
124
|
$table->row('class', $module_overview->{'class'}); |
144
|
2
|
50
|
33
|
|
|
242
|
if ($module_overview->{'parents'} || $module_overview->{'classes'}) { |
145
|
2
|
|
|
|
|
9
|
$table->hr; |
146
|
|
|
|
|
|
|
} |
147
|
2
|
50
|
|
|
|
33
|
if ($module_overview->{'parents'}) { |
148
|
2
|
|
|
|
|
4
|
$table->row('parents', join("\n", @{$module_overview->{'parents'}})); |
|
2
|
|
|
|
|
10
|
|
149
|
|
|
|
|
|
|
} |
150
|
2
|
50
|
|
|
|
172
|
if ($module_overview->{'classes'}) { |
151
|
2
|
|
|
|
|
5
|
$table->row('classes', join("\n", @{$module_overview->{'classes'}})); |
|
2
|
|
|
|
|
9
|
|
152
|
|
|
|
|
|
|
} |
153
|
2
|
100
|
|
|
|
178
|
if ($module_overview->{'uses'}) { |
154
|
1
|
|
|
|
|
4
|
$table->hr; |
155
|
1
|
|
|
|
|
14
|
$table->row('uses', join("\n", @{$module_overview->{'uses'}})); |
|
1
|
|
|
|
|
5
|
|
156
|
|
|
|
|
|
|
} |
157
|
2
|
100
|
|
|
|
168
|
if ($module_overview->{'methods'}) { |
158
|
1
|
|
|
|
|
5
|
$table->hr; |
159
|
1
|
|
|
|
|
14
|
$table->row('methods', join("\n", @{$module_overview->{'methods'}})); |
|
1
|
|
|
|
|
7
|
|
160
|
|
|
|
|
|
|
} |
161
|
2
|
100
|
|
|
|
240
|
if ($module_overview->{'methods_imported'}) { |
162
|
1
|
|
|
|
|
3
|
$table->hr; |
163
|
1
|
|
|
|
|
15
|
$table->row('methods_imported', join("\n", @{$module_overview->{'methods_imported'}})); |
|
1
|
|
|
|
|
6
|
|
164
|
|
|
|
|
|
|
} |
165
|
2
|
|
|
|
|
171
|
return $table->draw; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub graph { |
169
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
170
|
0
|
|
0
|
|
|
|
my $module_name = shift || $self->{'module_name'}; |
171
|
0
|
|
0
|
|
|
|
my $graph = shift || Graph::Easy->new(); |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
my $recursion_filter = $self->{'recursion_filter'}; |
174
|
0
|
0
|
0
|
|
|
|
return $graph |
175
|
|
|
|
|
|
|
if ($recursion_filter and ($module_name !~ m/$recursion_filter/)); |
176
|
|
|
|
|
|
|
|
177
|
0
|
|
|
|
|
|
my $module_overview = $self->get($module_name); |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
$graph->add_node($module_name)->set_attributes({'font-size' => '150%', 'textstyle' => 'bold', 'fill' => 'lightgrey'}); |
180
|
0
|
0
|
|
|
|
|
if ($module_overview->{'parents'}) { |
181
|
0
|
|
|
|
|
|
my $module_name_parent = $module_name.' parent'; |
182
|
0
|
|
|
|
|
|
$graph->add_node($module_name_parent)->set_attributes({ |
183
|
|
|
|
|
|
|
'label' => 'parent', |
184
|
|
|
|
|
|
|
'shape' => 'ellipse', |
185
|
|
|
|
|
|
|
'font-size' => '75%', |
186
|
|
|
|
|
|
|
}); |
187
|
0
|
|
|
|
|
|
$graph->add_edge_once($module_name => $module_name_parent); |
188
|
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
|
foreach my $parent (@{$module_overview->{'parents'}}) { |
|
0
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
$graph->add_node($parent); |
191
|
|
|
|
|
|
|
|
192
|
0
|
|
|
|
|
|
my $e = $graph->add_edge_once($module_name_parent, $parent); |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#my $e = $graph->add_edge_once($module_name, $parent, 'parent'); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$self->graph($parent, $graph) |
197
|
0
|
0
|
0
|
|
|
|
if ($e and $self->{'recursive'}); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
0
|
0
|
|
|
|
|
if ($module_overview->{'uses'}) { |
201
|
0
|
|
|
|
|
|
my $module_name_use = $module_name.' use'; |
202
|
0
|
|
|
|
|
|
$graph->add_node($module_name_use)->set_attributes({ |
203
|
|
|
|
|
|
|
'label' => 'use', |
204
|
|
|
|
|
|
|
'shape' => 'ellipse', |
205
|
|
|
|
|
|
|
'font-size' => '75%', |
206
|
|
|
|
|
|
|
}); |
207
|
0
|
|
|
|
|
|
$graph->add_edge_once($module_name => $module_name_use); |
208
|
|
|
|
|
|
|
|
209
|
0
|
|
|
|
|
|
foreach my $use (@{$module_overview->{'uses'}}) { |
|
0
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
$graph->add_node($use); |
211
|
|
|
|
|
|
|
|
212
|
0
|
|
|
|
|
|
my $e = $graph->add_edge_once($module_name_use, $use); |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
#my $e = $graph->add_edge_once($module_name, $use, 'use'); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
$self->graph($use, $graph) |
217
|
0
|
0
|
0
|
|
|
|
if ($e and $self->{'recursive'}); |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
0
|
0
|
|
|
|
|
if ($module_overview->{'methods'}) { |
221
|
0
|
|
|
|
|
|
my $module_name_methods = $module_name.' methods'; |
222
|
|
|
|
|
|
|
$graph->add_node($module_name_methods)->set_attributes({ |
223
|
0
|
|
|
|
|
|
'label' => join('\n', @{$module_overview->{'methods'}}), |
|
0
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
'font-size' => '75%', |
225
|
|
|
|
|
|
|
'align' => 'left', |
226
|
|
|
|
|
|
|
'borderstyle' => 'dashed', |
227
|
|
|
|
|
|
|
}); |
228
|
0
|
|
|
|
|
|
$graph->add_edge_once($module_name => $module_name_methods, 'methods'); |
229
|
|
|
|
|
|
|
} |
230
|
0
|
0
|
|
|
|
|
if ($module_overview->{'methods_imported'}) { |
231
|
0
|
|
|
|
|
|
my $module_name_methods = $module_name.' methods_imported'; |
232
|
|
|
|
|
|
|
$graph->add_node($module_name_methods)->set_attributes({ |
233
|
0
|
|
|
|
|
|
'label' => join('\n', @{$module_overview->{'methods_imported'}}), |
|
0
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
'font-size' => '75%', |
235
|
|
|
|
|
|
|
'align' => 'left', |
236
|
|
|
|
|
|
|
'borderstyle' => 'dashed', |
237
|
|
|
|
|
|
|
}); |
238
|
0
|
|
|
|
|
|
$graph->add_edge_once($module_name => $module_name_methods, 'methods imported'); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
return $graph; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
'OV?'; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
__END__ |