Remove copyright headers from individual scripts
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / TT / Table.pm
1 package SQL::Translator::Producer::TT::Table;
2
3 =pod
4
5 =head1 NAME
6
7 SQL::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
44 Produces schema output using a given Template Tookit template,
45 processing that template for each table in the schema. Optionally
46 allows you to write the result for each table to a separate file.
47
48 It needs one additional producer_arg of C<tt_table> which is the file
49 name of the template to use.  This template will be passed a template
50 var of C<table>, which is the current
51 L<SQL::Translator::Producer::Table> table we are producing, which you
52 can then use to walk the schema via the methods documented in that
53 module. You also get L<schema> as a shortcut to the
54 L<SQL::Translator::Producer::Schema> for the table and C<translator>,
55 the L<SQL::Translator> object for this parse in case you want to get
56 access to any of the options etc set here.
57
58 Here'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
66 See F<t/data/template/table.tt> for a more complete example.
67
68 You can also set any of the options used to initiallize the Template
69 object by adding them to your producer_args. See Template Toolkit docs
70 for details of the options.
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
81 If you set C<mk_files> and its additional options the producer will
82 write a separate file for each table in the schema. This is useful for
83 producing things like HTML documentation where every table gets its
84 own page (you could also use TTSchema producer to add an index page).
85 Its also particulary good for code generation where you want to
86 produce a class file per table.
87
88 =head1 OPTIONS
89
90 =over 4
91
92 =item tt_table
93
94 File name of the template to run for each table.
95
96 =item mk_files
97
98 Set to true to output a file for each table in the schema (as well as
99 returning the whole lot back to the Translalor and hence STDOUT). The
100 file will be named after the table, with the optional C<mk_files_ext>
101 added and placed in the directory C<mk_files_base>.
102
103 =item mk_files_ext
104
105 Extension (without the dot) to add to the filename when using mk_files.
106
107 =item mk_files_base = DIR
108
109 Dir to build the table files into when using mk_files. Defaults to the
110 current directory.
111
112 =item mk_file_dir
113
114 Set true and if the file needs to written to a directory that doesn't
115 exist, it will be created first.
116
117 =item on_exists [Default:replace]
118
119 What to do if we are running with mk_files and a file already exists
120 where we want to write our output. One of "skip", "die", "replace",
121 "insert".  The default is die.
122
123 B<replace> - Over-write the existing file with the new one, clobbering
124 anything already there.
125
126 B<skip> - Leave the origional file as it was and don't write the new
127 version anywhere.
128
129 B<die> - Die with an existing file error.
130
131 B<insert> - Insert the generated output into the file bewteen a set of
132 special comments (defined by the following options.) Any code between
133 the comments will be overwritten (ie the results from a previous
134 produce) but the rest of the file is left alone (your custom code).
135 This is particularly useful for code generation as it allows you to
136 generate schema derived code and then add your own custom code
137 to the file.  Then when the schema changes you just re-produce to
138 insert the new code.
139
140 =item insert_comment_start
141
142 The comment to look for in the file when on_exists is C<insert>. Default
143 is C<SQLF INSERT START>. Must appear on it own line, with only
144 whitespace either side, to be recognised.
145
146 =item insert_comment_end
147
148 The end comment to look for in the file when on_exists is C<insert>.
149 Default is C<SQLF INSERT END>. Must appear on it own line, with only
150 whitespace either side, to be recognised.
151
152 =back
153
154 =cut
155
156 # -------------------------------------------------------------------
157
158 use strict;
159
160 use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
161 $VERSION = '1.59';
162 $DEBUG   = 0 unless defined $DEBUG;
163
164 use File::Path;
165 use Template;
166 use Data::Dumper;
167 use Exporter;
168 use base qw(Exporter);
169 @EXPORT_OK = qw(produce);
170
171 use SQL::Translator::Utils 'debug';
172
173 my $Translator;
174
175 sub 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.
211 sub 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.
220 sub 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 {
245         if ( my $interactive = -t STDIN && -t STDOUT ) {
246             warn "Creating $file.\n";
247         }
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.
261 sub 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
283 1;
284
285 # -------------------------------------------------------------------
286
287 =pod
288
289 =head1 AUTHOR
290
291 Mark Addison E<lt>grommit@users.sourceforge.netE<gt>.
292
293 =head1 TODO
294
295 - Some tests for the various on exists options (they have been tested
296 implicitley through use in a project but need some proper tests).
297
298 - More docs on code generation strategies.
299
300 - Better hooks for filename generation.
301
302 - Integrate with L<TT::Base> and L<TTSchema>.
303
304 =head1 SEE ALSO
305
306 SQL::Translator.
307
308 =cut