File Coverage

blib/lib/SQL/Translator/Producer/TT/Table.pm
Criterion Covered Total %
statement 56 77 72.7
branch 6 26 23.0
condition 5 17 29.4
subroutine 11 12 91.6
pod 0 4 0.0
total 78 136 57.3


line stmt bran cond sub pod time code
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 which is the file
49             name of the template to use. This template will be passed a template
50             var of C, which is the current
51             L table we are producing,
52             which you can then use to walk the schema via the methods documented
53             in that module. You also get C as a shortcut to the
54             L for the table and C,
55             the L 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 for a more complete example.
67              
68             You can also set any of the options used to initialize 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 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             It's also particularly 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
101             added and placed in the directory C.
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 - Over-write the existing file with the new one, clobbering
124             anything already there.
125              
126             B - Leave the original file as it was and don't write the new
127             version anywhere.
128              
129             B - Die with an existing file error.
130              
131             B - Insert the generated output into the file between 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. Default
143             is C. 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.
149             Default is C. Must appear on it own line, with only
150             whitespace either side, to be recognised.
151              
152             =back
153              
154             =cut
155              
156 1     1   2696 use strict;
  1         3  
  1         49  
157 1     1   8 use warnings;
  1         3  
  1         122  
158              
159             our ($DEBUG, @EXPORT_OK);
160             our $VERSION = '1.66';
161             $DEBUG = 0 unless defined $DEBUG;
162              
163 1     1   9 use File::Path;
  1         2  
  1         106  
164 1     1   8 use Template;
  1         3  
  1         36  
165 1     1   7 use Data::Dumper;
  1         2  
  1         116  
166 1     1   9 use Exporter;
  1         3  
  1         53  
167 1     1   7 use base qw(Exporter);
  1         2  
  1         249  
168             @EXPORT_OK = qw(produce);
169              
170 1     1   10 use SQL::Translator::Utils 'debug';
  1         3  
  1         1450  
171              
172             my $Translator;
173              
174             sub produce {
175 1     1 0 3 $Translator = shift;
176 1         6 local $DEBUG = $Translator->debug;
177 1         36 my $scma = $Translator->schema;
178 1         37 my $pargs = $Translator->producer_args;
179 1 50       6 my $file = $pargs->{'tt_table'} or die "No template file given!";
180 1   50     6 $pargs->{on_exists} ||= "die";
181              
182 1         8 debug "Processing template $file\n";
183 1         2 my $out;
184 1   50     25 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              
191 1         27384 for my $tbl (sort { $a->order <=> $b->order } $scma->get_tables) {
  1         14  
192 2         7 my $outtmp;
193 2 50       22 $tt->process(
194             $file,
195             {
196             translator => $Translator,
197             schema => $scma,
198             table => $tbl,
199             },
200             \$outtmp
201             ) or die "Error processing template '$file' for table '" . $tbl->name . "': " . $tt->error;
202 2         486 $out .= $outtmp;
203              
204             # Write out the file...
205 2 50       17 write_file(table_file($tbl), $outtmp) if $pargs->{mk_files};
206             }
207              
208 1         17 return $out;
209             }
210              
211             # Work out the filename for a given table.
212             sub table_file {
213 2     2 0 5 my ($tbl) = shift;
214 2         117 my $pargs = $Translator->producer_args;
215 2         9 my $root = $pargs->{mk_files_base};
216 2         8 my $ext = $pargs->{mk_file_ext};
217 2         17 return "$root/$tbl.$ext";
218             }
219              
220             # Write the src given to the file given, handling the on_exists arg.
221             sub write_file {
222 2     2 0 72 my ($file, $src) = @_;
223 2         82 my $pargs = $Translator->producer_args;
224 2         8 my $root = $pargs->{mk_files_base};
225              
226 2 50       152 if (-e $file) {
227 0 0       0 if ($pargs->{on_exists} eq "skip") {
    0          
    0          
    0          
228 0         0 warn "Skipping existing $file\n";
229 0         0 return 1;
230             } elsif ($pargs->{on_exists} eq "die") {
231 0         0 die "File $file already exists.\n";
232             } elsif ($pargs->{on_exists} eq "replace") {
233 0         0 warn "Replacing $file.\n";
234             } elsif ($pargs->{on_exists} eq "insert") {
235 0         0 warn "Inserting into $file.\n";
236 0         0 $src = insert_code($file, $src);
237             } else {
238 0         0 die "Unknown on_exists action: $pargs->{on_exists}\n";
239             }
240             } else {
241 2 50 33     25 if (my $interactive = -t STDIN && -t STDOUT) {
242 0         0 warn "Creating $file.\n";
243             }
244             }
245              
246 2         15 my ($dir) = $file =~ m!^(.*)/!; # Want greedy, everything before the last /
247 2 0 33     51 if ($dir and not -d $dir and $pargs->{mk_file_dir}) { mkpath($dir); }
  0   33     0  
248              
249 2         18 debug "Writing to $file\n";
250 2 50       347 open(FILE, ">$file") or die "Error opening file $file : $!\n";
251 2         34 print FILE $src;
252 2         169 close(FILE);
253             }
254              
255             # Reads file and inserts code between the insert comments and returns the new
256             # source.
257             sub insert_code {
258 0     0 0   my ($file, $src) = @_;
259 0           my $pargs = $Translator->producer_args;
260 0   0       my $cstart = $pargs->{insert_comment_start} || "SQLF_INSERT_START";
261 0   0       my $cend = $pargs->{insert_comment_end} || "SQLF_INSERT_END";
262              
263             # Slurp in the original file
264 0 0         open(FILE, "<", "$file") or die "Error opening file $file : $!\n";
265 0           local $/ = undef;
266 0           my $orig = ;
267 0           close(FILE);
268              
269             # Insert the new code between the insert comments
270 0 0         unless ($orig =~ s/^\s*?$cstart\s*?\n.*?^\s*?$cend\s*?\n/\n$cstart\n$src\n$cend\n/ms) {
271 0           warn "No insert done\n";
272             }
273              
274 0           return $orig;
275             }
276              
277             1;
278              
279             =pod
280              
281             =head1 AUTHOR
282              
283             Mark Addison Egrommit@users.sourceforge.netE.
284              
285             =head1 TODO
286              
287             - Some tests for the various on exists options (they have been tested
288             implicitly through use in a project but need some proper tests).
289              
290             - More docs on code generation strategies.
291              
292             - Better hooks for filename generation.
293              
294             - Integrate with L and
295             L.
296              
297             =head1 SEE ALSO
298              
299             SQL::Translator.
300              
301             =cut