So I was browsing the Advanced Perl book, which has this nifty chapter on templating. I read over the text, and it looks like this guy had to (1) build a little template script language to populate his template data, and (2) hand-roll a parser for a toy version of an object model representation.
The first thing I thought was "that sounds like an awful lot of work to me".
I checked the book's copyright. 1997. Well, no wonder. This article was from the stone ages.
Anyhow, I browsed CPAN and came across plover's Text::Template module. It is so usefully clever to use Perl as the template script -- after a little reflection, it struck me as absurd to make up a new script language for template population... well, maybe not absurd, but there'd have to be a really, really good reason for someone to do that, anyway. OK, back end processing done.
Then I grabbed Ingy's YAML module, and had the front end, leaving me with the straightforward task of creating templates and populating the variables used by them.
Voila. In the time it took to research two modules, I cut my development time to nearly Zero, while that fellow in the AP book blathers on about hand-rolling 'toy parsers' and adding references to byacc. Granted, the book is copyright 1997, but still, I wonder if there's something I've missed. Am I on the right track?
plover, Thank you so much for Text::Template!
Here's the example code I drew up to use YAML and Text::Template -- it's only a proof of concept: a script that generates Perl classes. I know, there are better ways to build Perl classes.
spec.yaml
This is the YAML file that contains 'object descriptions'. It's more complete than necessary for this example, because I'm thinking of adding a SQL template for it, and expanding the Perl template I've already written to include database interaction. This is all pretty new to me: I'm using this as a learning exercise.
---
Objects:
- Employee:
fields:
emp_id: int
name: string[40]
dept_id: Department
salary: double
key: emp_id
- Department:
fields:
dept_id: int
name: string[20]
key: dept_id
Relationships:
-
- Owner:
name: Department
count: 1
- Contains:
name: Employee
count: many
template.tmpl:
Here's the template that generates inside-out Perl classes. As you can see, it's extremely primitive, and I know there are Perl modules that make class-writing trivial, but again, this is a template-learning exercise for me.
{ @attrlist = keys %attrs; '' }
package {$package};
use strict;
\{
{
' my %' . join( ";\n my \%", @attrlist ) . ';';
}
sub new \{ bless \{\}, shift \}
{foreach my $attr ( @attrlist )
{
my $v = '{ $' . $attr . '{+shift} }';
$OUT .= sprintf( " sub %-10s :lvalue " . $v . "\n", $attr );
}
}
sub DESTROY
\{
my $sref = 0+shift;
delete { join( "\n ,", map { '$' . $_ . '{$sref}' }
+@attrlist); };
\}
\}
1;
filler.pl:
And here's the script that ties it all together -- essentially, its job is to make sure the right data is loaded into the right Perl variables at the right time.
use YAML;
use Text::Template;
use strict;
my $data = YAML::LoadFile( 'spec.yaml' );
my $text = new Text::Template ( TYPE => 'FILE',
SOURCE => 'template.tmpl' );
my @objects = @{$data->{Objects}};
my @relationships = @{$data->{Relationships}};
foreach my $obj (@objects)
{
our ($package, $value) = %$obj;
our %attrs = %{$value->{fields}};
my $out = $text->fill_in();
print $out;
}
And here's the output from my examples:
package Employee;
use strict;
{
my %dept_id;
my %emp_id;
my %name;
my %salary;
sub new { bless {}, shift }
sub dept_id :lvalue { $dept_id{+shift} }
sub emp_id :lvalue { $emp_id{+shift} }
sub name :lvalue { $name{+shift} }
sub salary :lvalue { $salary{+shift} }
sub DESTROY
{
my $sref = 0+shift;
delete $dept_id{$sref}
,$emp_id{$sref}
,$name{$sref}
,$salary{$sref};
}
}
1;
package Department;
use strict;
{
my %dept_id;
my %name;
sub new { bless {}, shift }
sub dept_id :lvalue { $dept_id{+shift} }
sub name :lvalue { $name{+shift} }
sub DESTROY
{
my $sref = 0+shift;
delete $dept_id{$sref}
,$name{$sref};
}
}
1;