package App::Queries; use base 'Exporter'; our @EXPORT_OK = (qw( get_query_hash )); sub get_query_hash { return \%queries; } my %queries = ( get_reprocessing_list => { type => 'simple', source => q( SELECT typeID FROM typeAM; ), }, get_blueprintTypeID => { type => 'complex', source => q( SELECT [% var $extract %], bpID FROM invBpT; ), } ); #### package App; use App::Queries qw( get_query_hash ); use SQL::Template qw( load_query_hash get_query ); load_query_hash( get_query_hash() ); my $simple_query = get_query( 'get_reprocessing_list' ); my $input = { extract => 'productTypeID' }; my $complex_query = get_query( 'get_blueprintTypeID', $input ); print "$simple_query\n$complex_query"; #### SELECT typeID FROM typeAM; SELECT productTypeID, bpID FROM invBpT; #### package SQL::Template; use 5.010; use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = qw( load_query_hash get_query ); use Carp qw( cluck confess ); my $queries; my $eval_methods = { simple => \&eval_simple, complex => \&eval_complex, }; sub load_query_hash { my ($import) = @_; for my $name ( keys %{$import} ) { cluck "Existing query template redefined: $name" if ( $queries->{$name} ); $queries->{$name} = $import->{$name}; } return; } sub get_query { my ( $name, $input ) = @_; my $error; my $query = $queries->{$name}; $error = "Query template unknown for: $name" if ( !$query ); $error = "Eval method $query->{type} unknown for: $name" if ( !$error and !$eval_methods->{ $query->{type} } ); if ($error) { confess $error; return; } $eval_methods->{ $query->{type} }->( $query, $input ); return $query->{target}; } sub eval_simple { my ($query) = @_; return if ( $query->{target} ); $query->{target} = $query->{source}; return; } sub eval_complex { my ( $query, $input ) = @_; $query->{sub} = compile_template( $query->{source} ) if ( !$query->{sub} ); $query->{target} = $query->{sub}->( %{$input} ); return; } sub compile_template { my ($str) = @_; my $code; local $SIG{__WARN__} = sub { confess "Template Error: ", @_; }; $str =~ s/^\s+//; $str =~ s/\s+$//; while ( $str =~ m@(.*?)(\[% (/?)(var|const|if|loop)(?:|\s+(.*?[^\\])) %\]|$)@sg ) { my ( $content, $tag, $closing, $name, $args ) = ( $1, $2, $3, $4, $5 ); $content =~ s/(['\\])/\\$1/g; $code .= "\$res.='$content';" if ( length $content ); $args =~ s/\\>/>/g if ( defined $args ); if ($tag) { if ($closing) { if ( $name eq 'if' ) { $code .= '}'; } elsif ( $name eq 'loop' ) { $code .= '$$_=$__ov{$_} for(keys %__ov); } } }'; } } else { if ( $name eq 'var' ) { $code .= '$res.=eval{' . $args . '};'; } elsif ( $name eq 'const' ) { my $const = eval $args; $const =~ s/(['\\])/\\$1/g; $code .= '$res.=\'' . $const . '\';'; } elsif ( $name eq 'if' ) { $code .= 'if (eval{' . $args . '} ) {'; } elsif ( $name eq 'loop' ) { $code .= '{' . 'my $__a=eval{ ' . $args . '}; ' . 'if ($__a) { ' . ' for (@$__a) { ' . ' my %__v=%{$_}; ' . ' my %__ov; ' . ' for (keys %__v) { ' . ' $__ov{$_}=$$_; ' . ' $$_=$__v{$_}; ' . ' }'; } } } } $code = 'no strict; ' . 'sub { ' . ' my %__v=@_; ' . ' my %__ov; ' . ' for (keys %__v) { ' . ' $__ov{$_}=$$_; ' . ' $$_=$__v{$_}; ' . ' }' . ' my $res = "";' . $code . ' $$_=$__ov{$_} for(keys %__ov);' . ' return $res; ' . '} '; my $sub = eval $code; confess "Template format error: $@" if ( !$sub ); return $sub; } 1;