I'm using template toolkit to form a simple JSON response (see the code below).
I need to put a comma after all elements of the response except the last.
I believe I need to make use of TTs iterator, however I'm not getting it right.
With this code, a comma is still printed on the end of the last element.
The problem lies with the section that contains
[% UNLESS outer.last && loop.last %],[% END %]
this should add a comma unless the outer and inner loops are on their last iteration.
Any help on what I'm getting wrong greatly appreciated.
{ "success": true, "filesdata": [
[%~ USE outer = iterator(objects); FOREACH object IN outer;
FOREACH rep IN object.reps;
IF rep.rep == reptype %]
{ "id":"[% object.id | xml %]", "url":"[% rep.src | xml %]", "story":"[% object.story | xml %]" }[% UNLESS outer.last && loop.last %],[% END %]
[%~ END;
END;
END ~%]
] }
This works for me:
[% IF loop.last %]}[% ELSE %]},[% END %]
Have you tried using the join vmethod? You can create a list and join it with a comma:
[% items.join(', ') %]
Having said that, you may also want to look at Template::Plugin::SimpleJson. You could create a hash and then pass it to this plugin. However you do decide to do it, you probably don't want to worry about quoting your JSON in the actual template file and using something like this could save you some heartache down the line.
There's also the option of creating the JSON outside of the template itself, but that's outside the scope of your question.
Related
I have run some scripts to generate storage capacity report and configuration settings report using perl. I would like send this report to my mail id in html using perl.
Please note that I am new to perl programming.
Your question is very vague, so it's hard to be of much help. This might point you in the right direction though.
You basically have three tasks here.
Parse your report into data structures.
Use your data structures to generate an HTML document.
Send the HTML document by email.
I can't really help with step 1 as I know nothing about your report. If you have your file in CSV format, then Text:CSV will be useful to you. It's worth pointing out that if you're generating this report, then you could generate it in a format that is easier to parse - JSON, for example.
For step 2, I'd recommend a templating engine. I'd use the Template Toolkit, but other options are available. The idea is that you create a template file that contains all of your HTML with "tags" where you want your variable data to go. On a simple level it might look something like this:
<html>
<head><title>Some Title</title></head>
<body>
<h1>Some Title</h1>
<p>Blah...</p>
<table>
[% FOREACH row IN data -%]
<tr><td>[% row.value %]</td><td>[% row.another_value %]</td></tr>
[% END -%]
<table>
</body>
</html>
Assuming that's in a file called email.tt and you have your data in an array of hashes called #data, then you'd process the template like this:
use Template;
#data = ({
value => 'something',
another_value => 'something else',
}, {
value => 'something',
another_value => 'something else',
});
my $tt = Template->new;
$tt->process('email.tt', { data => \#data }, \$email_body)
or die $tt->error;
That will give you your expanded HTML in $email_body. And that brings us to step 3.
I recommend Email::Stuffer for sending email.
use Email::Stuffer;
Email::Stuffer->from ('you#example.com')
->to ('someone_else#example.com')
->html_body($email_body)
->send;
Is there a way to detect what browser you are using through template toolkit? For example I can achieve what I want to do using jQuery but thought it would be useful to know how to do it in template toolkit if possible?
Jquery
<script>
jQuery(window).load(function(){
if ( (jQuery.browser.msie && jQuery.browser.version < 9.0) )
{
jQuery('body').addClass('old-ie');
}
});
</script>
In template toolkit I want to do something like the below but can't see this in the documentation anywhere?
<body
[% IF browser.msie && browser.version < 9.0 %]
class="old-ie"
[% ELSE %]
[% END %]
>
I'm not aware of a TT plug-in for this, but it's trivial to add a line or two to your back-end application to make the information available to the template. For example, if your app is Catalyst based, you would add something like this to your main program:
__PACKAGE__->apply_request_class_roles(qw/
Catalyst::TraitFor::Request::BrowserDetect
/);
... and in your 'auto' handler, introduce a line such as (untested):
$c->stash(browser => $c->req->browser);
... or just use this is your template:
[%- SET browser = c.req.browser;
SET old_ie = 'class="old-ie"'
IF browser.windows && browser.ie && browser.public_major < 9.0;
-%]
and then include old_ie wherever it's required in your template.
See Catalyst::TraitFor::Request::BrowserDetect and HTTP::BrowserDetect for more info and options. I'm sure there are similar plugins/methods for Dancer, Mojolicious etc.
You could do that through the HTTP request headers. You didn't mention what was calling TT, but if you're using Catalyst, you could use Catalyst::TraitFor::Request::BrowserDetect, and then pass a variable to TT to say what kind of browser was requesting the page.
I have a relatively large legacy Perl/CGI/DBI web application which generates HTML on-the-fly piece by piece. We are reworking the HTML being produced, to come in to compliance with HTML 5 / CSS 3. This would be a good time to move to some sort of template system. We do NOT want to engage in a complete rewrite, and thus do NOT want to migrate to a framework such as Catalyst.
I'm thinking that Perl Template Toolkit might be our lowest-impact means. I'm re-reading the venerable Badger Book to study up on feasibility.
My question is this. Has anyone here migrated "old school" Perl web code to Template Toolkit? Are there any tricks you can share for minimizing the rewrite/rework needed? We have not 100% decided on Template Toolkit, either. If there is an alternative which we should consider?
What problem, specifically, are we trying to solve? We're generating invalid HTML and need to clean that up. Since we're cleaning, we want to produce fully-valid HTML 5 and, to the extent practicable, valid CSS3 and Javascript. We use jQuery, jQuery UI widgets, and AJAX via jQuery. We have typical Page Controller MVC architecture except no View layer as such. We'd like to go to some sort of template system but don't want to rewrite everything (or much of anything!) to migrate.
Thank You!
Ed Barnard, Cannon Falls MN
Here's what I've found, as I've moved my practice from CGI.pm to TT, and what I've also learned along the way using HTML::Mason, HTML::Template, Text::Template, and working with ERB and HAML in Rails.
The more logic you have in in your displays, especially if it is written in a display specific language, the less fun you're going to have.
I've come to prefer HAML for the reduction in size of the contents of my templates [ In HAML, closing tags are implied by indentation ]
Perform as much of the logic to compute the various dynamic bits of the page before you drop into the template, in the application's native language. [ call view methods, before engaging in rendering ].
In relation to (3), by using methods, instead of inline display/rendering logic, you can make your templates declarative, so while you might be performing logic in the middle of the render, your templates don't have a bunch of IF/THEN/ELSE logic causing clutter.
Let's imagine a reasonably small, contrived web page made up of a header, and footer and a body. Let's assume that the footer is completely static, the body changes every time a new page loads, but the header only changes when a user logs in/out.
You can imagine the header containing code like this:
<header>
[% IF $user.is_logged_in THEN %]
Hello [% $user.name %] - Log Out
[% ELSE %]
Please Log In
[% END %]
</header>
But you're better off in the long term doing this in header.tt:
<header>
[% user_info($user) |html %]
</header>
and this in View::Helpers::Header.pm:
sub user_info {
my $user = shift;
if ($user->{is_logged_in} ) {
return "Hello $user->{name} - " . logout_link($user->{id});
}
else {
return "Please " . login_link();
}
}
sub logout_link {
my $userid = shift;
return qq(Log Out)
}
You can, of course, implement the view helpers in TT rather than pure Perl, and I don't have any kind of numbers for you, but if you've done all of your logic in Perl already, you can refactor the Perl into modules (if it isn't there already), instead of re-coding them in TT.
As part of your test suite, consider an HTML validator like HTML::Lint or HTML::Tidy.
I am working on catalyst to design a front end for a database. I am new to perl and catalyst. I am stuck with one error in the controller.
I want to fetch gene names from one table and pass each gene name to a subroutine gene_name(call a subroutine gene_name) which will perform certain function. I some how fetched the column with gene names but they are in the form of Hash reference to other table. My call to gene_name is not working.
Any idea how to pass the values to the subroutine rather than reference?
My code is as follows:
my #gene_list = $c->model('Gene::GeneTable')->search({
},{
column =>[qw/symbol/],
}
);
foreach my $gene (#gene_list){
push #gene_aliases, &gene_name($gene);
}
The error I am getting after executing the code is as follows:
DBIx::Class::ResultSet::find(): Can't bind a reference (MyApp::Model::Gene::GeneTable=HASH(0x7ff6d88b7c58))
UPDATED
My gene_name() subroutine is in another module which I have included in the controller. Now I have changed the for loop(in controller) as following and currently it is passing gene name to gene_name() to fetch aliases(But query remains the same):
foreach my $gene (#gene_list){
push #gene_aliases, &gene_name($gene-> symbol);
}
I am accessing the #gene_aliases in my view file as follows:
[% FOREACH g in gene_aliases -%]
[% g %]
[% END -%]
The above code is fetching hash references instead of names in the page. I am not able to display the gene names on web page.If I give [% g.symbol %] in for loop then the page would be empty with no error message.Hope this would be sufficient information for answering my question. If not I would elaborate on it.
My SQL query is as follows:
Select Symbol from GeneTable;
Thanks in advance
UPDATED2
As I am not a full time programmer, I am asking these questions on the blog. Kindly help me resolve my issues. If in case I have search form which takes gene name from user and this gene name has to be passed to the gene_name() subroutine, how do I pass this (to the controller and how to call the gene_name()). Kindly help me in resolving this issue.
My form in view.tt is as follows:
<form method="post" action "[% c.uri_for('/gene')%]">
<input type="text" name="search_alias">
<input type="submit" name="alias_search" value="Search">
</form>
All my aliases are in the other table called Alias, which is been used in gene_name() subroutine.
Either the gene_name() function needs to expect a GeneTable object being passed to it, or you would call it like this: gene_name($gene->symbol).
Having said that, the Controller does not look like the right place for this, based on what limited information you've provided.
It would make a lot more sense to have gene_name as a method of Gene::GeneTable itself, ie:
package GeneTable;
...
sub gene_name {
my $self = shift;
my $gene_name = ... # do something with $self->symbol
$gene_name
}
...
So that the ->gene_name method is available to any GeneTable object in any context.
UPDATE following OP's initial two comments
It sounds like gene_name() (which possibly should be called gene_aliases()) is a method of a Gene object. Putting such a conversion into a Controller does not adhere to good MVC philosophy.
Presumably you have model code somewhere - MyApp/Model/Gene/ most likely - and this sub should exist inside Gene/GeneTable.pm. Then you could use it thus:
[%- FOREACH g IN gene_list -%]
[% g.symbol %]<br/><ul>
[%- FOREACH gn IN g.gene_name -%]
<li>[% gn %]</li>
[%- END -%]
</ul>
[%- END -%]
in your template, and anywhere else you have a GeneTable object or collection thereof.
UPDATE #2 Following updated question
DBIx:Class will return an array of objects when called in list context as you do. Perhaps you need to add some Data::Dumper->Dumper() calls to get a handle on what each step is returning to you.
I suggest you add the following line immediately after your call to ->search():
$c->log->debug("gene_list contains: ", Data::Dumper->Dumper(\#gene_list));
...and possibly the equivalent debug for #gene_aliases after you've populated that.
That might give you a clearer picture of what you're getting back from your search, but I'd hazard a guess you're getting Gene::GeneTable objects.
Is there a way to disabling a WRAPPER that was set in new(\%config), through either the template, or a temporary override with parse()? I want to have a single default WRAPPER (that I'll use for 99.9% of my templates), but exclude a few.
I'm doing this all through Catalyst::View::TT just like the example in the configuration synopsis, except I don't want the WRAPPER to apply to all of my templates.
Edit the wrapper, to include a conditional like:
[% IF no_wrapper OR template.no_wrapper %] [% content %] [% ELSE %]
top;
[% content %]
bottom;
[% END %]
This allows me to disable the wrapper either (1) inside of template, or (2) from the stash.
[%- META no_wrapper = 1 -%]
$c->stash->{no_wrapper} = 1
META var ...; is a directive that makes var accessible through the template hash as template.var
source: http://wiki.catalystframework.org/wiki/gettingstarted/howtos/template_wrappers
Define exceptions in site/wrapper itself, and btw there are exceptions defined there already.
[% IF template.name.match('\.(css|js|txt)');
debug("Passing page through as text: $template.name");
content;
ELSE;
debug("Applying HTML page layout wrappers to $template.name\n");
content WRAPPER site/html + site/layout;
END;
-%]
I stumbled into the same problem, and created a more generalized solution that allows for dynamic switching of layouts, or to have no layout at all. See here:
More than one layout/wrapper with Dancer and Template::Toolkit