/[Frey]/trunk/lib/Frey/Class/Graph.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/Frey/Class/Graph.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1156 - (show annotations)
Thu Jul 2 14:00:02 2009 UTC (14 years, 9 months ago) by dpavlin
File size: 5100 byte(s)
cluster classes
1 package Frey::Class::Graph;
2 use Moose;
3
4 extends 'Frey';
5 with 'Frey::Web', 'Frey::File', 'Frey::Storage';
6
7 use GraphViz;
8
9 has filter => (
10 documentation => 'Regex to select classes',
11 is => 'rw',
12 isa => 'Str',
13 required => 1,
14 default => sub {
15 '(' . join('|', map { s{lib/}{}; $_ } sort grep { -d $_ } glob("lib/*") ) . ')'
16 },
17 );
18
19
20 has filter_class => (
21 is => 'rw',
22 isa => 'Bool',
23 default => 1,
24 );
25
26 has filter_extends => (
27 is => 'rw',
28 isa => 'Bool',
29 default => 1,
30 );
31
32 has filter_includes => (
33 is => 'rw',
34 isa => 'Bool',
35 default => 1,
36 );
37
38 has filter_roles => (
39 is => 'rw',
40 isa => 'Bool',
41 default => 1,
42 );
43
44
45 has show_extends => (
46 documentation => 'connect to superclasses',
47 is => 'rw',
48 isa => 'Bool',
49 );
50
51 has show_includes => (
52 documentation => 'use and require connections',
53 is => 'rw',
54 isa => 'Bool',
55 default => 1,
56 );
57
58 has show_roles => (
59 documentation => 'roles consumers connections',
60 is => 'rw',
61 isa => 'Bool',
62 );
63
64 has show_disconnected => (
65 is => 'ro',
66 isa => 'Bool',
67 );
68
69
70 has layout => (
71 documentation => 'layout algorithm',
72 is => 'rw',
73 isa => 'Str',
74 required => 1,
75 );
76
77 sub layout_available { q/
78 dot directed graph
79 neato spring model
80 twopi radial
81 circo circular
82 fdp force directed spring model
83 / }
84
85 has portrait => (
86 documentation => 'vertical layout',
87 is => 'rw',
88 isa => 'Bool',
89 );
90
91 has no_overlap => (
92 documentation => 'avoid overlaping nodes',
93 is => 'rw',
94 isa => 'Bool',
95 );
96
97 has produce_dot => (
98 documentation => 'dump .dot text format',
99 is => 'rw',
100 isa => 'Bool',
101 );
102
103 has clusters => (
104 documentation => 'cluster by classes',
105 is => 'rw',
106 isa => 'Bool',
107 );
108
109 sub introspect_path { 'var/introspect/' };
110
111 sub as_markup {
112 my ($self) = @_;
113
114 my $rankdir = $self->portrait;
115
116 my $g = GraphViz->new(
117 rankdir => $rankdir,
118 layout => $self->layout,
119 # layout => 'neato', # grabs too much memory
120 # layout => 'twopi', # grabs too much memory
121 # overlap => 'compress',
122 no_overlap => $self->no_overlap,
123
124 node => {
125 shape => 'box',
126 style =>'filled',
127 color => 'grey',
128 fillcolor =>'lightgray',
129 fontname => 'verdana',
130 fontsize => '12',
131
132 },
133 edge => {
134 color => 'grey',
135 fontname => 'verdana',
136 fontsize => '8',
137 fontcolor => 'grey',
138 }
139 );
140
141 my $count;
142 my $filter = $self->filter;
143
144 foreach my $path ( $self->dir_extension( $self->introspect_path, qr{\.(ya?ml)$}) ) {
145
146 my $class = $self->strip_path_extension( $path ) || die "can't strip $path";
147
148 $count->{$class}++ if $self->show_disconnected;
149
150 my $data = $self->load( $path );
151 # warn "## $class $path ", $self->dump( $data ); # if $self->debug;
152
153 next if $filter && $self->filter_class && $class !~ m{$filter};
154
155 if ( $self->show_includes && defined $data->{includes} ) {
156
157 my $edge;
158
159 foreach my $type ( keys %{ $data->{includes} } ) {
160 foreach my $package ( @{ $data->{includes}->{$type} } ) {
161 next if $filter && $self->filter_includes && $package !~ m{$filter};
162 warn "# $class\t$type\t$package\n";
163 my $e = "$class $package";
164 if ( $edge->{$e} ) {
165 $edge->{$e}->{style} = 'dashed';
166 $edge->{$e}->{label} .= "\n$type";
167 } else {
168 $edge->{$e} = {
169 color => 'blue',
170 label => $type,
171 };
172 }
173 $count->{$class}++;
174 $count->{$package}++;
175 }
176 }
177
178 foreach my $e ( keys %$edge ) {
179 my ($c,$p) = split(/\s/, $e);
180 $g->add_edge( $c => $p, %{ $edge->{$e} } )
181 }
182
183 }
184
185 if ( $self->show_roles && defined $data->{roles} ) {
186 foreach my $role ( keys %{ $data->{roles} } ) {
187 next if $filter && $self->filter_roles && $role !~ m{$filter};
188 warn "# $class\trole\t$role\n";
189 $g->add_edge( $role => $class, label => 'with', color => 'yellow' );
190 $g->add_node( $role, shape => 'diamond' );
191 $count->{$class}++;
192 $count->{$role}++;
193 }
194 }
195
196 if ( $self->show_extends && defined $data->{superclass} ) {
197 foreach my $extends ( keys %{ $data->{superclass} } ) {
198 next if $filter && $self->filter_extends && $extends !~ m{$filter};
199 warn "# $class\textends\t$extends\n";
200 $g->add_edge( $extends => $class, label => 'extends', color => 'green' );
201 $count->{$class}++;
202 $count->{$extends}++;
203 }
204 }
205
206 }
207
208 warn "# count ",$self->dump( $count );
209
210 my $max_count = 1;
211 foreach ( keys %$count ) {
212 my $v = $count->{$_};
213 $max_count = $v if $v > $max_count;
214 }
215 warn "# max_count: $max_count";
216
217 foreach my $node ( keys %$count ) {
218 my $v = $count->{$node};
219 my $pcnt = $v / $max_count;
220 my $color = join(",", ( $pcnt, $pcnt, 0.75 ) );
221
222 my @cluster;
223 if ( $self->clusters ) {
224 my $name = $1 if $node =~ m{^([^:]+)};
225 @cluster = ( 'cluster' => {
226 name => $name,
227 style => 'filled',
228 bgcolor => 'lightgrey',
229 color => 'lightgrey',
230 });
231 }
232
233 $g->add_node( $node,
234 style =>'filled',
235 color => $color,
236 fillcolor => $color,
237 # label => "$node\n$v",
238 @cluster,
239 );
240
241 }
242
243 if ( $self->produce_dot ) {
244 $self->content_type( 'text/plain' );
245 $self->store( 'var/classes.dot', $g->as_canon );
246 return $g->as_canon;
247 }
248
249 $self->content_type( 'image/png' );
250 return $g->as_png;
251
252 }
253
254 __PACKAGE__->meta->make_immutable;
255 no Moose;
256
257 1;

  ViewVC Help
Powered by ViewVC 1.1.26