Re: Ordering Template cards
by kcott (Archbishop) on Jan 25, 2021 at 01:00 UTC
|
G'day Bod,
"But I especially do not like the conditional subroutine calls with magic numbers in the statement modifiers."
As far as the magic numbers are concerned, you can associate them with meaningful names;
however, I really wanted to comment on the "conditional subroutine calls".
Consider using a despatch table.
Here's a rough example.
my %handler_for = (
card1name => \&card_call_center,
card2name => \&card_price_data,
...
);
...
while (my $card = ...) {
$handler_for{$card}->();
}
| [reply] [d/l] |
|
Consider using a despatch table
Fabulous - thanks Ken and ++
That's exactly the kind of "more elegant solution" I was looking for. It is clearer, more extensible and much nicer than my solution.
| [reply] |
Re: Ordering Template cards
by 1nickt (Canon) on Jan 24, 2021 at 16:46 UTC
|
Hi,
"Should I be calling multiple template files from the script or should that processing be done within Template?"
Seems to me from what you've shown that for each rendering of the page, you will have an array of cards that you have pulled from the DB for the given user. If that's the case then I would use a [% FOREACH card IN cards %] ... [% END %] block in the template and just show 'em in the order given. See http://www.template-toolkit.org/docs/manual/Directives.html#section_Loop_Processing.
Hope this helps!
The way forward always starts with a minimal test.
| [reply] [d/l] |
|
I would use a [% FOREACH card IN cards %] ... [% END %] block in the template and just show 'em in the order given
I do exactly that when each card is a similar layout and it's really just a case of substituting in values. But here, every card is totally different. Some have tables, others just a heading a line of text whereas others have a graph. Hence why I was thinking of a separate template file for each card.
| [reply] [d/l] |
|
If the cards are that different, I would keep them as separate files. This will make it easier to make changes to limited parts of the cards, and will maybe even encourage you to copy a template file for a slightly (but enough) different new card.
As switching to a different card means a reload (I guess), keeping the different cards as different templates makes sense.
| [reply] |
|
|
Hm, so maybe the card hash in the array cards has a field card.data and a field card.template, and then inside the loop you process the template with the data using INCLUDE? You can reference data in variables using a sigil in nested directives for this.
Hope this helps!
The way forward always starts with a minimal test.
| [reply] [d/l] [select] |
|
[OT] LaTeX for PDF? Re: Ordering Template cards
by bliako (Monsignor) on Jan 24, 2021 at 20:41 UTC
|
Hi, this is irrelevant to your questions but I thought I mention one of the many ways to produce PDF output from your cards, e.g. for attaching to these email reports. This is of course LaTeX. It produces exceptional PDF/PS documents using a simple html-like (ok ok!) language which loves to be template-ed. You first need to install latex (TexLive will be called in your package manager) on your webserver. Then there are some Template plugins for it, e.g. Template::Latex (also Template::Plugin::Latex) and you have a Template-like interface to producing PDF files. The caveat is that you must now maintain both HTML and LaTeX templates. The latter is a pleasure though...
If there was a Nobel for Computer Programs, more than one would have been awarded to the humble, open-source and free TeX/LaTeX - created by a true scientist with an eye for aesthetics and detail.
Apropos your multiple templates dilemma. I would go with as many templates as possible if that would not hinder performance. For example, you can find out that card_call_center and card_sales have some common parts, let's say customer address. Well that part can be templated and re-used. Again, I don't know what effect this ultra-fine templating will have on performance.
bw, bliako
| [reply] [d/l] [select] |
|
Thanks bliako, I shall look at LaTeX as I have had need to dynamically generate PDFs in the past. They are not needed for this application but no doubt I'll need them again one day.
Apropos your multiple templates dilemma. I would go with as many templates as possible if that would not hinder performance.
Performance really is not an issue here. The process of collecting together the information is quite slow as some of it calls web APIs. It is an ultra low traffic web page and, as they are done by CRON in the early hours of the morning, creating the emails is not something where time is important.
It was more the block of conditional subroutines that seemed to be a rather inelegant way of doing things.
| [reply] |
Re: Ordering Template cards
by LanX (Saint) on Jan 25, 2021 at 21:04 UTC
|
| [reply] [d/l] [select] |
|
y $cards = $dbh->prepare("SELECT Card_idCard FROM CardList WHERE User_
+idUser = $user_number AND visible = 1 ORDER BY metric");
But you are right to point out this undefined behaviour. However, when written as you suggest, it always returns true (I haven't completely got my head around why) and the loop doesn't end. It needs to be written like this:
my $card;
while (($card) = $cards->fetchrow_array)
| [reply] [d/l] [select] |
|
I haven't completely got my head around why
In while (my ($card) = $cards->fetchrow_array), the boolean condition being evaluated is the return value of the list assignment ()=..., and as per Assignment Operators, "a list assignment in scalar context returns the number of elements produced by the expression on the right hand side of the assignment."
However, like LanX, I can't reproduce this, and you'd have to show an SSCCE that does. Are you sure you didn't accidentally write fetchrow_arrayref instead? That method returns undef if no more rows remain - and as per the above, it's one value being assigned, so the return value of the list assignment is 1 (true). For fetchrow_arrayref, the correct loop condition is while (my $card = $cards->fetchrow_arrayref), since that's a scalar assignment and it'll return undef (false).
use warnings;
use strict;
use Data::Dumper;
use DBI;
my $dbh = DBI->connect("dbi:SQLite:dbname=:memory:", undef, undef,
{ RaiseError=>1, AutoCommit=>1 } );
$dbh->do(q{ CREATE TABLE foobar ( foo TEXT, bar TEXT ) });
my $in = $dbh->prepare(q{INSERT INTO foobar (foo,bar) VALUES (?,?)});
$in->execute('a','b');
$in->execute('c','d');
my $cards = $dbh->prepare('SELECT * FROM foobar');
$cards->execute;
while (my ($card) = $cards->fetchrow_array) {
# OR
#while (my $card = $cards->fetchrow_arrayref) {
# BUT NOT
#while (my ($card) = $cards->fetchrow_arrayref) {
print Dumper($card);
}
| [reply] [d/l] [select] |
|
|
|
|
|
my $card_ref = $cards->fetchall_arrayref;
foreach my $card (@$card_ref)
{..}
This does expand the memory used by Perl to include the entire result set. But that is fine if the result set is "small" and the memory used for that result is recycled for further use by Perl. "Small" is a relative term. Nowadays, I would consider a 10,000 line result set as "small".
| [reply] [d/l] |
|
|
|
|
> However, when written as you suggest, it always returns true
That's hard to believe, it's a very common construct.
And a little test in the debugger doesn't show any endless loop.
DB<2> while ( my ($x) = () ) {die $x}
DB<3>
| [reply] [d/l] |
|
A reply falls below the community's threshold of quality. You may see it by logging in. |