| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package DBICx::AutoDoc; | 
| 2 | 1 |  |  | 1 |  | 13782 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 23 |  | 
| 3 | 1 |  |  | 1 |  | 3 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 37 |  | 
| 4 |  |  |  |  |  |  | our $VERSION = '0.08'; | 
| 5 | 1 |  |  | 1 |  | 3 | use base qw( Class::Accessor::Grouped ); | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 531 |  | 
| 6 | 1 |  |  | 1 |  | 8609 | use Carp qw( croak ); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 41 |  | 
| 7 | 1 |  |  | 1 |  | 489 | use Template; | 
|  | 1 |  |  |  |  | 17442 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 8 | 1 |  |  | 1 |  | 485 | use FindBin qw( ); | 
|  | 1 |  |  |  |  | 716 |  | 
|  | 1 |  |  |  |  | 20 |  | 
| 9 | 1 |  |  | 1 |  | 386 | use Data::Dump qw( dump ); | 
|  | 1 |  |  |  |  | 3680 |  | 
|  | 1 |  |  |  |  | 66 |  | 
| 10 | 1 |  |  | 1 |  | 378 | use DBICx::AutoDoc::Magic; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 11 | 1 |  |  | 1 |  | 602 | use File::Temp qw( tempfile ); | 
|  | 1 |  |  |  |  | 14850 |  | 
|  | 1 |  |  |  |  | 81 |  | 
| 12 | 1 |  |  | 1 |  | 658 | use File::ShareDir qw( dist_dir ); | 
|  | 1 |  |  |  |  | 5442 |  | 
|  | 1 |  |  |  |  | 101 |  | 
| 13 | 1 |  |  | 1 |  | 9 | use File::Spec; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 17 |  | 
| 14 | 1 |  |  | 1 |  | 496 | use Tie::IxHash; | 
|  | 1 |  |  |  |  | 3336 |  | 
|  | 1 |  |  |  |  | 502 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | __PACKAGE__->mk_group_accessors( simple => qw( | 
| 17 |  |  |  |  |  |  | output connect dsn user pass | 
| 18 |  |  |  |  |  |  | ) ); | 
| 19 |  |  |  |  |  |  | __PACKAGE__->mk_group_accessors( inherited => qw( | 
| 20 |  |  |  |  |  |  | include_path graphviz_command | 
| 21 |  |  |  |  |  |  | ) ); | 
| 22 |  |  |  |  |  |  | __PACKAGE__->include_path( __PACKAGE__->default_include_path() ); | 
| 23 |  |  |  |  |  |  | __PACKAGE__->graphviz_command( [ "fdp" ] ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | sub new { | 
| 26 | 0 |  |  | 0 | 1 | 0 | my $self = bless( { | 
| 27 |  |  |  |  |  |  | output          => '.', | 
| 28 |  |  |  |  |  |  | connect         => 0, | 
| 29 |  |  |  |  |  |  | }, shift() ); | 
| 30 | 0 |  |  |  |  | 0 | my %args = @_; | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 0 |  |  |  |  | 0 | for my $key ( keys %args ) { $self->$key( $args{ $key } ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 0 |  |  |  |  | 0 | return $self; | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub schema { | 
| 38 | 0 |  |  | 0 | 1 | 0 | my ( $self, $val ) = @_; | 
| 39 |  |  |  |  |  |  |  | 
| 40 | 0 | 0 |  |  |  | 0 | if ( $val ) { | 
|  |  | 0 |  |  |  |  |  | 
| 41 | 0 |  |  |  |  | 0 | $self->{ 'schema' } = $val; | 
| 42 | 0 |  |  |  |  | 0 | eval "require $val"; | 
| 43 | 0 | 0 |  |  |  | 0 | if ( $@ ) { croak "Could not load $val: $@" } | 
|  | 0 |  |  |  |  | 0 |  | 
| 44 |  |  |  |  |  |  | } elsif( my $schema = $self->{ 'schema' } ) { | 
| 45 | 0 | 0 | 0 |  |  | 0 | if ( ref( $schema ) || ! $self->connect ) { return $schema } | 
|  | 0 |  |  |  |  | 0 |  | 
| 46 | 0 |  |  |  |  | 0 | print "Connecting to database\n"; | 
| 47 | 0 |  |  |  |  | 0 | $self->{ 'schema' } = $schema->connect( | 
| 48 |  |  |  |  |  |  | $self->dsn, $self->user, $self->pass, | 
| 49 |  |  |  |  |  |  | ); | 
| 50 | 0 |  |  |  |  | 0 | return $self->{ 'schema' }; | 
| 51 |  |  |  |  |  |  | } else { | 
| 52 | 0 |  |  |  |  | 0 | croak "No schema provided"; | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  | } | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | sub schema_class { | 
| 57 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 0 |  |  |  |  | 0 | my $schema = $self->schema; | 
| 60 | 0 |  | 0 |  |  | 0 | return ref( $schema ) || $schema; | 
| 61 |  |  |  |  |  |  | } | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 0 | 0 |  | 0 | 1 | 0 | sub schema_version { shift->schema->VERSION || 1 } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub generated { | 
| 66 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 0 |  | 0 |  |  | 0 | $self->{ 'generated' } ||= localtime; | 
| 69 | 0 |  |  |  |  | 0 | return $self->{ 'generated' }; | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | sub software_versions { | 
| 73 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | return { | 
| 76 | 0 |  |  |  |  | 0 | map { ( $_ => $_->VERSION ) } qw( | 
|  | 0 |  |  |  |  | 0 |  | 
| 77 |  |  |  |  |  |  | DBICx::AutoDoc DBICx::AutoDoc::Magic | 
| 78 |  |  |  |  |  |  | DBIx::Class Template | 
| 79 |  |  |  |  |  |  | ) | 
| 80 |  |  |  |  |  |  | }; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | sub sources { | 
| 84 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 0 | 0 |  |  |  | 0 | if ( $self->{ 'sources' } ) { return $self->{ 'sources' } } | 
|  | 0 |  |  |  |  | 0 |  | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 |  |  |  |  | 0 | my $schema = $self->schema; | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 0 |  |  |  |  | 0 | my @sources = (); | 
| 91 | 0 |  |  |  |  | 0 | $self->{ 'sources' } = \@sources; | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 0 |  |  |  |  | 0 | my %source_names = (); | 
| 94 | 0 |  |  |  |  | 0 | $self->{ 'source_names' } = \%source_names; | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | # mst: map { $_->source_name } | 
| 97 |  |  |  |  |  |  | #      grep { $_->result_class eq $class } | 
| 98 |  |  |  |  |  |  | #      map { $schema->source($_) } $schema->sources | 
| 99 |  |  |  |  |  |  | # mst: it's all you can have safely :) | 
| 100 | 0 |  |  |  |  | 0 | for my $moniker ( sort $schema->sources ) { | 
| 101 | 0 |  |  |  |  | 0 | my $source = $schema->source( $moniker ); | 
| 102 | 0 |  |  |  |  | 0 | my $rs = $schema->resultset( $moniker ); | 
| 103 | 0 |  |  |  |  | 0 | my $cl = $rs->result_class; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 0 |  |  |  |  | 0 | $source_names{ $cl } = $source->source_name; | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | # COLLECTING DATA | 
| 108 | 0 |  |  |  |  | 0 | push( @sources, { | 
| 109 |  |  |  |  |  |  | moniker             => $moniker, | 
| 110 |  |  |  |  |  |  | simple_moniker      => $self->get_simple_moniker_for( $moniker ), | 
| 111 |  |  |  |  |  |  | class               => $cl, | 
| 112 |  |  |  |  |  |  | primary_columns     => [ $cl->primary_columns ], | 
| 113 |  |  |  |  |  |  | table               => $cl->table, | 
| 114 |  |  |  |  |  |  | result_class        => $cl, | 
| 115 |  |  |  |  |  |  | resultset_class     => $cl->resultset_class, | 
| 116 |  |  |  |  |  |  | columns             => [ $self->get_columns_for( $cl ) ], | 
| 117 |  |  |  |  |  |  | unique_constraints  => [ $self->get_unique_constraints_for( $cl ) ], | 
| 118 |  |  |  |  |  |  | relationships       => [ $self->get_relationships_for( $cl ) ], | 
| 119 |  |  |  |  |  |  | } ); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 | 0 |  |  |  |  | 0 | return $self->{ 'sources' }; | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub inheritance { | 
| 126 | 0 |  |  | 0 | 1 | 0 | my ( $self, @classes ) = @_; | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 0 | 0 |  |  |  | 0 | if ( ! @classes ) { | 
| 129 | 0 |  |  |  |  | 0 | @classes = ( map { $_->{ 'class' } } @{ $self->sources } ); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 130 |  |  |  |  |  |  | } | 
| 131 | 0 |  |  |  |  | 0 | my %parents = (); | 
| 132 | 0 |  |  |  |  | 0 | while ( @classes ) { | 
| 133 | 0 |  |  |  |  | 0 | my $class = shift( @classes ); | 
| 134 | 0 | 0 |  |  |  | 0 | next if $parents{ $class }; | 
| 135 | 1 |  |  | 1 |  | 7 | my @tmp = do { no strict 'refs'; @{ $class.'::ISA' } }; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 1569 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 136 | 0 |  |  |  |  | 0 | push( @classes, @tmp ); | 
| 137 | 0 |  |  |  |  | 0 | $parents{ $class } = \@tmp; | 
| 138 |  |  |  |  |  |  | } | 
| 139 | 0 |  |  |  |  | 0 | return \%parents; | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | sub get_columns_for { | 
| 143 | 0 |  |  | 0 | 1 | 0 | my ( $self, $class ) = @_; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 0 |  |  |  |  | 0 | my %cols = (); | 
| 146 | 0 |  |  |  |  | 0 | tie( %cols, 'Tie::IxHash' ); | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | # COLUMNS | 
| 149 | 0 |  |  |  |  | 0 | for ( $class->columns ) { | 
| 150 | 0 |  |  |  |  | 0 | my $col = $class->column_info( $_ ); | 
| 151 |  |  |  |  |  |  | $col->{ 'default_value' } = | 
| 152 | 0 |  |  |  |  | 0 | ref($col->{ 'default_value' }) eq "SCALAR" ? ${$col->{ 'default_value' }} | 
| 153 |  |  |  |  |  |  | : defined($col->{ 'default_value' })         ? "'$col->{ 'default_value' }'" | 
| 154 |  |  |  |  |  |  | :                                              'NULL' | 
| 155 | 0 | 0 |  |  |  | 0 | if exists $col->{ 'default_value' }; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 156 | 0 |  |  |  |  | 0 | $col->{ 'name' } = $_; | 
| 157 | 0 | 0 |  |  |  | 0 | $col->{ 'is_inflated' } = delete $col->{ '_inflate_info' } ? 1 : 0; | 
| 158 | 0 |  |  |  |  | 0 | $cols{ $_ } = $col; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | # PRIMARY COLUMNS | 
| 162 | 0 |  |  |  |  | 0 | for my $c ( $class->primary_columns ) { | 
| 163 | 0 |  |  |  |  | 0 | $cols{ $c }->{ 'is_primary' } = 1; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | # UNIQUE CONSTRAINTS | 
| 167 | 0 |  |  |  |  | 0 | my %tmp = $class->unique_constraints; | 
| 168 | 0 |  |  |  |  | 0 | while ( my ( $key, $val ) = each %tmp ) { | 
| 169 | 0 |  |  |  |  | 0 | for my $x ( @{ $val } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 170 | 0 |  |  |  |  | 0 | push( @{ $cols{ $x }->{ 'unique_constraints' } }, $key ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 0 |  |  |  |  | 0 | return values %cols; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | sub get_unique_constraints_for { | 
| 178 | 0 |  |  | 0 | 1 | 0 | my ( $self, $class ) = @_; | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | # UNIQUE CONSTRAINTS | 
| 181 | 0 |  |  |  |  | 0 | my %unique = (); | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 0 |  |  |  |  | 0 | my %tmp = $class->unique_constraints; | 
| 184 | 0 |  |  |  |  | 0 | for my $key ( sort keys %tmp ) { | 
| 185 | 0 |  |  |  |  | 0 | $unique{ $key }->{ 'name' } = $key; | 
| 186 | 0 |  |  |  |  | 0 | $unique{ $key }->{ 'columns' } = $tmp{ $key } | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 0 |  |  |  |  | 0 | return values %unique; | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub get_relationships_for { | 
| 193 | 0 |  |  | 0 | 1 | 0 | my ( $self, $class ) = @_; | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 0 |  |  |  |  | 0 | my %relationships = (); | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | # RELATIONSHIPS (from DBICx::AutoDoc::Magic) | 
| 198 | 0 | 0 |  |  |  | 0 | unless ( $class->can( '_autodoc' ) ) { | 
| 199 | 0 |  |  |  |  | 0 | croak "$class cannot _autodoc, something must have gone wrong"; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 0 |  | 0 |  |  | 0 | my $ad = $class->_autodoc || {}; | 
| 203 | 0 | 0 |  |  |  | 0 | for ( @{ $ad->{ 'relationships' } || [] } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 204 | 0 |  |  |  |  | 0 | my ( $type, $relname, @parts ) = @{ $_ }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 205 | 0 |  | 0 |  |  | 0 | my $rel = ( $relationships{ $relname } ||= {} ); | 
| 206 | 0 |  |  |  |  | 0 | @{ $rel }{qw( name type )} = ( $relname, $type ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 0 | 0 |  |  |  | 0 | if ( $type eq 'many_to_many' ) { | 
| 209 | 0 |  |  |  |  | 0 | @{ $rel }{qw( link_rel_name foreign_rel_name attributes )} = @parts; | 
|  | 0 |  |  |  |  | 0 |  | 
| 210 |  |  |  |  |  |  | } else { | 
| 211 | 0 |  |  |  |  | 0 | @{ $rel }{qw( foreign_class condition attributes )} = @parts; | 
|  | 0 |  |  |  |  | 0 |  | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # RELATIONSHIPS (from DBIx::Class::Relationship) | 
| 216 | 0 |  |  |  |  | 0 | for my $name ( $class->relationships ) { | 
| 217 | 0 |  | 0 |  |  | 0 | my $rel = ( $relationships{ $name } ||= {} ); | 
| 218 | 0 |  |  |  |  | 0 | my $info = $class->relationship_info( $name ); | 
| 219 | 0 |  | 0 |  |  | 0 | $rel->{ 'name' } ||= $name; | 
| 220 | 0 |  |  |  |  | 0 | for my $key ( keys %{ $info } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 221 | 0 |  |  |  |  | 0 | $rel->{ $key } = $info->{ $key }; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | # GENERAL RELATIONSHIP MUNGING | 
| 226 | 0 |  |  |  |  | 0 | for my $name ( keys %relationships ) { | 
| 227 | 0 |  |  |  |  | 0 | my $rel = $relationships{ $name }; | 
| 228 | 0 |  |  |  |  | 0 | for my $x ( '', 'foreign_' ) { | 
| 229 | 0 | 0 |  |  |  | 0 | if ( $rel->{ $x.'class' } ) { | 
| 230 | 0 |  |  |  |  | 0 | $rel->{ $x.'moniker' } = $rel->{ $x.'class' }->source_name; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 0 |  |  |  |  | 0 | return values %relationships; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | sub relationship_map { | 
| 239 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 0 |  |  |  |  | 0 | my @relmap = (); | 
| 242 | 0 |  |  |  |  | 0 | my $snames = $self->{ 'source_names' }; | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 0 |  |  |  |  | 0 | for my $source ( @{ $self->sources } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 245 | 0 |  |  |  |  | 0 | for my $rel ( @{ $source->{ 'relationships' } } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 246 | 0 |  |  |  |  | 0 | my $type = $rel->{ 'type' }; | 
| 247 |  |  |  |  |  |  | my $map = { | 
| 248 | 0 |  |  |  |  | 0 | name    => $rel->{ 'name' }, | 
| 249 |  |  |  |  |  |  | type    => $type, | 
| 250 |  |  |  |  |  |  | }; | 
| 251 | 0 |  |  |  |  | 0 | push( @relmap, $map ); | 
| 252 | 0 | 0 |  |  |  | 0 | if ( $type eq 'many_to_many' ) { | 
| 253 | 0 |  |  |  |  | 0 | for my $x (qw( link_rel_name foreign_rel_name )) { | 
| 254 | 0 |  |  |  |  | 0 | $map->{ $x } = $rel->{ $x }; | 
| 255 |  |  |  |  |  |  | } | 
| 256 | 0 |  |  |  |  | 0 | $map->{ 'accessor' } = 'many_to_many'; | 
| 257 |  |  |  |  |  |  | } else { | 
| 258 | 0 |  |  |  |  | 0 | $map->{ 'accessor' } = $rel->{ 'attr' }->{ 'accessor' }; | 
| 259 | 0 |  |  |  |  | 0 | $map->{ 'self' } = $source->{ 'moniker' }; | 
| 260 | 0 |  |  |  |  | 0 | $map->{ 'foreign' } = $snames->{ $rel->{ 'foreign_class' } }; | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 0 |  |  |  |  | 0 | my %cond = %{ $rel->{ 'cond' } }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 0 |  |  |  |  | 0 | my @cond = (); | 
| 265 | 0 |  |  |  |  | 0 | while ( my ( $l, $r ) = each %cond ) { | 
| 266 | 0 |  |  |  |  | 0 | push( @cond, { split( '\.', $l, 2 ), split( '\.', $r ) } ); | 
| 267 |  |  |  |  |  |  | } | 
| 268 | 0 |  |  |  |  | 0 | $map->{ 'cond' } = \@cond; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  | } | 
| 272 | 0 |  |  |  |  | 0 | return \@relmap; | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub get_simple_moniker_for { | 
| 276 | 0 |  |  | 0 | 1 | 0 | my ( $self, $moniker ) = @_; | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | #if ( $moniker->can( 'source_name' ) ) { $moniker = $moniker->source_name } | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 0 |  | 0 |  |  | 0 | $self->{ '_simple_moniker_cache' } ||= {}; | 
| 281 | 0 |  |  |  |  | 0 | my $cache = $self->{ '_simple_moniker_cache' }; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 0 | 0 |  |  |  | 0 | if ( $cache->{ $moniker } ) { return $cache->{ $moniker } } | 
|  | 0 |  |  |  |  | 0 |  | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 0 |  |  |  |  | 0 | my $simple = $moniker; | 
| 286 | 0 |  |  |  |  | 0 | $simple =~ s/\W+/_/g; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 0 |  |  |  |  | 0 | my %inverse_cache = reverse %{ $cache }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 289 | 0 | 0 |  |  |  | 0 | if ( $inverse_cache{ $simple } ) { | 
| 290 | 0 |  |  |  |  | 0 | my $i = 0; | 
| 291 | 0 |  |  |  |  | 0 | while ( $inverse_cache{ $simple.$i } ) { $i++ } | 
|  | 0 |  |  |  |  | 0 |  | 
| 292 | 0 |  |  |  |  | 0 | $simple .= $i; | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 | 0 |  |  |  |  | 0 | $cache->{ $moniker } = $simple; | 
| 296 |  |  |  |  |  |  | } | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 0 |  |  | 0 | 1 | 0 | sub byname($$) { return shift->{ 'name' } cmp shift->{ 'name' } } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | sub get_vars { | 
| 301 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 0 |  |  |  |  | 0 | my @vars = qw( | 
| 304 |  |  |  |  |  |  | schema schema_class schema_version generated software_versions sources | 
| 305 |  |  |  |  |  |  | relationship_map filename_base output connect dsn user | 
| 306 |  |  |  |  |  |  | graphviz_command inheritance | 
| 307 |  |  |  |  |  |  | ); | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | $self->{ '_vars' } ||= { | 
| 310 |  |  |  |  |  |  | autodoc         => $self, | 
| 311 | 0 |  |  | 0 |  | 0 | dumper          => sub { return dump( @_ ) }, | 
| 312 | 0 |  |  | 0 |  | 0 | simplify        => sub { return $self->get_simple_moniker_for( @_ ) }, | 
| 313 | 0 |  |  | 0 |  | 0 | output_filename => sub { return $self->output_filename( @_ ) }, | 
| 314 |  |  |  |  |  |  | ENV             => \%ENV, | 
| 315 |  |  |  |  |  |  | varlist         => [ @vars, 'ENV' ], | 
| 316 | 0 |  | 0 |  |  | 0 | ( map { ( $_ => $self->$_() ) } @vars ), | 
|  | 0 |  |  |  |  | 0 |  | 
| 317 |  |  |  |  |  |  | }; | 
| 318 | 0 |  |  |  |  | 0 | return $self->{ '_vars' }; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | sub find_template_file { | 
| 322 | 0 |  |  | 0 | 1 | 0 | my ( $self, $template ) = @_; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 0 |  |  |  |  | 0 | my $path = $self->include_path; | 
| 325 | 0 | 0 |  |  |  | 0 | if ( ! ref $path ) { $path = [ $path ] } | 
|  | 0 |  |  |  |  | 0 |  | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 0 |  |  |  |  | 0 | for my $x ( @{ $path } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 328 | 0 |  |  |  |  | 0 | my $test = File::Spec->catfile( $x, $template ); | 
| 329 | 0 | 0 |  |  |  | 0 | if ( -f $test ) { return $test } | 
|  | 0 |  |  |  |  | 0 |  | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 0 |  |  |  |  | 0 | return; | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | sub fill_template { | 
| 336 | 0 |  |  | 0 | 1 | 0 | my ( $self, $template ) = @_; | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | my $first_line = sub { | 
| 339 | 0 |  |  | 0 |  | 0 | open( my $fh, shift() ); chomp( my $start = <$fh> ); close( $fh ); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 340 | 0 |  |  |  |  | 0 | return $start; | 
| 341 | 0 |  |  |  |  | 0 | }; | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 0 |  |  |  |  | 0 | my $tmpl = Template->new( { INCLUDE_PATH => $self->include_path } ); | 
| 344 | 0 |  |  |  |  | 0 | my $outfile = $self->output_filename( $template, 1 ); | 
| 345 | 0 |  |  |  |  | 0 | my $vars = $self->get_vars; | 
| 346 |  |  |  |  |  |  |  | 
| 347 | 0 | 0 |  |  |  | 0 | if ( $first_line->( $self->find_template_file( $template ) ) =~ /^#!/ ) { | 
| 348 | 0 |  |  |  |  | 0 | my ( undef, $file ) = tempfile(); | 
| 349 | 0 |  |  |  |  | 0 | my $script = $outfile.'.script'; | 
| 350 | 0 | 0 |  |  |  | 0 | $tmpl->process( $template, $vars, $script ) || croak $tmpl->error; | 
| 351 |  |  |  |  |  |  |  | 
| 352 | 0 |  |  |  |  | 0 | my $cmd = $first_line->( $script ); | 
| 353 | 0 |  |  |  |  | 0 | $cmd =~ s/^#!//; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 0 |  |  |  |  | 0 | open( my $outfh, '>', $outfile ); | 
| 356 | 0 |  |  |  |  | 0 | open( my $infh, '-|', $cmd, $script ); | 
| 357 | 0 |  |  |  |  | 0 | $outfh->print( <$infh> ); | 
| 358 | 0 |  |  |  |  | 0 | close( $infh ); | 
| 359 | 0 |  |  |  |  | 0 | close( $outfh ); | 
| 360 | 0 |  |  |  |  | 0 | unlink( $script ); | 
| 361 |  |  |  |  |  |  | } else { | 
| 362 | 0 | 0 |  |  |  | 0 | $tmpl->process( $template, $vars, $outfile ) || croak $tmpl->error; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  | } | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | sub filename_base { | 
| 367 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 0 |  | 0 |  |  | 0 | my $name = ref( $self->schema ) || $self->schema; | 
| 370 | 0 | 0 |  |  |  | 0 | if ( ! $name ) { croak "Cannot call filename_base without a schema" } | 
|  | 0 |  |  |  |  | 0 |  | 
| 371 | 0 |  |  |  |  | 0 | $name =~ s/::/-/g; | 
| 372 | 0 |  | 0 |  |  | 0 | return join( '-', $name, $self->schema->VERSION || 1 ); | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | sub output_filename { | 
| 376 | 0 |  |  | 0 | 1 | 0 | my ( $self, $template, $full ) = @_; | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 0 |  |  |  |  | 0 | my $base = $self->filename_base; | 
| 379 | 0 |  |  |  |  | 0 | $template =~ s/^AUTODOC/$base/; | 
| 380 | 0 | 0 |  |  |  | 0 | if ( $full ) { | 
| 381 | 0 |  |  |  |  | 0 | return File::Spec->catfile( $self->output, $template ); | 
| 382 |  |  |  |  |  |  | } else { | 
| 383 | 0 |  |  |  |  | 0 | return $template; | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | sub default_include_path { | 
| 388 | 1 |  |  | 1 | 1 | 2 | my ( $self ) = @_; | 
| 389 | 1 |  | 33 |  |  | 12 | (my $dist = ref( $self ) || $self) =~ s/::/-/g; | 
| 390 | 1 |  |  |  |  | 4 | return [ dist_dir( $dist ), File::Spec->catdir( $FindBin::Bin, "templates" ) ]; | 
| 391 |  |  |  |  |  |  | } | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | sub list_templates { | 
| 394 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 0 |  |  |  |  |  | my $inc = $self->include_path; | 
| 397 | 0 | 0 |  |  |  |  | if ( ! ref $inc ) { $inc = [ $inc ] } | 
|  | 0 |  |  |  |  |  |  | 
| 398 | 0 |  |  |  |  |  | my %tmpls = (); | 
| 399 | 0 |  |  |  |  |  | for my $dir ( @{ $inc } ) { | 
|  | 0 |  |  |  |  |  |  | 
| 400 | 0 |  |  |  |  |  | opendir( my $dirfh, $dir ); | 
| 401 | 0 |  |  |  |  |  | for ( readdir( $dirfh ) ) { | 
| 402 | 0 | 0 |  |  |  |  | next unless /^AUTODOC/; | 
| 403 | 0 |  |  |  |  |  | $tmpls{ $_ } = 1; | 
| 404 |  |  |  |  |  |  | } | 
| 405 | 0 |  |  |  |  |  | closedir( $dirfh ); | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 0 | 0 |  |  |  |  | return sort { length( $a ) <=> length( $b ) || $a cmp $b } keys %tmpls; | 
|  | 0 |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | } | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | sub fill_all_templates { | 
| 412 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 413 |  |  |  |  |  |  |  | 
| 414 | 0 |  |  |  |  |  | $self->fill_templates( $self->list_templates ); | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | sub fill_templates { | 
| 418 | 0 |  |  | 0 | 1 |  | my ( $self, @templates ) = @_; | 
| 419 |  |  |  |  |  |  |  | 
| 420 | 0 |  |  |  |  |  | $self->fill_template( $_ ) for @templates; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | 1; | 
| 425 |  |  |  |  |  |  | __END__ |