File Coverage

blib/lib/Module/Overview.pm
Criterion Covered Total %
statement 118 154 76.6
branch 25 48 52.0
condition 9 35 25.7
subroutine 15 16 93.7
pod 4 4 100.0
total 171 257 66.5


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   61915 use warnings;
  1         2  
  1         27  
25 1     1   5 use strict;
  1         2  
  1         33  
26              
27             our $VERSION = '0.01';
28              
29 1     1   21 use 5.010;
  1         6  
  1         40  
30              
31 1     1   887 use Class::Sniff;
  1         262625  
  1         45  
32 1     1   13 use Text::SimpleTable;
  1         4  
  1         23  
33 1     1   1724 use Module::ExtractUse;
  1         173058  
  1         36  
34 1     1   10 use Graph::Easy;
  1         3  
  1         25  
35 1     1   5 use Carp 'confess';
  1         2  
  1         56  
36              
37 1     1   5 use base 'Class::Accessor::Fast';
  1         1  
  1         776  
38              
39             __PACKAGE__->mk_accessors(qw{
40             module_name
41             recursive
42             recursion_filter
43             hide_methods
44             });
45              
46             sub new {
47 2     2 1 47483 my $class = shift;
48 2         36 my $self = $class->SUPER::new(@_);
49            
50 2 50       35 confess('module_name is mandatory property')
51             if not $self->module_name;
52            
53 2         29 return $self;
54             }
55              
56             sub get {
57 2     2 1 4 my $self = shift;
58 2   33     8 my $module_name = shift || $self->{'module_name'};
59            
60 2         4 my $recursion_filter = $self->{'recursion_filter'};
61              
62 2         5 my %overview;
63            
64 1     1   11 eval qq{ use $module_name };
  1     1   2  
  1         12  
  1         10  
  1         2  
  1         12  
  2         231  
65 2 50       40 warn 'error loading "'.$module_name.'" - '.$@ if $@;
66              
67 2         24 my $sniff = Class::Sniff->new({class => $module_name});
68 2         1639034 my $euse = Module::ExtractUse->new;
69              
70             #my $graph = $sniff->graph; # Graph::Easy
71             #print $sniff->report;
72             #print join("\n", $sniff->methods), "\n";
73 2         25 $overview{'class'} = $module_name;
74 2         618 $overview{'parents'} = [
75 2         39 grep { not ($_ ~~ [qw(Exporter)]) } # skip uninteresting
76 2         10 grep { $_ !~ m{^[0-9._]+$} } # skip perl versions
77             $sniff->parents
78             ];
79 2         13 delete $overview{'parents'}
80 2 50       6 if not @{$overview{'parents'}};
81 5         18 $overview{'classes'} = [
82 5         20 grep { not ($_ ~~ $overview{'parents'}) } # skip parents
83 5         16 grep { not ($_ ~~ [qw(Exporter)]) } # skip uninteresting
84 7         25 grep { $_ !~ m{^[0-9._]+$} } # skip perl versions
85 2         12 grep { $_ ne $module_name } # skip self
86             $sniff->classes
87             ];
88 2         10 delete $overview{'classes'}
89 2 50       6 if not @{$overview{'classes'}};
90            
91 2         8 my $module_name_path = $module_name.'.pm';
92 2         11 $module_name_path =~ s{::}{/}g;
93 2 50 33     151 if (exists $INC{$module_name_path} and (-r $INC{$module_name_path})) {
94 2         14 $euse->extract_use($INC{$module_name_path});
95 2         75086 $DB::single=1;
96 5 50       19 $overview{'uses'} = [
97 7         17 grep { (not $recursion_filter) or ($_ =~ m/$recursion_filter/) } # filter modules
98 9         34 grep { not ($_ ~~ $overview{'parents'}) } # skip parents
99 10         80 grep { not ($_ ~~ [qw(strict warnings constant vars Exporter)]) } # skip uninteresting
100 2         13 grep { $_ !~ m{^[0-9._]+$} } # skip perl versions
101             sort
102             $euse->array
103             ];
104 2         11 delete $overview{'uses'}
105 2 100       4 if not @{$overview{'uses'}};
106             }
107              
108 2         3 my (@methods, @methods_imported);
109 2         4 while (my ($method, $classes) = each %{$sniff->{methods}}) {
  60         191  
110 58         59 my $class = ${$classes}[0];
  58         87  
111 58 100       165 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   5720 my $glob = do { no strict 'refs'; \*{$class.'::'.$method} };
  1         2  
  1         816  
  58         60  
  58         59  
  58         172  
116 58         168 my $o = B::svref_2object($glob);
117             # in 5.005 this flag is not exposed via B, though it exists
118 58   50     65 my $imported_cv = eval { B::GVf_IMPORTED_CV() } || 0x80;
119 58         126 my $imported = $o->GvFLAGS & $imported_cv;
120              
121 58 100       108 if ($imported) {
122 18         27 push @methods_imported, $method_desc;
123 18         38 next;
124             }
125            
126 40         103 push @methods, $method_desc;
127             }
128 2 100 66     47 $overview{'methods'} = [ sort @methods ]
129             if @methods and (not $self->{'hide_methods'});
130 2 100 66     21 $overview{'methods_imported'} = [ sort @methods_imported ]
131             if @methods_imported and (not $self->{'hide_methods'});
132            
133 2         77 return \%overview;
134             }
135              
136             sub text_simpletable {
137 2     2 1 2148 my $self = shift;
138 2   33     17 my $module_name = shift || $self->{'module_name'};
139            
140 2         11 my $module_overview = $self->get($module_name);
141 2         358 my $table = Text::SimpleTable->new(16, 60);
142              
143 2         93 $table->row('class', $module_overview->{'class'});
144 2 50 33     174 if ($module_overview->{'parents'} || $module_overview->{'classes'}) {
145 2         10 $table->hr;
146             }
147 2 50       37 if ($module_overview->{'parents'}) {
148 2         5 $table->row('parents', join("\n", @{$module_overview->{'parents'}}));
  2         11  
149             }
150 2 50       136 if ($module_overview->{'classes'}) {
151 2         4 $table->row('classes', join("\n", @{$module_overview->{'classes'}}));
  2         11  
152             }
153 2 100       124 if ($module_overview->{'uses'}) {
154 1         4 $table->hr;
155 1         11 $table->row('uses', join("\n", @{$module_overview->{'uses'}}));
  1         5  
156             }
157 2 100       82 if ($module_overview->{'methods'}) {
158 1         5 $table->hr;
159 1         14 $table->row('methods', join("\n", @{$module_overview->{'methods'}}));
  1         10  
160             }
161 2 100       161 if ($module_overview->{'methods_imported'}) {
162 1         3 $table->hr;
163 1         11 $table->row('methods_imported', join("\n", @{$module_overview->{'methods_imported'}}));
  1         5  
164             }
165 2         98 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 0 0 0       $self->graph($parent, $graph)
197             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 0 0 0       $self->graph($use, $graph)
217             if ($e and $self->{'recursive'});
218             }
219             }
220 0 0         if ($module_overview->{'methods'}) {
221 0           my $module_name_methods = $module_name.' methods';
222 0           $graph->add_node($module_name_methods)->set_attributes({
223 0           'label' => join('\n', @{$module_overview->{'methods'}}),
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 0           $graph->add_node($module_name_methods)->set_attributes({
233 0           'label' => join('\n', @{$module_overview->{'methods_imported'}}),
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__