/[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 1137 - (show annotations)
Tue Jun 30 19:47:10 2009 UTC (14 years, 10 months ago) by dpavlin
File size: 4773 byte(s)
show use with no combination as single dashed edge
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 sub introspect_path { 'var/introspect/' };
104
105 sub as_markup {
106 my ($self) = @_;
107
108 my $rankdir = $self->portrait;
109
110 my $g = GraphViz->new(
111 rankdir => $rankdir,
112 layout => $self->layout,
113 # layout => 'neato', # grabs too much memory
114 # layout => 'twopi', # grabs too much memory
115 # overlap => 'compress',
116 no_overlap => $self->no_overlap,
117
118 node => {
119 shape => 'box',
120 style =>'filled',
121 color => 'grey',
122 fillcolor =>'lightgray',
123 fontname => 'verdana',
124 fontsize => '12',
125
126 },
127 edge => {
128 color => 'grey',
129 fontname => 'verdana',
130 fontsize => '8',
131 fontcolor => 'grey',
132 }
133 );
134
135 my $count;
136 my $filter = $self->filter;
137
138 foreach my $path ( $self->dir_extension( $self->introspect_path, qr{\.(ya?ml)$}) ) {
139
140 my $class = $self->strip_path_extension( $path ) || die "can't strip $path";
141
142 $count->{$class}++ if $self->show_disconnected;
143
144 my $data = $self->load( $path );
145 # warn "## $class $path ", $self->dump( $data ); # if $self->debug;
146
147 next if $filter && $self->filter_class && $class !~ m{$filter};
148
149 if ( $self->show_includes && defined $data->{includes} ) {
150
151 my $edge;
152
153 foreach my $type ( keys %{ $data->{includes} } ) {
154 foreach my $package ( @{ $data->{includes}->{$type} } ) {
155 next if $filter && $self->filter_includes && $package !~ m{$filter};
156 warn "# $class\t$type\t$package\n";
157 my $e = "$class $package";
158 if ( $edge->{$e} ) {
159 $edge->{$e}->{style} = 'dashed';
160 $edge->{$e}->{label} .= "\n$type";
161 } else {
162 $edge->{$e} = {
163 color => 'blue',
164 label => $type,
165 };
166 }
167 $count->{$class}++;
168 $count->{$package}++;
169 }
170 }
171
172 foreach my $e ( keys %$edge ) {
173 my ($c,$p) = split(/\s/, $e);
174 $g->add_edge( $c => $p, %{ $edge->{$e} } )
175 }
176
177 }
178
179 if ( $self->show_roles && defined $data->{roles} ) {
180 foreach my $role ( keys %{ $data->{roles} } ) {
181 next if $filter && $self->filter_roles && $role !~ m{$filter};
182 warn "# $class\trole\t$role\n";
183 $g->add_edge( $role => $class, label => 'with', color => 'yellow' );
184 $g->add_node( $role, shape => 'diamond' );
185 $count->{$class}++;
186 $count->{$role}++;
187 }
188 }
189
190 if ( $self->show_extends && defined $data->{superclass} ) {
191 foreach my $extends ( keys %{ $data->{superclass} } ) {
192 next if $filter && $self->filter_extends && $extends !~ m{$filter};
193 warn "# $class\textends\t$extends\n";
194 $g->add_edge( $extends => $class, label => 'extends', color => 'green' );
195 $count->{$class}++;
196 $count->{$extends}++;
197 }
198 }
199
200 }
201
202 warn "# count ",$self->dump( $count );
203
204 my $max_count = 1;
205 foreach ( keys %$count ) {
206 my $v = $count->{$_};
207 $max_count = $v if $v > $max_count;
208 }
209 warn "# max_count: $max_count";
210
211 foreach my $node ( keys %$count ) {
212 my $v = $count->{$node};
213 my $pcnt = $v / $max_count;
214 my $color = join(",", ( $pcnt, $pcnt, 0.75 ) );
215
216 $g->add_node( $node,
217 style =>'filled',
218 color => $color,
219 fillcolor => $color,
220 # label => "$node\n$v",
221 );
222
223 }
224
225 if ( $self->produce_dot ) {
226 $self->content_type( 'text/plain' );
227 $self->store( 'var/classes.dot', $g->as_canon );
228 return $g->as_canon;
229 }
230
231 $self->content_type( 'image/png' );
232 return $g->as_png;
233
234 }
235
236 __PACKAGE__->meta->make_immutable;
237 no Moose;
238
239 1;

  ViewVC Help
Powered by ViewVC 1.1.26