6c254c2a5e95caeca164af2dee74cd0ca2614b9d
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / Turnkey.pm
1 package SQL::Translator::Producer::Turnkey;
2
3 use strict;
4 use vars qw[ $VERSION $DEBUG ];
5 $VERSION = sprintf "%d.%02d", q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/;
6 $DEBUG   = 1 unless defined $DEBUG;
7
8 use SQL::Translator::Schema::Constants;
9 use SQL::Translator::Schema::Graph;
10 use SQL::Translator::Schema::Graph::HyperEdge;
11 use SQL::Translator::Utils qw(header_comment);
12 use Data::Dumper;
13 use Template;
14
15 #{
16 #  local $/;
17 #  my $data = <SQL::Translator::Producer::Turnkey::DATA>;
18 #  eval { $data };
19 #  warn $@ if $@;
20 #}
21
22 my %producer2dsn = (
23     MySQL      => 'mysql',
24     PostgreSQL => 'Pg',
25     Oracle     => 'Oracle',
26 );
27
28 # -------------------------------------------------------------------
29 sub produce {
30     my $t             = shift;
31         my $create        = undef;
32     my $args          = $t->producer_args;
33     my $no_comments   = $t->no_comments;
34         my $baseclass     = $args->{'main_pkg_name'} || $t->format_package_name('DBI');
35         my $graph         = SQL::Translator::Schema::Graph->new(translator => $t, baseclass => $baseclass);
36
37         my $parser_type   = (split /::/, $t->parser_type)[-1];
38
39     local $DEBUG      = $t->debug;
40
41         my %meta          = (
42                                                  format_fk => $t->format_fk_name,
43                                                  template  => $args->{'template'}      || '',
44                                                  baseclass => $baseclass,
45                                                  db_user   => $args->{'db_user'}       || '',
46                                                  db_pass   => $args->{'db_pass'}       || '',
47                                                  parser    => $t->parser_type,
48                                                  producer  => __PACKAGE__,
49                                                  dsn       => $args->{'dsn'} || sprintf( 'dbi:%s:_', $producer2dsn{ $parser_type }
50                                                                                                                                  ? $producer2dsn{ $parser_type }
51                                                                                                                                  : $parser_type
52                                                                                                                            )
53                                                  );
54
55     #
56     # create methods
57     #
58         foreach my $node_from ($graph->node_values){
59           next unless $node_from->table->is_data;
60           foreach my $cedge ( $node_from->compoundedges ){
61
62                 my $hyperedge = SQL::Translator::Schema::Graph::HyperEdge->new();
63
64                 my $node_to;
65                 foreach my $edge ($cedge->edges){
66                   if($edge->thisnode->name eq $node_from->name){
67                         $hyperedge->vianode($edge->thatnode);
68
69                         if($edge->thatnode->name ne $cedge->via->name){
70                           $node_to ||= $graph->node($edge->thatnode->table->name);
71                         }
72
73                         $hyperedge->push_thisnode($edge->thisnode);
74                         $hyperedge->push_thisfield($edge->thisfield);
75                         $hyperedge->push_thisviafield($edge->thatfield);
76
77                   } else {
78                         if($edge->thisnode->name ne $cedge->via->name){
79                           $node_to ||= $graph->node($edge->thisnode->table->name);
80                         }
81
82                         $hyperedge->push_thatnode($edge->thisnode);
83                         $hyperedge->push_thatfield($edge->thisfield);
84                         $hyperedge->push_thatviafield($edge->thatfield);
85                   }
86                 }
87                    if($hyperedge->count_thisnode == 1 and $hyperedge->count_thatnode == 1){ $hyperedge->type('one2one')   }
88                 elsif($hyperedge->count_thisnode  > 1 and $hyperedge->count_thatnode == 1){ $hyperedge->type('many2one')  }
89                 elsif($hyperedge->count_thisnode == 1 and $hyperedge->count_thatnode  > 1){ $hyperedge->type('one2many')  }
90                 elsif($hyperedge->count_thisnode  > 1 and $hyperedge->count_thatnode  > 1){ $hyperedge->type('many2many') }
91
92                 if(scalar($hyperedge->thisnode) > 1){
93 warn $hyperedge;
94 warn $hyperedge->type;
95                   foreach my $thisnode ( $hyperedge->thisnode ){
96 #warn $thisnode;
97 #warn $hyperedge->thatnode;
98 warn $thisnode->name;
99
100 eval { $hyperedge->thatnode->name }; warn $@ if $@;
101
102 warn $hyperedge->thatnode if(defined($hyperedge->thatnode));
103
104
105 #warn $hyperedge->thisfield->name;
106 #warn $hyperedge->thatfield->name;
107 #warn $hyperedge->thisviafield->name;
108 #warn $hyperedge->thatviafield->name;
109                   }
110                 }
111 exit;
112
113 #warn $node_from->name ."\t". $node_to->name ."\t". $hyperedge->type ."\t". $hyperedge->vianode->name;
114
115                 $node_from->push_hyperedges($hyperedge);
116           }
117         }
118
119         $meta{"nodes"} = $graph->node;
120         return(translateForm($t, \%meta));
121 }
122
123 sub translateForm {
124   my $t = shift;
125   my $meta = shift;
126   my $args = $t->producer_args;
127   my $type = $meta->{'template'};
128   my $tt2;
129   $tt2 = template($type);
130   my $template = Template->new({
131                                                                 EVAL_PERL => 1
132                                                            });
133
134   my $result;
135   # specify input filename, or file handle, text reference, etc.
136   # process input template, substituting variables
137   $template->process(\$tt2, $meta, \$result) || die $template->error();
138   return($result);
139 }
140
141 1;
142
143 # -------------------------------------------------------------------
144
145 =pod
146
147 =head1 NAME
148
149 SQL::Translator::Producer::Turnkey - create Turnkey classes from schema
150
151 =head1 SYNOPSIS
152
153 Creates output for use with the Turnkey project.
154
155 =head1 SEE ALSO
156
157 L<http://turnkey.sourceforge.net>.
158
159 =head1 AUTHORS
160
161 Allen Day E<lt>allenday@ucla.eduE<gt>
162 Ying Zhang E<lt>zyolive@yahoo.comE<gt>,
163 Brian O\'Connor E<lt>brian.oconnor@excite.comE<gt>.
164
165 =cut
166
167 sub template {
168   my $type = shift;
169
170 ###########################################
171 # Here documents for the tt2 templates    #
172 ###########################################
173
174   if($type eq 'classdbi'){
175         return <<EOF;
176 [% MACRO printPackage(node) BLOCK %]
177 # --------------------------------------------
178
179 package [% node.name %];
180 use base '[% node.base %]';
181 use Class::DBI::Pager;
182
183 [% node.name %]->set_up_table('[% node.table.name %]');
184 [% printPKAccessors(node.primary_key, node.table.name) %]
185 [% printHasA(node.edges, node) %]
186 [% printHasMany(node.edges, node) %]
187 [% printHasCompound(node.compoundedges, node.hyperedges, node.name) %]
188 [% printHasFriendly(node) %]
189 [% END %]
190
191 [% MACRO printPKAccessors(array, name) BLOCK %]
192 #
193 # Primary key accessors
194 #
195 [% FOREACH item = array %]
196 sub id { shift->[% item %] }
197 sub [% name %] { shift->[% item %] }
198 [% END %]
199 [% END %]
200
201 [% MACRO printHasA(edges, name) BLOCK %]
202 #
203 # Has A
204 #
205 [% FOREACH edge = edges %]
206   [%- IF edge.type == 'import' -%]
207 [% node.name %]->has_a([% edge.thisfield.name %] => '[% edge.thatnode.name %]');
208     [%- IF node.has(edge.thatnode.name) < 2 %]
209 sub [% edge.thatnode.table.name %] { return shift->[% edge.thisfield.name %] }
210     [%- ELSE %]
211 sub [% format_fk(edge.thisnode.table.name,edge.thisfield.name) %] { return shift->[% edge.thisfield.name %] }
212     [%- END %]
213   [%- END %]
214 [% END %]
215 [% END %]
216
217 [% MACRO printHasMany(edges, node) BLOCK %]
218 #
219 # Has Many
220 #
221 [% FOREACH edge = edges %]
222   [%- IF edge.type == 'export' -%]
223 [% node.name %]->has_many([% edge.thatnode.table.name %]_[% edge.thatfield.name %], '[% edge.thatnode.name %]' => '[% edge.thatfield.name %]');
224     [%- IF node.via(edge.thatnode.name) >= 1 %]
225 sub [% edge.thatnode.table.name %]_[% format_fk(edge.thatnode.table.name,edge.thatfield.name) %]s { return shift->[% edge.thatnode.table.name %]_[% edge.thatfield.name %] }
226     [%- ELSIF edge.thatnode.table.is_data %]
227 sub [% edge.thatnode.table.name %]s { return shift->[% edge.thatnode.table.name %]_[% edge.thatfield.name %] }
228     [%- END %]
229   [%- END %]
230 [% END %]
231 [% END %]
232
233 [% MACRO printHasCompound(cedges,hedges,name) BLOCK %]
234 #
235 # Has Compound Many
236 #
237 [% FOREACH cedge = cedges %]
238 [% FOREACH edge = cedge.edges %]
239   [%- NEXT IF edge.thisnode.name != name -%]
240 sub [% cedge.via.table.name %]_[% format_fk(edge.thatnode.table.name,edge.thatfield.name) %]s { return shift->[% cedge.via.table.name %]_[% edge.thatfield.name %] }
241 [% END %]
242 [% END %]
243 [% FOREACH h = hedges %]
244   [%- NEXT IF h.thisnode.name != name -%]
245   [%- IF h.type == 'one2one' %]
246 1sub [% h.thatnode.table.name %]s { my \$self = shift; return map \$_->[% h.thatviafield.name %], \$self->[% h.vianode.table.name %]_[% h.thisviafield.name %] }
247   [%- ELSIF h.type == 'one2many' %]
248     [% FOREACH thisnode = h.thisnode %]
249 2
250  h.thatnode.name=[% h.thatnode.name %]
251  h.thatfield.name [% h.thatfield.name %]
252  h.thisnode.name=[% thisnode.name %]
253  h.thisfield.name=[% thisfield.name %]
254 2
255     [% END %]
256   [%- ELSIF h.type == 'many2one' %]
257 3sub [% h.thatnode.table.name %]s { my \$self = shift; return map \$_->[% h.thatviafield.name %], \$self->[% h.vianode.table.name %]_[% h.thisviafield.name %] }
258   [%- ELSIF h.type == 'many2many' %]
259 4
260   [%- END %]
261 [% END %]
262 [% END %]
263
264 [% MACRO printHasFriendly(node) BLOCK %]
265 #
266 # Has Friendly
267 #
268 hello, sailor!
269 [% END %]
270
271 [% MACRO printList(array) BLOCK %][% FOREACH item = array %][% item %] [% END %][% END %]
272 package [% baseclass %];
273
274 # Created by SQL::Translator::Producer::Turnkey
275 # Template used: classdbi
276
277 use strict;
278 use base qw(Class::DBI::Pg);
279
280 [% baseclass %]->set_db('Main', '[% db_str  %]', '[% db_user %]', '[% db_pass %]');
281
282 [% FOREACH node = nodes %]
283     [% printPackage(node.value) %]
284 [% END %]
285 EOF
286
287 } elsif($type eq 'atom'){
288   return <<'EOF';
289 [% ###### DOCUMENT START ###### %]
290
291 [% FOREACH node = linkable %]
292
293 ##############################################
294
295 package Durian::Atom::[% node.key FILTER ucfirst %];
296
297 [% pname = node.key FILTER ucfirst%]
298 [% pkey = "Durian::Model::${pname}" %]
299
300 use base qw(Durian::Atom);
301 use Data::Dumper;
302
303 sub can_render {
304         return 1;
305 }
306
307 sub render {
308         my $self = shift;
309         my $dbobject = shift;
310     # Assumption here that if it's not rendering on it's own dbobject
311     # then it's a list. This will be updated when AtomLists are implemented -boconnor
312         if(ref($dbobject) eq 'Durian::Model::[% node.key FILTER ucfirst %]') {
313                 return(_render_record($dbobject));
314         }
315         else { return(_render_list($dbobject)); }
316 }
317
318 sub _render_record {
319         my $dbobject = shift;
320         my @output = ();
321         my $row = {};
322         my $field_hash = {};
323         [% FOREACH field = nodes.$pkey.columns_essential %]
324         $field_hash->{[% field %]} = $dbobject->[% field %]();
325     [% END %]
326         $row->{data} = $field_hash;
327         $row->{id} = $dbobject->id();
328         push @output, $row;
329         return(\@output);
330 }
331
332 sub _render_list {
333         my $dbobject = shift;
334         my @output = ();
335         my @objects = $dbobject->[% node.key %]s;
336         foreach my $object (@objects)
337     {
338                 my $row = {};
339             my $field_hash = {};
340           [% FOREACH field = nodes.$pkey.columns_essential %]
341                 $field_hash->{[% field %]} = $object->[% field %]();
342           [% END %]
343                 $row->{data} = $field_hash;
344             $row->{id} = $object->id();
345             push @output, $row;
346     }
347         return(\@output);
348 }
349
350 sub head {
351         return 1;
352 }
353
354 1;
355
356 [% END %]
357 EOF
358
359 } elsif($type eq 'xml'){
360   return <<EOF;
361 <?xml version="1.0" encoding="UTF-8"?>
362 <!DOCTYPE Durian SYSTEM "Durian.dtd">
363 <Durian>
364
365 <!-- The basic layout is fixed -->
366   <container bgcolor="#FFFFFF" cellpadding="0" cellspacing="0" height="90%" orientation="vertical" type="root" width="100%" xlink:label="RootContainer">
367         <container cellpadding="3" cellspacing="0" orientation="horizontal" type="container" height="100%" width="100%" xlink:label="MiddleContainer">
368           <container align="center" cellpadding="2" cellspacing="0" class="leftbar" orientation="vertical" type="minor" width="0%" xlink:label="MidLeftContainer"/>
369           <container cellpadding="0" cellspacing="0" orientation="vertical" width="100%" type="major" xlink:label="MainContainer"/>
370         </container>
371   </container>
372
373 <!-- Atom Classes -->
374 [% FOREACH node = linkable %]
375   <atom class="Durian::Atom::[% node.key FILTER ucfirst %]"  name="[% node.key FILTER ucfirst %]" xlink:label="[% node.key FILTER ucfirst %]Atom"/>
376 [%- END -%]
377
378 <!-- Atom Bindings -->
379 <atomatombindings>
380 [% FOREACH focus_atom = linkable %]
381   [% FOREACH link_atom = focus_atom.value %]
382   <atomatombinding xlink:from="#[% focus_atom.key FILTER ucfirst %]Atom" xlink:to="#[% link_atom.key FILTER ucfirst %]Atom" xlink:label="[% focus_atom.key FILTER ucfirst %]Atom2[% link_atom.key FILTER ucfirst %]Atom"/>
383   [%- END -%]
384 [%- END -%]
385 </atomatombindings>
386
387 <atomcontainerbindings>
388 [% FOREACH focus_atom = linkable %]
389   <atomcontainerbindingslayout xlink:label="Durian::Model::[% focus_atom.key FILTER ucfirst %]">
390   [% FOREACH link_atom = focus_atom.value %]
391     <atomcontainerbinding xlink:from="#MidLeftContainer" xlink:label="MidLeftContainer2[% link_atom.key FILTER ucfirst %]Atom"  xlink:to="#[% link_atom.key FILTER ucfirst %]Atom"/>
392   [%- END -%]
393   <atomcontainerbinding xlink:from="#MainContainer"    xlink:label="MainContainer2[% focus_atom.key FILTER ucfirst %]Atom"    xlink:to="#[% focus_atom.key FILTER ucfirst %]Atom"/>
394   </atomcontainerbindingslayout>
395   [%- END -%]
396 </atomcontainerbindings>
397
398 <uribindings>
399   <uribinding uri="/" class="Durian::Util::Frontpage"/>
400 </uribindings>
401
402 <classbindings>
403 [% FOREACH focus_atom = linkable %]
404    <classbinding class="Durian::Model::[% focus_atom.key FILTER ucfirst %]" plugin="#[% focus_atom.key FILTER ucfirst %]Atom" rank="0"/>
405 [%- END -%]
406
407 </classbindings>
408
409 </Durian>
410 EOF
411
412 } elsif($type eq 'template'){
413   return <<'EOF';
414 [% TAGS [- -] %]
415 [% MACRO renderpanel(panel,dbobject) BLOCK %]
416   <!-- begin panel: [% panel.label %] -->
417   <table border="0" width="[% panel.width %]" height="[% panel.height %]" bgcolor="[% panel.bgcolor %]" valign="top" cellpadding="[% panel.cellpadding %]" cellspacing="[% panel.cellspacing %]" align="[% panel.align %]" valign="[% panel.valign %]">
418     <tr>
419     [% FOREACH p = panel.containers %]
420       [% IF p.can_render(panel) %]
421         <td valign="top" class="[% p.class %]" align="[% panel.align %]" height="[% p.height || 1 %]" width="[% p.width %]">
422           [% IF p.type == 'Container' %]
423             [% renderpanel(p,dbobject) %]
424           [% ELSE %]
425             <table cellpadding="0" cellspacing="0" align="left" height="100%" width="100%">
426               [% IF p.name %]
427                 <tr bgcolor="#4444FF" height="1">
428                   <td><font color="#FFFFFF">[% p.name %][% IF panel.type == 'major' %]: [% dbobject.name %][% END %]</font></td>
429                   <td align="right" width="0"><!--<nobr><img src="/images/v.gif"/><img src="/images/^.gif"/>[% IF p.delible == 'yes' %]<img src="/images/x.gif"/>[% END %]</nobr>--></td>
430                 </tr>
431               [% END %]
432               <tr><td colspan="2" bgcolor="#FFFFFF">
433               <!-- begin atom: [% p.label %] -->
434               <table cellpadding="0" cellspacing="0" align="left" height="100%" width="100%"><!-- [% ref(atom) %] [% ref(dbobject) %] -->
435                 [% renderatom(p,dbobject) %] <!-- used to be renderplugin(p,panel) -->
436               </table>
437             </table>
438           [% END %]
439         </td>
440         [% IF panel.orientation == 'vertical' %]
441           </tr><tr>
442         [% END %]
443       [% END %]
444     [% END %]
445     </tr>
446   </table>
447   <!-- end panel: [% panel.label %] -->
448 [% END %]
449 [% MACRO renderatom(atom, dbobject) SWITCH atom.name %]
450   [- FOREACH node = linkable -]
451     [% CASE '[- node.key FILTER ucfirst -]' %]
452       [% render[- node.key FILTER ucfirst -]Atom(atom.render(dbobject)) %]
453   [- END -]
454     [% CASE DEFAULT %]
455       [% renderlist(atom.render(dbobject)) %]
456 [% END %]
457 [- FOREACH node = linkable -]
458 [% MACRO render[- node.key FILTER ucfirst -]Atom(lstArr) BLOCK %]
459   [% FOREACH record = lstArr %]
460     [% fields = record.data %]
461     [- pname = node.key FILTER ucfirst -]
462     [- pkey = "Durian::Model::${pname}" -]
463     [- FOREACH field = nodes.$pkey.columns_essential -]
464       <tr><td><b>[- field -]</b></td><td>[% fields.[- field -] %]</td></tr>
465     [- END -]
466     [% id = record.id %]
467     <tr><td><a href="?id=[% id %];class=Durian::Model::[- node.key FILTER ucfirst -]">Link</a></td><td></td></tr>
468   [% END %]
469 [% END %]
470 [- END -]
471 [% MACRO renderlist(lstArr) BLOCK %]
472   [%  FOREACH item = lstArr %]
473     <tr>[% item %]</tr>
474   [% END %]
475 [% END %]
476 EOF
477
478 1;
479
480 }
481 }