double variable interpolation question

C

ccc31807

I've been asked to maintain a web application/site which runs on
Linux, Postgres, Apache, and Perl.

The script contains these lines which query a database:
my $page = param('page');
my $person = param('person');
my $doc_ref = get_doc_ref($page);
my $per_ref = get_per_ref($person);

When I print the values, this is what I get:
print $doc_ref->[0] returns
<body>
<h1>Hello, #a_name#</h1>
<p>You have won #a_prize#.</p>
</body>
print $per_ref->{a_name) returns 'John Smith'
print $per_ref->{a_prize} returns 'dinner at Outback Steakhouse'

The next line assigns the $doc_ref to a scalar:
my $doc = $doc_ref->[0];

My question relates to the IN BETWEEN code here, which I'll come back
to.

After the IN BETWEEN code executes, we have this:
print $doc;

This results in the the following:
<body>
<h1>Hello, John Smith</h1>
<p>You have won dinner at Outback Steakhouse.</p>
</body>

Here is the IN BETWEEN code:
$doc =~ s/#a_name#/$per_ref->{a_name}/g;
$doc =~ s/#a_prize#/$per_ref->{a_prize}/g;
.... and so on for a large number of variables. There are many pages,
and some of the pages contain thousands of different values.

I have tried several different ways to fold the $per_ref data into the
$doc without the IN BETWEEN code, all without success. My question is
whether this is even possible? Do I have to accept the IN BETWEEN
substitutions, or can I directly update one scalar variable ($doc)
with a number of other scalars?

The good news is that the old code works perfectly. The bad news is
that I'm staring at a lot of mindless coding. If I could change the
database and have everything automatically update, I'd be very happy.

Thanks, CC.
 
R

RedGrittyBrick

ccc31807 said:
I've been asked to maintain a web application/site which runs on
Linux, Postgres, Apache, and Perl.

The script contains these lines which query a database:
my $page = param('page');
my $person = param('person');
my $doc_ref = get_doc_ref($page);
my $per_ref = get_per_ref($person);

When I print the values, this is what I get:
print $doc_ref->[0] returns
<body>
<h1>Hello, #a_name#</h1>
<p>You have won #a_prize#.</p>
</body>
print $per_ref->{a_name) returns 'John Smith'
print $per_ref->{a_prize} returns 'dinner at Outback Steakhouse'

The next line assigns the $doc_ref to a scalar:
my $doc = $doc_ref->[0];
....
$doc =~ s/#a_name#/$per_ref->{a_name}/g;
$doc =~ s/#a_prize#/$per_ref->{a_prize}/g;
... and so on for a large number of variables. There are many pages,
and some of the pages contain thousands of different values.

Hmm, can't you loop over the keys of the hash pointed at by $per_ref and
for each key substitute /#$key#/$value/;

perl -e '%x=qw(name fred age 23); \
$y="foo #name# bar #age# end"; \
for(keys %x) {$y=~s/#$_#/$x{$_}/g;} \
print "$y\n";
foo fred bar 23 end

Someone will be along soon with a FAQ ref and a much better solution.
 
J

Jim Gibson

ccc31807 said:
I've been asked to maintain a web application/site which runs on
Linux, Postgres, Apache, and Perl.

The script contains these lines which query a database:
my $page = param('page');
my $person = param('person');
my $doc_ref = get_doc_ref($page);
my $per_ref = get_per_ref($person);

When I print the values, this is what I get:
print $doc_ref->[0] returns
<body>
<h1>Hello, #a_name#</h1>
<p>You have won #a_prize#.</p>
</body>
print $per_ref->{a_name) returns 'John Smith'
print $per_ref->{a_prize} returns 'dinner at Outback Steakhouse'

The next line assigns the $doc_ref to a scalar:
my $doc = $doc_ref->[0];

My question relates to the IN BETWEEN code here, which I'll come back
to.

After the IN BETWEEN code executes, we have this:
print $doc;

This results in the the following:
<body>
<h1>Hello, John Smith</h1>
<p>You have won dinner at Outback Steakhouse.</p>
</body>

Here is the IN BETWEEN code:
$doc =~ s/#a_name#/$per_ref->{a_name}/g;
$doc =~ s/#a_prize#/$per_ref->{a_prize}/g;
... and so on for a large number of variables. There are many pages,
and some of the pages contain thousands of different values.

To me, that looks like a home-grown "templating" system, where the
programmer has identified a field such as '#a_name#' and is using the
substitution operator to fill in the variable with their actual values
at runtime based on the form input and subsequent database query.
I have tried several different ways to fold the $per_ref data into the
$doc without the IN BETWEEN code, all without success. My question is
whether this is even possible? Do I have to accept the IN BETWEEN
substitutions, or can I directly update one scalar variable ($doc)
with a number of other scalars?

The substitute operator would seem to be the best choice for updating a
scalar by substituting values for embedded variables.
The good news is that the old code works perfectly. The bad news is
that I'm staring at a lot of mindless coding. If I could change the
database and have everything automatically update, I'd be very happy.

It doesn't look like mindless coding to me. That said, if I were going
to implement this from scratch, I would consider using one of the
existing templating systems available from CPAN. There are several.
However, if your program is working, and working well, why change it?
Maybe you just need to spend more time learning about how it works.

The advantage of the templating systems is that they usually have more
powerful semantics than simple substitution, and can be extended more
easily. If you are thinking about adding more functionality to your
program, then you might want to consider re-implementing using a
templating system.
 
U

Uri Guttman

c> Here is the IN BETWEEN code:
c> $doc =~ s/#a_name#/$per_ref->{a_name}/g;
c> $doc =~ s/#a_prize#/$per_ref->{a_prize}/g;
c> ... and so on for a large number of variables. There are many pages,
c> and some of the pages contain thousands of different values.

that is a basic templating thing. and if the #foo# name is ALWAYS the
same (or easily derived) as the hash key name, you can reduce all of
those to one simple s/// operation:

s/#(\w+?)#/$per_ref->{$1}/g ;

if you need to do more work on $1 to convert the name, then add the /e
modifier so you can put code in the replacement part.

uri
 
C

ccc31807

        s/#(\w+?)#/$per_ref->{$1}/g ;

This works perfectly. I wasn't looking for this kind of solution, but
it works perfectly, and I have now modified the first script and
commented out 27 lines that are now surplus.

A question and a comment.
Question: Is there any reason you didn't use something like this?
s/#[^#]*#/$per_ref->{$1}/g ;
From what little I understand about the way RE's work, the non-greedy
quantifier has to first match to the end, while the any-character-but
stops matching when it reaches the character it is looking for.

Comment: Yes, I suppose this is a home grown templating system.
However, instead of days of work I'm now looking at hours, and I don't
really have an incentive to rewrite the entire application. There is a
book on Catalyst that I've been thinking about buying. Would it be
worth my time to buy it and read it? I work on web enabled apps fairly
often.

Thanks, CC.
 
J

Jim Gibson

ccc31807 said:
        s/#(\w+?)#/$per_ref->{$1}/g ;

This works perfectly. I wasn't looking for this kind of solution, but
it works perfectly, and I have now modified the first script and
commented out 27 lines that are now surplus.

A question and a comment.
Question: Is there any reason you didn't use something like this?
s/#[^#]*#/$per_ref->{$1}/g ;
From what little I understand about the way RE's work, the non-greedy
quantifier has to first match to the end, while the any-character-but
stops matching when it reaches the character it is looking for.

Non-greedy matches do not have to first match to the end. They should
stop when the first match is encountered. Just how they do that is
known only to those familiar with Perl's Regular Expression Engine,
i.e., not me.

Your templating system uses '#' to denote the variables. Either of
those REs will work, provided you do not have any other '#' characters
in your source text. The difference is when there are other '#'
characters in the text. Your RE will match '# this is a comment #'
while Uri's will not. They will both match '#comment#'.
 
U

Uri Guttman

c> This works perfectly. I wasn't looking for this kind of solution, but
c> it works perfectly, and I have now modified the first script and
c> commented out 27 lines that are now surplus.

of course it works perfectly. it is the classic single level (scalar
values only) templater in one line. i used that many times for many
years on various projects. i have seen your existing style too often. a
client's code base had about 100 s/// ops doing the same thing ON EACH
LINE OF A LOOP!! talk about inefficient. but soon you will need more
power and you should check out Template::Simple as it does the scalar
and more with very easy to use markup and it is faster than all the
others too.

c> A question and a comment.
c> Question: Is there any reason you didn't use something like this?
c> s/#[^#]*#/$per_ref->{$1}/g ;
c> From what little I understand about the way RE's work, the non-greedy
c> quantifier has to first match to the end, while the any-character-but
c> stops matching when it reaches the character it is looking for.

that works too. no reason i used that other than habit. also if the
delimiter isn't one char (common ones are [% and %], then you can't use
the negated char class. also yours doesn't work well with dealing with
escaped closing delimiters.

c> Comment: Yes, I suppose this is a home grown templating system.
c> However, instead of days of work I'm now looking at hours, and I don't
c> really have an incentive to rewrite the entire application. There is a
c> book on Catalyst that I've been thinking about buying. Would it be
c> worth my time to buy it and read it? I work on web enabled apps fairly
c> often.

catalyst isn't a templating system but a full web app engine system. the
large template systems are mason and template toolkit. there are other
smaller ones like html::template. of course mine is smaller, simpler,
faster and better. :)

uri
 
U

Uri Guttman

BM> ~% cat ts
BM> #!/usr/bin/perl

BM> use 5.010;
BM> use Template::Simple;

BM> my $str = "foo #bar# baz #quux#";
BM> my %data = ( bar => "ONE", quux => "TWO" );
BM> my $out = Template::Simple
-> new(pre_delim => qr/#/, post_delim => qr/#/)
-> render(\$str, \%data);

i tend to not daisy chain that but it works. also if you want to apply
the template object more than once you need to keep it.

BM> say $$out;

BM> ~% perl ts
BM> foo ONE baz TWO
BM> ~%

BM> No need to rewrite anything :).

nice plug!! :)

and i have added a compiled template option which works but isn't
released yet. it needs some tests and docs (not much but too low on my
list). if you want that, you can get it from the git repo at:

perlhunter.com/git

and you can browse it at:

perlhunter.com/git/gitweb/gitweb.cgi

the api is trivial, just compile a template (by name) first and then
render as normal. it is much faster than the current rendering and blows
away anything else out there. but of course comparing this to mason and
template toolkit is not fair to them! :)

uri
 
M

Mart van de Wege

There is a book on Catalyst that I've been thinking about
buying. Would it be worth my time to buy it and read it? I work on web
enabled apps fairly often.

If that is Jonathan Rockway's book, yeah it's worth it. Do keep the
perldoc close though, as there were some changes from Catalyst 5.7 to
5.8, and not some of Jonathan's advice is now considered deprecated
(like using BindLex).

Mart
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top