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