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