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   556 use strict;
  1         2  
  1         32  
157 1     1   5 use warnings;
  1         2  
  1         82  
158              
159             our ( $DEBUG, @EXPORT_OK );
160             our $VERSION = '1.62';
161             $DEBUG = 0 unless defined $DEBUG;
162              
163 1     1   6 use File::Path;
  1         2  
  1         59  
164 1     1   6 use Template;
  1         1  
  1         22  
165 1     1   4 use Data::Dumper;
  1         2  
  1         47  
166 1     1   7 use Exporter;
  1         4  
  1         50  
167 1     1   9 use base qw(Exporter);
  1         2  
  1         206  
168             @EXPORT_OK = qw(produce);
169              
170 1     1   7 use SQL::Translator::Utils 'debug';
  1         3  
  1         980  
171              
172             my $Translator;
173              
174             sub produce {
175 1     1 0 3 $Translator = shift;
176 1         6 local $DEBUG = $Translator->debug;
177 1         30 my $scma = $Translator->schema;
178 1         46 my $pargs = $Translator->producer_args;
179 1 50       6 my $file = $pargs->{'tt_table'} or die "No template file given!";
180 1   50     5 $pargs->{on_exists} ||= "die";
181              
182 1         8 debug "Processing template $file\n";
183 1         2 my $out;
184 1   50     28 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         23967 for my $tbl ( sort {$a->order <=> $b->order} $scma->get_tables ) {
  1         367  
192 2         7 my $outtmp;
193 2 50       22 $tt->process( $file, {
194             translator => $Translator,
195             schema => $scma,
196             table => $tbl,
197             }, \$outtmp )
198             or die "Error processing template '$file' for table '".$tbl->name
199             ."': ".$tt->error;
200 2         291 $out .= $outtmp;
201              
202             # Write out the file...
203 2 50       14 write_file( table_file($tbl), $outtmp ) if $pargs->{mk_files};
204             }
205              
206 1         30 return $out;
207             };
208              
209             # Work out the filename for a given table.
210             sub table_file {
211 2     2 0 5 my ($tbl) = shift;
212 2         53 my $pargs = $Translator->producer_args;
213 2         8 my $root = $pargs->{mk_files_base};
214 2         5 my $ext = $pargs->{mk_file_ext};
215 2         11 return "$root/$tbl.$ext";
216             }
217              
218             # Write the src given to the file given, handling the on_exists arg.
219             sub write_file {
220 2     2 0 49 my ($file, $src) = @_;
221 2         42 my $pargs = $Translator->producer_args;
222 2         7 my $root = $pargs->{mk_files_base};
223              
224 2 50       67 if ( -e $file ) {
225 0 0       0 if ( $pargs->{on_exists} eq "skip" ) {
    0          
    0          
    0          
226 0         0 warn "Skipping existing $file\n";
227 0         0 return 1;
228             }
229             elsif ( $pargs->{on_exists} eq "die" ) {
230 0         0 die "File $file already exists.\n";
231             }
232             elsif ( $pargs->{on_exists} eq "replace" ) {
233 0         0 warn "Replacing $file.\n";
234             }
235             elsif ( $pargs->{on_exists} eq "insert" ) {
236 0         0 warn "Inserting into $file.\n";
237 0         0 $src = insert_code($file, $src);
238             }
239             else {
240 0         0 die "Unknown on_exists action: $pargs->{on_exists}\n";
241             }
242             }
243             else {
244 2 50 33     24 if ( my $interactive = -t STDIN && -t STDOUT ) {
245 0         0 warn "Creating $file.\n";
246             }
247             }
248              
249 2         15 my ($dir) = $file =~ m!^(.*)/!; # Want greedy, everything before the last /
250 2 0 33     48 if ( $dir and not -d $dir and $pargs->{mk_file_dir} ) { mkpath($dir); }
  0   33     0  
251              
252 2         19 debug "Writing to $file\n";
253 2 50       147 open( FILE, ">$file") or die "Error opening file $file : $!\n";
254 2         37 print FILE $src;
255 2         88 close(FILE);
256             }
257              
258             # Reads file and inserts code between the insert comments and returns the new
259             # source.
260             sub insert_code {
261 0     0 0   my ($file, $src) = @_;
262 0           my $pargs = $Translator->producer_args;
263 0   0       my $cstart = $pargs->{insert_comment_start} || "SQLF_INSERT_START";
264 0   0       my $cend = $pargs->{insert_comment_end} || "SQLF_INSERT_END";
265              
266             # Slurp in the original file
267 0 0         open ( FILE, "<", "$file") or die "Error opening file $file : $!\n";
268 0           local $/ = undef;
269 0           my $orig = ;
270 0           close(FILE);
271              
272             # Insert the new code between the insert comments
273 0 0         unless (
274             $orig =~ s/^\s*?$cstart\s*?\n.*?^\s*?$cend\s*?\n/\n$cstart\n$src\n$cend\n/ms
275             ) {
276 0           warn "No insert done\n";
277             }
278              
279 0           return $orig;
280             }
281              
282             1;
283              
284             =pod
285              
286             =head1 AUTHOR
287              
288             Mark Addison Egrommit@users.sourceforge.netE.
289              
290             =head1 TODO
291              
292             - Some tests for the various on exists options (they have been tested
293             implicitly through use in a project but need some proper tests).
294              
295             - More docs on code generation strategies.
296              
297             - Better hooks for filename generation.
298              
299             - Integrate with L and
300             L.
301              
302             =head1 SEE ALSO
303              
304             SQL::Translator.
305              
306             =cut