Added TT::Table producer.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / TT / Table.pm
CommitLineData
0be5b227 1package SQL::Translator::Producer::TT::Table;
2
3# -------------------------------------------------------------------
4# $Id: Table.pm,v 1.1 2004-04-01 19:02:39 grommit Exp $
5# -------------------------------------------------------------------
6# Copyright (C) 2002-4 SQLFairy Authors
7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License as
10# published by the Free Software Foundation; version 2.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20# 02111-1307 USA
21# -------------------------------------------------------------------
22
23=pod
24
25=head1 NAME
26
27SQL::Translator::Producer::TT::Table -
28 Produces output using the Template Toolkit from a SQL schema, per table.
29
30=head1 SYNOPSIS
31
32 # Normal STDOUT version
33 #
34 my $translator = SQL::Translator->new(
35 from => 'MySQL',
36 filename => 'foo_schema.sql',
37 to => 'TT::Table',
38 producer_args => {
39 tt_table => 'foo_table.tt',
40 },
41 );
42 print $translator->translate;
43
44 # To generate a file per table
45 #
46 my $translator = SQL::Translator->new(
47 from => 'MySQL',
48 filename => 'foo_schema.sql',
49 to => 'TT::Table',
50 producer_args => {
51 tt_table => 'foo_table.tt.html',
52 mk_files => 1,
53 mk_files_base => "./doc/tables",
54 mk_file_ext => ".html",
55 on_exists => "replace",
56 },
57 );
58 #
59 # ./doc/tables/ now contains the templated tables as $tablename.html
60 #
61
62=head1 DESCRIPTION
63
64Produces schema output using a given Template Tookit template, processing that
65template for each table in the schema. Optionally allows you to write the result
66for each table to a seperate file.
67
68It needs one additional producer_arg of C<tt_table> which is the file
69name of the template to use. This template will be passed a template var of
70C<table>, which is the current C<SQL::Translator::Producer::Table> table we are
71producing, which you can then use to walk the schema via the methods documented
72in that module. You also get C<schema> as a shortcut to the
73C<SQL::Translator::Producer::Schema> for the table and C<translator>, the
74C<SQL::Translator> object for this parse in case you want to get access to any
75of the options etc set here.
76
77Here's a brief example of what the template could look like:
78
79 [% table.name %]
80 ================
81 [% FOREACH field = table.get_fields %]
82 [% field.name %] [% field.data_type %]([% field.size %])
83 [% END -%]
84
85See F<t/data/template/table.tt> for a more complete example.
86
87You can also set any of the options used to initiallize the Template object by
88adding them to your producer_args. See Template Toolkit docs for details of
89the options.
90
91 $translator = SQL::Translator->new(
92 to => 'TT',
93 producer_args => {
94 ttfile => 'foo_template.tt',
95 INCLUDE_PATH => '/foo/templates/tt',
96 INTERPOLATE => 1,
97 },
98 );
99
100If you set C<mk_files> and its additional options the producer will write a
101seperate file for each table in the schema. This is usefull for producing
102things like HTML documentation where every table gets its own page (you could
103also use TTSchema producer to add an index page). Its also particulary good
104for code generation where you want to produce a class file per table.
105
106=head1 OPTIONS
107
108=over 4
109
110=item tt_table
111
112File name of the template to run for each table.
113
114=item mk_files
115
116Set to true to output a file for each table in the schema (as well as returning
117the whole lot back to the Translalor and hence STDOUT). The file will be named
118after the table, with the optional C<mk_files_ext> added and placed in the
119directory C<mk_files_base>.
120
121=item mk_files_ext
122
123Extension (without the dot) to add to the filename when using mk_files.
124
125=item mk_files_base = DIR
126
127Dir to build the table files into when using mk_files. Defaults to the current
128directory.
129
130=item mk_file_dir
131
132Set true and if the file needs to written to a directory that doesn't exist,
133it will be created first.
134
135=item on_exists [Default:replace]
136
137What to do if we are running with mk_files and a file already exists where we
138want to write our output. One of "skip", "die", "replace", "insert".
139The default is die.
140
141B<replace> - Over-write the existing file with the new one, clobbering anything
142already there.
143
144B<skip> - Leave the origional file as it was and don't write the new version
145anywhere.
146
147B<die> - Die with an existing file error.
148
149B<insert> - Insert the generated output into the file bewteen a set of special
150comments (defined by the following options.) Any code between the comments will
151be overwritten (ie the results from a previous produce) but the rest of the file
152is left alone (your custom code).
153This is particularly usefull for code generation as it allows you to
154generate schema derived code and then add your own custom code using it to file.
155Then when the scheam changes you just re-produce and insert the new code.
156
157=item insert_comment_start
158
159The comment to look for in the file when on_exists is insert. Default is
160C<SQLF INSERT START>. Must appear on it own line, with only whitespace either
161side, to be recognised.
162
163=item insert_comment_end
164
165The end comment to look for in the file when on_exists is insert. Default is
166C<SQLF INSERT END>. Must appear on it own line, with only whitespace either
167side, to be recognised.
168
169=back
170
171=cut
172
173# -------------------------------------------------------------------
174
175use strict;
176
177use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
178$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
179$DEBUG = 0 unless defined $DEBUG;
180
181use File::Path;
182use Template;
183use Data::Dumper;
184use Exporter;
185use base qw(Exporter);
186@EXPORT_OK = qw(produce);
187
188use SQL::Translator::Utils 'debug';
189
190my $Translator;
191
192sub produce {
193 $Translator = shift;
194 local $DEBUG = $Translator->debug;
195 my $scma = $Translator->schema;
196 my $pargs = $Translator->producer_args;
197 my $file = $pargs->{'tt_table'} or die "No template file given!";
198 $pargs->{on_exists} ||= "die";
199
200 debug "Processing template $file\n";
201 my $out;
202 my $tt = Template->new(
203 DEBUG => $DEBUG,
204 ABSOLUTE => 1, # Set so we can use from the command line sensibly
205 RELATIVE => 1, # Maybe the cmd line code should set it! Security!
206 %$pargs, # Allow any TT opts to be passed in the producer_args
207 ) || die "Failed to initialize Template object: ".Template->error;
208
209 for my $tbl ( sort {$a->order <=> $b->order} $scma->get_tables ) {
210 my $outtmp;
211 $tt->process( $file, {
212 translator => $Translator,
213 schema => $scma,
214 table => $tbl,
215 }, \$outtmp )
216 or die "Error processing template '$file' for table '".$tbl->name
217 ."': ".$tt->error;
218 $out .= $outtmp;
219
220 # Write out the file...
221 write_file( table_file($tbl), $outtmp ) if $pargs->{mk_files};
222 }
223
224 return $out;
225};
226
227# Work out the filename for a given table.
228sub table_file {
229 my ($tbl) = shift;
230 my $pargs = $Translator->producer_args;
231 my $root = $pargs->{mk_files_base};
232 my $ext = $pargs->{mk_file_ext};
233 return "$root/$tbl.$ext";
234}
235
236# Write the src given to the file given, handling the on_exists arg.
237sub write_file {
238 my ($file, $src) = @_;
239 my $pargs = $Translator->producer_args;
240 my $root = $pargs->{mk_files_base};
241
242 if ( -e $file ) {
243 if ( $pargs->{on_exists} eq "skip" ) {
244 warn "Skipping existing $file\n";
245 return 1;
246 }
247 elsif ( $pargs->{on_exists} eq "die" ) {
248 die "File $file already exists.\n";
249 }
250 elsif ( $pargs->{on_exists} eq "replace" ) {
251 warn "Replacing $file.\n";
252 }
253 elsif ( $pargs->{on_exists} eq "insert" ) {
254 warn "Inserting into $file.\n";
255 $src = insert_code($file, $src);
256 }
257 else {
258 die "Unknown on_exists action: $pargs->{on_exists}\n";
259 }
260 }
261 else {
262 warn "Creating $file.\n";
263 }
264
265 my ($dir) = $file =~ m!^(.*)/!; # Want greedy, eveything before the last /
266 if ( $dir and not -d $dir and $pargs->{mk_file_dir} ) { mkpath($dir); }
267
268 debug "Writing to $file\n";
269 open( FILE, ">$file") or die "Error opening file $file : $!\n";
270 print FILE $src;
271 close(FILE);
272}
273
274# Reads file and inserts code between the insert comments and returns the new
275# source.
276sub insert_code {
277 my ($file, $src) = @_;
278 my $pargs = $Translator->producer_args;
279 my $cstart = $pargs->{insert_comment_start} || "SQLF_INSERT_START";
280 my $cend = $pargs->{insert_comment_end} || "SQLF_INSERT_END";
281
282 # Slurp in the origional file
283 open ( FILE, "<", "$file") or die "Error opening file $file : $!\n";
284 local $/ = undef;
285 my $orig = <FILE>;
286 close(FILE);
287
288 # Insert the new code between the insert comments
289 unless (
290 $orig =~ s/^\s*?$cstart\s*?\n.*?^\s*?$cend\s*?\n/\n$cstart\n$src\n$cend\n/ms
291 ) {
292 warn "No insert done\n";
293 }
294
295 return $orig;
296}
297
2981;
299
300# -------------------------------------------------------------------
301
302=pod
303
304=head1 AUTHOR
305
306Mark Addison E<lt>grommit@users.sourceforge.netE<gt>.
307
308=head1 TODO
309
310+ Some tests for the various on exists options (they have been tested
311implicitley through use in a project but need some proper tests).
312
313+ More docs code generation strategies.
314
315+ Better hooks for filename generation.
316
317+ Better integration with TTSchema.
318
319=head1 SEE ALSO
320
321SQL::Translator.
322
323=cut