Remove copyright headers from individual scripts
[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
156# -------------------------------------------------------------------
157
158use strict;
159
da06ac74 160use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
11ad2df9 161$VERSION = '1.59';
0be5b227 162$DEBUG = 0 unless defined $DEBUG;
163
164use File::Path;
165use Template;
166use Data::Dumper;
167use Exporter;
168use base qw(Exporter);
169@EXPORT_OK = qw(produce);
170
171use SQL::Translator::Utils 'debug';
172
173my $Translator;
174
175sub produce {
176 $Translator = shift;
177 local $DEBUG = $Translator->debug;
178 my $scma = $Translator->schema;
179 my $pargs = $Translator->producer_args;
180 my $file = $pargs->{'tt_table'} or die "No template file given!";
181 $pargs->{on_exists} ||= "die";
182
183 debug "Processing template $file\n";
184 my $out;
185 my $tt = Template->new(
186 DEBUG => $DEBUG,
187 ABSOLUTE => 1, # Set so we can use from the command line sensibly
188 RELATIVE => 1, # Maybe the cmd line code should set it! Security!
189 %$pargs, # Allow any TT opts to be passed in the producer_args
190 ) || die "Failed to initialize Template object: ".Template->error;
191
192 for my $tbl ( sort {$a->order <=> $b->order} $scma->get_tables ) {
193 my $outtmp;
194 $tt->process( $file, {
195 translator => $Translator,
196 schema => $scma,
197 table => $tbl,
198 }, \$outtmp )
199 or die "Error processing template '$file' for table '".$tbl->name
200 ."': ".$tt->error;
201 $out .= $outtmp;
202
203 # Write out the file...
204 write_file( table_file($tbl), $outtmp ) if $pargs->{mk_files};
205 }
206
207 return $out;
208};
209
210# Work out the filename for a given table.
211sub table_file {
212 my ($tbl) = shift;
213 my $pargs = $Translator->producer_args;
214 my $root = $pargs->{mk_files_base};
215 my $ext = $pargs->{mk_file_ext};
216 return "$root/$tbl.$ext";
217}
218
219# Write the src given to the file given, handling the on_exists arg.
220sub write_file {
221 my ($file, $src) = @_;
222 my $pargs = $Translator->producer_args;
223 my $root = $pargs->{mk_files_base};
224
225 if ( -e $file ) {
226 if ( $pargs->{on_exists} eq "skip" ) {
227 warn "Skipping existing $file\n";
228 return 1;
229 }
230 elsif ( $pargs->{on_exists} eq "die" ) {
231 die "File $file already exists.\n";
232 }
233 elsif ( $pargs->{on_exists} eq "replace" ) {
234 warn "Replacing $file.\n";
235 }
236 elsif ( $pargs->{on_exists} eq "insert" ) {
237 warn "Inserting into $file.\n";
238 $src = insert_code($file, $src);
239 }
240 else {
241 die "Unknown on_exists action: $pargs->{on_exists}\n";
242 }
243 }
244 else {
52e2730e 245 if ( my $interactive = -t STDIN && -t STDOUT ) {
246 warn "Creating $file.\n";
247 }
0be5b227 248 }
249
250 my ($dir) = $file =~ m!^(.*)/!; # Want greedy, eveything before the last /
251 if ( $dir and not -d $dir and $pargs->{mk_file_dir} ) { mkpath($dir); }
252
253 debug "Writing to $file\n";
254 open( FILE, ">$file") or die "Error opening file $file : $!\n";
255 print FILE $src;
256 close(FILE);
257}
258
259# Reads file and inserts code between the insert comments and returns the new
260# source.
261sub insert_code {
262 my ($file, $src) = @_;
263 my $pargs = $Translator->producer_args;
264 my $cstart = $pargs->{insert_comment_start} || "SQLF_INSERT_START";
265 my $cend = $pargs->{insert_comment_end} || "SQLF_INSERT_END";
266
267 # Slurp in the origional file
268 open ( FILE, "<", "$file") or die "Error opening file $file : $!\n";
269 local $/ = undef;
270 my $orig = <FILE>;
271 close(FILE);
272
273 # Insert the new code between the insert comments
274 unless (
275 $orig =~ s/^\s*?$cstart\s*?\n.*?^\s*?$cend\s*?\n/\n$cstart\n$src\n$cend\n/ms
276 ) {
277 warn "No insert done\n";
278 }
279
280 return $orig;
281}
282
2831;
284
285# -------------------------------------------------------------------
286
287=pod
288
289=head1 AUTHOR
290
291Mark Addison E<lt>grommit@users.sourceforge.netE<gt>.
292
293=head1 TODO
294
1672901e 295- Some tests for the various on exists options (they have been tested
0be5b227 296implicitley through use in a project but need some proper tests).
297
1672901e 298- More docs on code generation strategies.
0be5b227 299
1672901e 300- Better hooks for filename generation.
0be5b227 301
1672901e 302- Integrate with L<TT::Base> and L<TTSchema>.
0be5b227 303
304=head1 SEE ALSO
305
306SQL::Translator.
307
308=cut