File Coverage

blib/lib/SQL/Translator/Producer/HTML.pm
Criterion Covered Total %
statement 71 93 76.3
branch 24 44 54.5
condition 5 23 21.7
subroutine 5 5 100.0
pod 0 1 0.0
total 105 166 63.2


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