File Coverage

blib/lib/SQL/Generator.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Author: Murat Uenalan (muenalan@cpan.org)
2             #
3             # Copyright (c) 2001 Murat Uenalan. All rights reserved.
4             #
5             # Note: This program is free software; you can redistribute
6             #
7             # it and/or modify it under the same terms as Perl itself.
8            
9             our $VERSION = '0.02';
10            
11             require 5.005_62;
12            
13 5     5   4856 use warnings;
  5         9  
  5         161  
14            
15 5     5   23 use Carp;
  5         7  
  5         219  
16            
17 5     5   4852 use Class::Maker;
  5         68109  
  5         57  
18            
19             package Object::ObjectList;
20            
21 5     5   19894 use Class::Maker::Examples::Array;
  0            
  0            
22            
23             #use Types::Array;
24            
25             Class::Maker::class
26             {
27             isa => [qw(Array)],
28            
29             attribute =>
30             {
31             getset => [ qw/name blessed/ ],
32             },
33             };
34            
35             sub push_new
36             {
37             my $this = shift;
38            
39             my %arghash = @_;
40            
41             my $obj;
42            
43             my $class = $this->blessed();
44            
45             unless( $class->can( 'new' ) )
46             {
47             ::croak "Couldn't call method new via module '$class' :$@";
48             }
49            
50             $obj = $class->new( %arghash );
51            
52             $this->push( $obj );
53            
54             return $obj;
55             }
56            
57             # search our object list and return the obj with matching attributes
58            
59             sub get_where
60             {
61             my $this = shift;
62            
63             my %arghash = @_;
64            
65             my $key = shift @{ [ keys %arghash ] };
66            
67             my @results;
68            
69             foreach my $obj ( $this->get )
70             {
71             if( $obj->$key() eq $arghash{$key} )
72             {
73             push @results, $obj;
74             }
75             }
76            
77             return undef unless @results;
78            
79             return wantarray ? @results : $results[0];
80             }
81            
82             sub get_where_islike
83             {
84             my $this = shift;
85            
86             my %arghash = @_;
87            
88             my $key = shift @{ [ keys %arghash ] };
89            
90             my @results;
91            
92             foreach my $obj ( @{ $this->get } )
93             {
94             if( $obj->$key() =~ /$arghash{$key}/ )
95             {
96             push @results, $obj;
97             }
98             }
99            
100             return \@results;
101             }
102            
103             package SQL::Generator::Argument;
104            
105             Class::Maker::class
106             {
107             attribute =>
108             {
109             getset =>
110             [
111             qw(replace pre post token parameter),
112            
113             qw(token_printf joinstr param_printf),
114            
115             qw(joinseperator),
116            
117             qw(hash_keyprintf hash_valueprintf hash_assigner),
118            
119             qw(array_valueprintf),
120             ],
121            
122             hash => [qw(argtypes)],
123             },
124             };
125            
126             require 5.005_62; use warnings; use strict;
127            
128             # Preloaded methods go here.
129            
130             sub _preinit
131             {
132             my $this = shift;
133            
134             $this->pre('');
135             $this->post('');
136            
137             $this->token_printf('%s'); # format tokenstring
138             $this->param_printf('%s'); # format paramstring
139             $this->joinstr(' ');
140            
141             $this->joinseperator(', '); # what is the seperator between elements
142            
143             $this->hash_keyprintf('%s'); # do we should quote the keys ?
144             $this->hash_valueprintf("'%s'"); # do we should quote the values ?
145             $this->hash_assigner(' => '); # string between key => value pairs
146            
147             $this->array_valueprintf('%s'); # do we should quote the values ?
148            
149             $this->replace( 0 );
150             }
151            
152             sub _postinit
153             {
154             my $this = shift;
155            
156             if( $this->pre )
157             {
158             $this->token_printf( $this->pre.$this->token_printf ); # format tokenstring
159             }
160            
161             if( $this->post )
162             {
163             $this->param_printf( $this->param_printf.$this->post ); # format tokenstring
164             }
165            
166             if( $this->replace )
167             {
168             $this->token('');
169            
170             $this->param_printf('%s');
171             }
172            
173             unless( keys %{ $this->argtypes } )
174             {
175             $this->argtypes->{SCALAR} = 1;
176             }
177             }
178            
179             sub scalar_totext
180             {
181             my $this = shift;
182            
183             return sprintf( $this->param_printf, ref($this->parameter) ? ${$this->parameter} : $this->parameter );
184             }
185            
186             sub array_totext
187             {
188             my $this = shift;
189            
190             my @fields=();
191            
192             foreach my $field ( @{ $this->parameter } )
193             {
194             push @fields, sprintf( $this->array_valueprintf, $field );
195             }
196            
197             return sprintf( $this->param_printf, join( $this->joinseperator, @fields ) );
198             }
199            
200             sub hash_totext
201             {
202             my $this = shift;
203            
204             my @fields=();
205            
206             while( my ($key, $value) = each %{$this->parameter} )
207             {
208             my $field='';
209            
210             $field .= sprintf( $this->hash_keyprintf, $key );
211            
212             $field .= $this->hash_assigner;
213            
214             $field .= sprintf( $this->hash_valueprintf, $value );
215            
216             push @fields, $field;
217             }
218            
219             return sprintf( $this->param_printf, join( $this->joinseperator, @fields ) );
220             }
221            
222             sub param : method
223             {
224             my $this = shift;
225            
226             my $param = shift;
227            
228             my $type = ref( $param ) || 'SCALAR';
229            
230             unless( $this->testType( $type ) )
231             {
232             ::carp 'Incorrect type ', $type, ', use ', join( ' or ',$this->wantType ), ' instead';
233             }
234            
235             return $this->parameter( $param );
236             }
237            
238             sub reset : method
239             {
240             my $this = shift;
241            
242             $this->parameter( undef );
243             return;
244             }
245            
246             sub wantType : method
247             {
248             my $this = shift;
249            
250             my @allowed = ();
251            
252             foreach ( keys %{ $this->argtypes } )
253             {
254             if( $this->argtypes->{$_} )
255             {
256             push @allowed, $_;
257             }
258             }
259            
260             return @allowed;
261             }
262            
263             # tests wether the given type is allowed and with undef argument returns list of allowed
264             # types
265            
266             sub testType($) : method
267             {
268             my $this = shift;
269            
270             my $type = shift;
271            
272             if( $this->argtypes->{'ALL'} )
273             {
274             return 'ALL';
275             }
276            
277             if( $this->argtypes->{$type} )
278             {
279             return $type;
280             }
281            
282             return undef;
283             }
284            
285             #
286             # translates the element to a sql language element
287             #
288            
289             sub totext : method
290             {
291             my $this = shift;
292            
293             my $pre = sprintf( $this->token_printf, $this->token ); # format tokenstring
294            
295             if( $pre )
296             {
297             $pre .= $this->joinstr;
298             }
299            
300             my $type = ref( $this->parameter ) || 'SCALAR';
301            
302             if( $type eq 'ARRAY' )
303             {
304             return $pre.$this->array_totext( $this->parameter );
305             }
306             elsif( $type eq 'HASH' )
307             {
308             return $pre.$this->hash_totext( $this->parameter );
309             }
310             else
311             {
312             return $pre.$this->scalar_totext( $this->parameter );
313             }
314            
315             return undef;
316             }
317            
318             package SQL::Generator::Command;
319            
320             Class::Maker::class
321             {
322             can => [qw( validate )],
323            
324             attribute =>
325             {
326             getset => [qw(id subobject prettyprint)],
327            
328             array => [qw(template required)],
329            
330             hash => [qw(arguments subobjects defaults)],
331             },
332             };
333            
334             require 5.005_62;
335            
336             use strict;
337            
338             use warnings;
339            
340             # Preloaded methods go here.
341            
342             sub _preinit : method
343             {
344             my $this = shift;
345            
346             $this->prettyprint( '' );
347            
348             return;
349             }
350            
351             sub _postinit : method
352             {
353             my $this = shift;
354            
355             # generate 'Argument' instances for every arg of the template
356            
357             foreach my $arg ( @{ $this->template } )
358             {
359             if( exists $this->arguments->{$arg} )
360             {
361             $this->arguments->{$arg} = new SQL::Generator::Argument( token => $arg, %{ $this->arguments->{$arg} } );
362             }
363             else
364             {
365             $this->arguments->{$arg} = new SQL::Generator::Argument( token => $arg, argtypes => { SCALAR => 1 } );
366             }
367             }
368            
369             return;
370             }
371            
372             sub validate : method
373             {
374             my $this = shift;
375            
376             my $href_args = shift;
377            
378             # validate if all required fields are existing
379            
380             my @missing = ();
381            
382             foreach my $element ( @{ $this->required } )
383             {
384             if( not exists $href_args->{$element} )
385             {
386             if( not exists $this->defaults->{$element} )
387             {
388             push @missing, $element;
389             }
390             }
391             }
392            
393             die sprintf( 'Required argument(s) (%s) %s missing (defaults: %s)', join( ', ',@missing ), @missing > 1 ? 'are' : 'is', join( ', ', keys %{ $this->defaults } ) ) if @missing;
394            
395             return 1;
396             }
397            
398             sub totext
399             {
400             my $this = shift;
401            
402             my $href_args = shift;
403            
404             $this->validate( $href_args );
405            
406             my @construct;
407            
408             foreach my $element ( @{ $this->template } )
409             {
410             #$this->debugPrint( sprintf "%s( '%s' %s )\n",$this->id, $element, (exists $href_args->{$element}) ? 'EXISTS' : 'NOT FOUND' );
411            
412             # defaults should be use, if value is missing ## ERROR/WARN: ONLY WHEN IT IS A REQUIRED ELEMENT ...see validate !!
413            
414             unless( exists $href_args->{$element} )
415             {
416             if( exists $this->defaults->{$element} )
417             {
418             $href_args->{$element} = $this->defaults->{$element};
419             }
420             }
421            
422             if( exists $href_args->{$element} )
423             {
424             if( my $subcmd = $this->subobjects->{$element} )
425             {
426             $this->arguments->{$element}->param( $subcmd->totext( $href_args ) );
427             }
428             else
429             {
430             $this->arguments->{$element}->param( $href_args->{$element} );
431             }
432            
433             push @construct, sprintf '%s%s', $this->arguments->{$element}->totext(), $this->prettyprint;
434            
435             $this->arguments->{$element}->reset();
436             }
437             }
438            
439             return join( ' ', @construct);
440             }
441            
442             package SQL::Generator;
443            
444             Class::Maker::class
445             {
446             attribute =>
447             {
448             getset =>
449             [
450             qw/command pre post autoprint prettyprint historysize/,
451            
452             qw/table database where/,
453            
454             qw/lang langrules file handle/,
455             ],
456            
457             array => [ qw/history/ ],
458            
459             hash => [ qw/defaults/ ],
460             },
461             };
462            
463             use strict;
464            
465             use IO::File;
466            
467             use vars qw($AUTOLOAD $VERSION);
468            
469             use Exporter;
470            
471             our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
472            
473             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
474            
475             our @EXPORT = qw( );
476            
477             # Preloaded methods go here.
478            
479             sub _preinit
480             {
481             my $this = shift;
482            
483             $this->pre( '' );
484            
485             $this->post( '' );
486            
487             $this->autoprint(0);
488            
489             $this->prettyprint(0);
490            
491             $this->historysize(100);
492            
493             $this->lang( 'MYSQL' );
494            
495             $this->file( undef );
496            
497             #$this->debugOn;
498             }
499            
500             sub close
501             {
502             my $this = shift;
503            
504             if( defined $this->handle() )
505             {
506             $this->handle->close();
507             }
508             }
509            
510             sub _postinit
511             {
512             my $this = shift;
513            
514             $this->change_dialect( $this->lang );
515            
516             if( $this->file )
517             {
518             $this->debugPrint( sprintf "opening file %s as STDOUT", $this->file );
519            
520             $this->handle( new IO::File( $this->file ) ) ;
521            
522             unless( defined $this->handle() )
523             {
524             die sprintf "FAILURE: opening file %s as STDOUT", $this->file;
525             }
526             }
527             }
528            
529             sub dialect_path
530             {
531             my $this = shift;
532            
533             my $lang = shift || $this->lang;
534            
535             return 'SQL::Generator::Lang::'.$lang;
536             }
537            
538             sub change_dialect
539             {
540             my $this = shift;
541            
542             my $lang = shift;
543            
544             #die "WE DIE HERE";
545            
546             my $dialect = $this->dialect_path($lang);
547            
548             eval "require $dialect";
549            
550             if( $@ )
551             {
552             ::croak "Couldn't change/load language module '$dialect' :$@";
553             }
554            
555             # update rules via new CommandList instance: SQL::Generator::Lang::*->new()
556            
557             $this->langrules( $dialect->new() );
558            
559             die "Failed loading language module" unless $this->langrules;
560            
561             return $this->lang( $lang );
562             }
563            
564             sub DEFAULT : method
565             {
566             my $this = shift;
567            
568             my %args = @_;
569            
570             $this->defaults( \%args );
571            
572             # foreach my $key ( keys %{ $this->defaults } )
573             # {
574             # print "SETTING DEFAULT: $key => ", $args{$key}, "\n\n";
575             # }
576            
577             return;
578             }
579            
580             sub AUTOLOAD
581             {
582             my $func = $AUTOLOAD;
583            
584             $func =~ s/.*:://;
585            
586             return if $func eq 'DESTROY';
587            
588             my $this = shift;
589            
590             my %args = @_;
591            
592             my $result;
593            
594             # only single command per func in this version !!! we take the first one...
595            
596             my ( $cmd ) = $this->langrules->get_where( id => $func );
597            
598             if( defined $cmd )
599             {
600             $cmd->defaults( \%{ $this->defaults } );
601            
602             $result = sprintf '%s %s', $func, $cmd->totext( \%args );
603            
604             $result = $this->pre.$result.$this->post;
605            
606             if( $result eq 1 )
607             {
608             die "DONT KNOW WHAT THE HELL HAPPENED";
609             }
610             }
611             else
612             {
613             warn( "Can't find command for $func in ", $this->dialect_path );
614             }
615            
616             $this->handle->print($result) if $this->file;
617            
618             print $result if $this->autoprint;
619            
620             if( $this->history )
621             {
622             if( @{ $this->history } < $this->historysize )
623             {
624             push @{ $this->history }, $result;
625             }
626             }
627            
628             return $result;
629             }
630            
631             sub dump_history
632             {
633             my $this = shift;
634            
635             my $pre = shift;
636            
637             my $post = shift;
638            
639             foreach ( @{ $this->history } )
640             {
641             print $pre || '', $_, $post || '';
642             }
643            
644             @{ $this->history } = [];
645             }
646            
647             1;
648            
649             __END__