Commit | Line | Data |
88f2d2fd |
1 | package SQL::Translator::Producer::Turnkey; |
c00bb9f8 |
2 | |
3 | use strict; |
4 | use vars qw[ $VERSION $DEBUG ]; |
d8dc07e7 |
5 | $VERSION = sprintf "%d.%02d", q$Revision: 1.7 $ =~ /(\d+)\.(\d+)/; |
c00bb9f8 |
6 | $DEBUG = 1 unless defined $DEBUG; |
7 | |
8 | use SQL::Translator::Schema::Constants; |
d8dc07e7 |
9 | use SQL::Translator::Schema::Graph; |
10 | use SQL::Translator::Schema::Graph::HyperEdge; |
c00bb9f8 |
11 | use SQL::Translator::Utils qw(header_comment); |
12 | use Data::Dumper; |
13 | use Template; |
14 | |
d8dc07e7 |
15 | #{ |
16 | # local $/; |
17 | # my $data = <SQL::Translator::Producer::Turnkey::DATA>; |
18 | # eval { $data }; |
19 | # warn $@ if $@; |
20 | #} |
21 | |
22 | my %producer2dsn = ( |
c00bb9f8 |
23 | MySQL => 'mysql', |
24 | PostgreSQL => 'Pg', |
25 | Oracle => 'Oracle', |
26 | ); |
27 | |
28 | # ------------------------------------------------------------------- |
29 | sub produce { |
30 | my $t = shift; |
31 | my $create = undef; |
c00bb9f8 |
32 | my $args = $t->producer_args; |
d8dc07e7 |
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); |
c00bb9f8 |
36 | |
88f2d2fd |
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'} || '', |
d8dc07e7 |
44 | baseclass => $baseclass, |
88f2d2fd |
45 | db_user => $args->{'db_user'} || '', |
46 | db_pass => $args->{'db_pass'} || '', |
47 | parser => $t->parser_type, |
48 | producer => __PACKAGE__, |
d8dc07e7 |
49 | dsn => $args->{'dsn'} || sprintf( 'dbi:%s:_', $producer2dsn{ $parser_type } |
50 | ? $producer2dsn{ $parser_type } |
88f2d2fd |
51 | : $parser_type |
52 | ) |
53 | ); |
54 | |
69c7a62f |
55 | # |
c8515c9f |
56 | # create methods |
69c7a62f |
57 | # |
d8dc07e7 |
58 | foreach my $node_from ($graph->node_values){ |
88f2d2fd |
59 | next unless $node_from->table->is_data; |
60 | foreach my $cedge ( $node_from->compoundedges ){ |
69c7a62f |
61 | |
d8dc07e7 |
62 | my $hyperedge = SQL::Translator::Schema::Graph::HyperEdge->new(); |
c8515c9f |
63 | |
d8dc07e7 |
64 | my $node_to; |
88f2d2fd |
65 | foreach my $edge ($cedge->edges){ |
66 | if($edge->thisnode->name eq $node_from->name){ |
67 | $hyperedge->vianode($edge->thatnode); |
c8515c9f |
68 | |
88f2d2fd |
69 | if($edge->thatnode->name ne $cedge->via->name){ |
d8dc07e7 |
70 | $node_to ||= $graph->node($edge->thatnode->table->name); |
88f2d2fd |
71 | } |
c8515c9f |
72 | |
d8dc07e7 |
73 | $hyperedge->push_thisnode($edge->thisnode); |
74 | $hyperedge->push_thisfield($edge->thisfield); |
75 | $hyperedge->push_thisviafield($edge->thatfield); |
c8515c9f |
76 | |
88f2d2fd |
77 | } else { |
88f2d2fd |
78 | if($edge->thisnode->name ne $cedge->via->name){ |
d8dc07e7 |
79 | $node_to ||= $graph->node($edge->thisnode->table->name); |
88f2d2fd |
80 | } |
c8515c9f |
81 | |
d8dc07e7 |
82 | $hyperedge->push_thatnode($edge->thisnode); |
83 | $hyperedge->push_thatfield($edge->thisfield); |
84 | $hyperedge->push_thatviafield($edge->thatfield); |
88f2d2fd |
85 | } |
86 | } |
88f2d2fd |
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 | |
d8dc07e7 |
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 | |
88f2d2fd |
113 | #warn $node_from->name ."\t". $node_to->name ."\t". $hyperedge->type ."\t". $hyperedge->vianode->name; |
114 | |
115 | $node_from->push_hyperedges($hyperedge); |
116 | } |
c8515c9f |
117 | } |
c00bb9f8 |
118 | |
d8dc07e7 |
119 | $meta{"nodes"} = $graph->node; |
88f2d2fd |
120 | return(translateForm($t, \%meta)); |
c00bb9f8 |
121 | } |
122 | |
d8dc07e7 |
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 | |
c00bb9f8 |
170 | ########################################### |
171 | # Here documents for the tt2 templates # |
172 | ########################################### |
173 | |
d8dc07e7 |
174 | if($type eq 'classdbi'){ |
175 | return <<EOF; |
88f2d2fd |
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) %] |
d8dc07e7 |
188 | [% printHasFriendly(node) %] |
88f2d2fd |
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' %] |
d8dc07e7 |
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 %] |
88f2d2fd |
254 | 2 |
d8dc07e7 |
255 | [% END %] |
88f2d2fd |
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 | |
d8dc07e7 |
264 | [% MACRO printHasFriendly(node) BLOCK %] |
265 | # |
266 | # Has Friendly |
267 | # |
268 | hello, sailor! |
269 | [% END %] |
270 | |
88f2d2fd |
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 | |
d8dc07e7 |
280 | [% baseclass %]->set_db('Main', '[% db_str %]', '[% db_user %]', '[% db_pass %]'); |
88f2d2fd |
281 | |
282 | [% FOREACH node = nodes %] |
283 | [% printPackage(node.value) %] |
284 | [% END %] |
285 | EOF |
286 | |
d8dc07e7 |
287 | } elsif($type eq 'atom'){ |
288 | return <<'EOF'; |
c00bb9f8 |
289 | [% ###### DOCUMENT START ###### %] |
290 | |
88f2d2fd |
291 | [% FOREACH node = linkable %] |
c00bb9f8 |
292 | |
293 | ############################################## |
294 | |
88f2d2fd |
295 | package Durian::Atom::[% node.key FILTER ucfirst %]; |
c00bb9f8 |
296 | |
88f2d2fd |
297 | [% pname = node.key FILTER ucfirst%] |
c00bb9f8 |
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 |
88f2d2fd |
312 | if(ref($dbobject) eq 'Durian::Model::[% node.key FILTER ucfirst %]') { |
c00bb9f8 |
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 = {}; |
88f2d2fd |
323 | [% FOREACH field = nodes.$pkey.columns_essential %] |
c00bb9f8 |
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 = (); |
88f2d2fd |
335 | my @objects = $dbobject->[% node.key %]s; |
c00bb9f8 |
336 | foreach my $object (@objects) |
337 | { |
338 | my $row = {}; |
339 | my $field_hash = {}; |
88f2d2fd |
340 | [% FOREACH field = nodes.$pkey.columns_essential %] |
c00bb9f8 |
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 | |
d8dc07e7 |
359 | } elsif($type eq 'xml'){ |
360 | return <<EOF; |
c00bb9f8 |
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 --> |
88f2d2fd |
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"/> |
69c7a62f |
376 | [%- END -%] |
c00bb9f8 |
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"/> |
69c7a62f |
383 | [%- END -%] |
384 | [%- END -%] |
c00bb9f8 |
385 | </atomatombindings> |
386 | |
387 | <atomcontainerbindings> |
69c7a62f |
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 -%] |
c00bb9f8 |
406 | |
69c7a62f |
407 | </classbindings> |
c00bb9f8 |
408 | |
409 | </Durian> |
410 | EOF |
411 | |
d8dc07e7 |
412 | } elsif($type eq 'template'){ |
413 | return <<'EOF'; |
c00bb9f8 |
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 %] |
88f2d2fd |
450 | [- FOREACH node = linkable -] |
451 | [% CASE '[- node.key FILTER ucfirst -]' %] |
452 | [% render[- node.key FILTER ucfirst -]Atom(atom.render(dbobject)) %] |
c00bb9f8 |
453 | [- END -] |
454 | [% CASE DEFAULT %] |
455 | [% renderlist(atom.render(dbobject)) %] |
456 | [% END %] |
88f2d2fd |
457 | [- FOREACH node = linkable -] |
458 | [% MACRO render[- node.key FILTER ucfirst -]Atom(lstArr) BLOCK %] |
c00bb9f8 |
459 | [% FOREACH record = lstArr %] |
460 | [% fields = record.data %] |
88f2d2fd |
461 | [- pname = node.key FILTER ucfirst -] |
c00bb9f8 |
462 | [- pkey = "Durian::Model::${pname}" -] |
88f2d2fd |
463 | [- FOREACH field = nodes.$pkey.columns_essential -] |
c00bb9f8 |
464 | <tr><td><b>[- field -]</b></td><td>[% fields.[- field -] %]</td></tr> |
465 | [- END -] |
466 | [% id = record.id %] |
88f2d2fd |
467 | <tr><td><a href="?id=[% id %];class=Durian::Model::[- node.key FILTER ucfirst -]">Link</a></td><td></td></tr> |
c00bb9f8 |
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 | |
c00bb9f8 |
478 | 1; |
479 | |
d8dc07e7 |
480 | } |
481 | } |