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