--- trunk/lib/Frey/Class/Graph.pm 2009/01/09 16:21:26 970 +++ trunk/lib/Frey/Class/Graph.pm 2009/01/10 01:03:28 981 @@ -3,29 +3,167 @@ extends 'Frey'; with 'Frey::Web'; -#with 'Frey::Storage'; +with 'Frey::File'; +with 'Frey::Storage'; use GraphViz; -has skeleton => ( +has filter => ( + documentation => 'Regex to select classes', is => 'rw', isa => 'Str', required => 1, - default => 'skeleton', + default => 'Frey', ); +has show_extends => ( + documentation => 'connect to superclasses', + is => 'rw', + isa => 'Bool', +); + +has show_includes => ( + documentation => 'use and require connections', + is => 'rw', + isa => 'Bool', + default => 1, +); + +has show_roles => ( + documentation => 'roles consumers connections', + is => 'rw', + isa => 'Bool', +); + +has show_disconnected => ( + is => 'ro', + isa => 'Bool', +); + +has portrait => ( + documentation => 'vertical layout', + is => 'rw', + isa => 'Bool', +); + +has produce_dot => ( + documentation => 'dump .dot text format', + is => 'rw', + isa => 'Bool', +); + +sub introspect_path { 'var/introspect/' }; + sub as_markup { my ($self) = @_; - my $g = GraphViz->new(); + my $rankdir = $self->portrait; - $g->add_node( 'foo' ); - $g->add_node( 'bar' ); - - $g->add_edge( 'foo' => 'bar' ); + my $g = GraphViz->new( + rankdir => $rankdir, +# layout => 'neato', # grabs too much memory +# layout => 'twopi', # grabs too much memory +# overlap => 'compress', +# no_overlap => 1, + + node => { + shape => 'box', + style =>'filled', + color => 'grey', + fillcolor =>'lightgray', + fontname => 'verdana', + fontsize => '12', + + }, + edge => { + color => 'grey', + fontname => 'verdana', + fontsize => '8', + fontcolor => 'grey', + } + ); + + my $count; + my $filter = $self->filter; + + foreach my $path ( $self->dir_extension( $self->introspect_path, qr{\.(ya?ml)$}) ) { + + my $class = $self->strip_path_extension( $path ) || die "can't strip $path"; + + $count->{$class}++ if $self->show_disconnected; + + my $data = $self->load( $path ); +# warn "## $class $path ", $self->dump( $data ); # if $self->debug; + + next if $filter && $class !~ m{$filter}; + + if ( $self->show_includes && defined $data->{includes} ) { + foreach my $type ( keys %{ $data->{includes} } ) { + foreach my $package ( @{ $data->{includes}->{$type} } ) { + next if $filter && $package !~ m{$filter}; + warn "# $class\t$type\t$package\n"; + $g->add_edge( $class => $package, label => $type, color => 'blue' ); + $count->{$class}++; + $count->{$package}++; + } + } + } + + if ( $self->show_roles && defined $data->{roles} ) { + foreach my $role ( keys %{ $data->{roles} } ) { + next if $filter && $role !~ m{$filter}; + warn "# $class\trole\t$role\n"; + $g->add_edge( $role => $class, label => 'role', color => 'yellow' ); +# $g->add_node( $role, rank => 'role' ); + $count->{$class}++; + $count->{$role}++; + } + } + + if ( $self->show_extends && defined $data->{superclass} ) { + foreach my $extends ( keys %{ $data->{superclass} } ) { + next if $filter && $extends !~ m{$filter}; + warn "# $class\textends\t$extends\n"; + $g->add_edge( $extends => $class, label => 'extends', color => 'green' ); + $count->{$class}++; + $count->{$extends}++; + } + } + + } + + warn "# count ",$self->dump( $count ); + + my $max_count = 1; + foreach ( keys %$count ) { + my $v = $count->{$_}; + $max_count = $v if $v > $max_count; + } + warn "# max_count: $max_count"; + + foreach my $node ( keys %$count ) { + my $v = $count->{$node}; + my $pcnt = $v / $max_count; + my $color = join(",", ( $pcnt, $pcnt, 0.75 ) ); + + $g->add_node( $node, + style =>'filled', + color => $color, + fillcolor => $color, +# label => "$node\n$v", + ); + + } + + if ( $self->produce_dot ) { + $self->content_type( 'text/plain' ); + $self->store( 'var/classes.dot', $g->as_canon ); + return $g->as_canon; + } $self->content_type( 'image/png' ); return $g->as_png; + } 1;