File Coverage

blib/lib/SQL/Translator/Producer/HTML.pm
Criterion Covered Total %
statement 12 93 12.9
branch 0 44 0.0
condition 0 23 0.0
subroutine 4 5 80.0
pod 0 1 0.0
total 16 166 9.6


line stmt bran cond sub pod time code
1             package SQL::Translator::Producer::HTML;
2              
3 1     1   5 use strict;
  1         2  
  1         32  
4 1     1   4 use warnings;
  1         164  
  1         52  
5 1     1   6 use Data::Dumper;
  1         1  
  1         88  
6              
7             our $VERSION = '1.66';
8             our $NAME = __PACKAGE__;
9             our $NOWRAP = 0 unless defined $NOWRAP;
10             our $NOLINKTABLE = 0 unless defined $NOLINKTABLE;
11              
12             # Emit XHTML by default
13             $CGI::XHTML = $CGI::XHTML = 42;
14              
15 1     1   4 use SQL::Translator::Schema::Constants;
  1         2  
  1         1352  
16              
17             # -------------------------------------------------------------------
18             # Main entry point. Returns a string containing HTML.
19             # -------------------------------------------------------------------
20             sub produce {
21 0     0 0   my $t = shift;
22 0           my $args = $t->producer_args;
23 0           my $schema = $t->schema;
24 0   0       my $schema_name = $schema->name || 'Schema';
25 0   0       my $title = $args->{'title'} || "Description of $schema_name";
26             my $wrap = !(
27             defined $args->{'nowrap'}
28 0 0         ? $args->{'nowrap'}
29             : $NOWRAP
30             );
31             my $linktable = !(
32             defined $args->{'nolinktable'}
33 0 0         ? $args->{'nolinktable'}
34             : $NOLINKTABLE
35             );
36             my %stylesheet
37             = defined $args->{'stylesheet'}
38 0 0         ? (-style => { src => $args->{'stylesheet'} })
39             : ();
40 0           my @html;
41             my $q = defined $args->{'pretty'}
42             ? do {
43 0           require CGI::Pretty;
44 0           import CGI::Pretty;
45 0           CGI::Pretty->new;
46             }
47 0 0         : do {
48 0           require CGI;
49 0           import CGI;
50 0           CGI->new;
51             };
52 0           my ($table, @table_names);
53              
54 0 0         if ($wrap) {
55 0           push @html,
56             $q->start_html({
57             -title => $title,
58             %stylesheet,
59             -meta => { generator => $NAME },
60             }),
61             $q->h1({ -class => 'SchemaDescription' }, $title),
62             $q->hr;
63             }
64              
65 0           @table_names = grep { length $_->name } $schema->get_tables;
  0            
66              
67 0 0         if ($linktable) {
68              
69             # Generate top menu, with links to full table information
70 0           my $count = scalar(@table_names);
71 0 0         $count = sprintf "%d table%s", $count, $count == 1 ? '' : 's';
72              
73             # Leading table of links
74 0           push @html,
75             $q->comment("Table listing ($count)"),
76             $q->a({ -name => 'top' }),
77             $q->start_table({ -width => '100%', -class => 'LinkTable' }),
78              
79             # XXX This needs to be colspan="$#{$table->fields}" class="LinkTableHeader"
80             $q->Tr($q->td({ -class => 'LinkTableCell' }, $q->h2({ -class => 'LinkTableTitle' }, 'Tables'),),);
81              
82 0           for my $table (@table_names) {
83 0           my $table_name = $table->name;
84 0           push @html, $q->comment("Start link to table '$table_name'"),
85             $q->Tr({ -class => 'LinkTableRow' },
86             $q->td({ -class => 'LinkTableCell' }, qq[$table_name])),
87             $q->comment("End link to table '$table_name'");
88             }
89 0           push @html, $q->end_table;
90             }
91              
92 0           for my $table ($schema->get_tables) {
93 0 0         my $table_name = $table->name or next;
94 0 0         my @fields = $table->get_fields or next;
95 0           push @html, $q->comment("Starting table '$table_name'"), $q->a({ -name => $table_name }),
96             $q->table(
97             { -class => 'TableHeader', -width => '100%' },
98             $q->Tr(
99             { -class => 'TableHeaderRow' },
100             $q->td({ -class => 'TableHeaderCell' }, $q->h3($table_name)),
101             qq[],
102             $q->td({ -class => 'TableHeaderCell', -align => 'right' }, qq[Top])
103             )
104             );
105              
106 0 0         if (my @comments = map { $_ ? $_ : () } $table->comments) {
  0 0          
107 0           push @html, $q->b("Comments:"), $q->br, $q->em(map { $q->br, $_ } @comments);
  0            
108             }
109              
110             #
111             # Fields
112             #
113 0           push @html, $q->start_table({ -border => 1 }),
114             $q->Tr($q->th(
115             { -class => 'FieldHeader' },
116             [ 'Field Name', 'Data Type', 'Size', 'Default Value', 'Other', 'Foreign Key' ]
117             ));
118              
119 0           my $i = 0;
120 0           for my $field (@fields) {
121 0   0       my $name = $field->name || '';
122 0           $name = qq[$name];
123 0   0       my $data_type = $field->data_type || '';
124 0 0         my $size = defined $field->size ? $field->size : '';
125 0 0         my $default = defined $field->default_value ? $field->default_value : '';
126 0   0       my $comment = $field->comments || '';
127 0           my $fk = '';
128              
129 0 0         if ($field->is_foreign_key) {
130 0           my $c = $field->foreign_key_reference;
131 0   0       my $ref_table = $c->reference_table || '';
132 0   0       my $ref_field = ($c->reference_fields)[0] || '';
133 0           $fk = qq[$ref_table.$ref_field];
134             }
135              
136 0           my @other = ();
137 0 0         push @other, 'PRIMARY KEY' if $field->is_primary_key;
138 0 0         push @other, 'UNIQUE' if $field->is_unique;
139 0 0         push @other, 'NOT NULL' unless $field->is_nullable;
140 0 0         push @other, $comment if $comment;
141 0 0         my $class = $i++ % 2 ? 'even' : 'odd';
142 0           push @html,
143             $q->Tr(
144             { -class => "tr-$class" },
145             $q->td({ -class => "FieldCellName" }, $name),
146             $q->td({ -class => "FieldCellType" }, $data_type),
147             $q->td({ -class => "FieldCellSize" }, $size),
148             $q->td({ -class => "FieldCellDefault" }, $default),
149             $q->td({ -class => "FieldCellOther" }, join(', ', @other)),
150             $q->td({ -class => "FieldCellFK" }, $fk),
151             );
152             }
153 0           push @html, $q->end_table;
154              
155             #
156             # Indices
157             #
158 0 0         if (my @indices = $table->get_indices) {
159 0           push @html,
160             $q->h3('Indices'),
161             $q->start_table({ -border => 1 }),
162             $q->Tr({ -class => 'IndexRow' }, $q->th([ 'Name', 'Fields' ]));
163              
164 0           for my $index (@indices) {
165 0   0       my $name = $index->name || '';
166 0   0       my $fields = join(', ', $index->fields) || '';
167              
168 0           push @html, $q->Tr({ -class => 'IndexCell' }, $q->td([ $name, $fields ]));
169             }
170              
171 0           push @html, $q->end_table;
172             }
173              
174             #
175             # Constraints
176             #
177 0           my @constraints = grep { $_->type ne PRIMARY_KEY } $table->get_constraints;
  0            
178 0 0         if (@constraints) {
179 0           push @html,
180             $q->h3('Constraints'),
181             $q->start_table({ -border => 1 }),
182             $q->Tr({ -class => 'IndexRow' }, $q->th([ 'Type', 'Fields' ]));
183              
184 0           for my $c (@constraints) {
185 0   0       my $type = $c->type || '';
186 0   0       my $fields = join(', ', $c->fields) || '';
187              
188 0           push @html, $q->Tr({ -class => 'IndexCell' }, $q->td([ $type, $fields ]));
189             }
190              
191 0           push @html, $q->end_table;
192             }
193              
194 0           push @html, $q->hr;
195             }
196              
197 0           my $sqlt_version = $t->version;
198 0 0         if ($wrap) {
199 0           push @html,
200             qq[Created by ],
201             qq[SQL::Translator $sqlt_version],
202             $q->end_html;
203             }
204              
205 0           return join "\n", @html;
206             }
207              
208             1;
209              
210             # -------------------------------------------------------------------
211             # Always be ready to speak your mind,
212             # and a base man will avoid you.
213             # William Blake
214             # -------------------------------------------------------------------
215              
216             =head1 NAME
217              
218             SQL::Translator::Producer::HTML - HTML producer for SQL::Translator
219              
220             =head1 SYNOPSIS
221              
222             use SQL::Translator::Producer::HTML;
223              
224             =head1 DESCRIPTION
225              
226             Creates an HTML document describing the tables.
227              
228             The HTML produced is composed of a number of tables:
229              
230             =over 4
231              
232             =item Links
233              
234             A link table sits at the top of the output, and contains anchored
235             links to elements in the rest of the document.
236              
237             If the I producer arg is present, then this table is not
238             produced.
239              
240             =item Tables
241              
242             Each table in the schema has its own HTML table. The top row is a row
243             of EthE elements, with a class of B; these
244             elements are I, I, I, I,
245             I and I. Each successive row describes one field
246             in the table, and has a class of B, where $item id
247             corresponds to the label of the column. For example:
248              
249            
250             id
251             int
252             11
253            
254             PRIMARY KEY, NOT NULL
255            
256            
257              
258            
259             foo
260             varchar
261             255
262            
263             NOT NULL
264            
265            
266              
267            
268             updated
269             timestamp
270             0
271            
272            
273            
274            
275              
276             =back
277              
278             Unless the I producer arg is present, the HTML will be
279             enclosed in a basic HTML header and footer.
280              
281             If the I producer arg is present, the generated HTML will be
282             nicely spaced and human-readable. Otherwise, it will have very little
283             insignificant whitespace and be generally smaller.
284              
285              
286             =head1 AUTHORS
287              
288             Ken Youens-Clark Ekclark@cpan.orgE,
289             Darren Chamberlain Edarren@cpan.orgE.
290              
291             =cut