What's the right way to display a DBIx::Class ResultSet in my Catalyst project that uses Template Toolkit? - perl

Given a DBIx::Class resultset, for example:
my $rs = $c->model("DB::Card")->search({family_name => "Smith"});
the tutorials I've read use the stash to pass an arrayref of rows:
$c->stash->{cards} = [$rs->all];
This results in the query getting executed at this point, and the resulting objects stuffed into the stash, so they can be used in TemplateToolkit as:
[% FOREACH card IN cards %]
[% card.given_name %] [% card.family_name %]
[%END%]
Is there a proper way to have TT iterate over the rows as they get fetched from the DB?

Sure. You can pass the result set directly to TT and iterate over it in the template.
$c->stash->{cards} = $rs;
...and then:
[% WHILE (card = cards.next) %]
[% card.given_name %] [% card.family_name %]
[% END %]

Or, even better:
$c->stash(cards => $rs);
...in TT template:
[% FOREACH card = cards %]
[% card.given_name %] [% card.family_name %]
[% END %]

I do:
#{$c->stash->{cards}} = $rs->all;
In the template:
[% FOREACH card IN cards %]
[% card.given_name %] [% card.family_name %]
[% END %]

I WAS doing exactly the same thing as the author.
In trying to create a more strict MVC approach, I'm now processing the DBIC objects in the controller and passing a very simple stash for the template to display. (Key benefit being the code is reusable by other scripts instead of just the web interface.)
I'm curious if anyone knows if this is more efficient or not, processing or memory-wise. I would think the first method results in less processing time but holds onto memory longer. I'd guess my new approach takes a bit more processing and a bit more memory temporarily, but the potentially large result set object doesn't live as long.

Related

with Catalyst framework, how to check the uri path in wrapper.tt

I like to check the path in wrapper.tt, so I can dynamically include links based on the path. But I can't figure out the way to get the path in the tt file after extensive online search. For example, I can check if a user has signed in and, if yes, show the user's name as the following. Thanks.
[% IF c.user_exists %]
<h4 style="float:right;">[% c.user.username %] logout</h4>
[% ELSE %]
<h4 style="float:right;">login</h4>
[% END %]
I got the answer. c.req.path is the uri path.

Implementing drop down box in perl cgi for a value selected from database

I am writing a cgi script to show a form which displays database results for a given id and also allowing the user to make changes and submit.
Eg. select * from insp_gang where id = 1;
The form has few text fields which shows the results from the above table.
Now i have to implement a drop down box for a column s1_q1 from the select query.
It can have 3 values. (Full,Part,No)
By default the drop down should show the current value returned by the query for this id.
At the same time, the user might be able to change it.
How do I do this?
I have the below code inside html block in my cgi script. $job->{s1_q2} is the variable that has the value from the database.
<tr><td><b> s1_q2 </b></td><td>
<select name="s1_q2">
<option value="Full">Full</option>
<option value="Part">Part</option>
<option value="No">No</option>
<option value="N/A">N/A</option>
</select>
</td></tr>
The basic approach would be to create your options in a loop, and to check against the current value on each iteration of the loop.
my #values = qw[Full Part No N/A];
foreach my $value (#values) {
print qq[ <option value="$value"];
print ' selected' if $value eq $job->{s1_q2};
print qq[">$value</option>\n";
}
But this kind of logic should be implemented using a templating engine.

Trouble pinpointing child elements while using Mojo::DOM

I'm trying to extract text from an old vBulletin forum using WWW::Mechanize and Mojo::DOM.
vBulletin doesn't use HTML and CSS for semantic markup, and I'm having trouble using Mojo::DOM->children to get at certain elements.
These vBulletin posts are structured differently depending on their content.
Single message:
<div id="postid_12345">The quick brown fox jumps over the lazy dog.<div>
Single message quoting another user:
<div id="postid_12345">
<div>
<table>
<tr>
<td>
<div>Quote originally posted by Bob</div>
<div>Everyone knows the sky is blue.</div>
</td>
</tr>
</table>
</div>
I disagree with you, Bob. It's obviously green.
</div>
Single message with spoilers:
<div id="postid_12345">
<div class="spoiler">Yoda is Luke's father!</div>
</div>
Single message quoting another user, with spoilers:
<div id="postid_12345">
<div>
<table>
<tr>
<td>
<div>Quote originally posted by Fred</div>
<div class="spoiler">Yoda is Luke's father!</div>
</td>
</tr>
</table>
</div>
<div class="spoiler">No waaaaay!</div>
</div>
Assuming the above HTML and an array packed with the necessary post IDs:
for (#post_ids) {
$mech->get($full_url_of_specific_forum_post);
my $dom = Mojo::DOM->new($mech->content);
my $div_id = 'postid_' . $_;
say $dom->at($div_id)->children('div')->first;
say $dom->at($div_id)->text;
}
Using $dom->at($div_id)->all_text gives me everything in an unbroken line, which makes it difficult to tell what's quoted and what's original in the post.
Using $dom->at($div_id)->text skips all of the child elements, so quoted text and spoilers are not picked up.
I've tried variations of $dom->at($div_id)->children('div')->first, but this gives me everything, including the HTML.
Ideally, I'd like to be able to pick up all the text in each post, with each child element on its own line, e.g.
POSTID12345:
+ Quote originally posted by Bob
+ Everyone knows the sky is blue.
I disagree with you, Bob. It's obviously green.
I'm new to Mojo and rusty with Perl. I wanted to solve this on my own, but after looking over the documentation and fiddling with it for a few hours, my brain is mush and I'm at a loss. I'm just not getting how Mojo::DOM and Mojo::Collections work.
Any help will be greatly appreciated.
Looking at the source of Mojo::DOM, basically the all_text method recursively walks the DOM and extracts all text. Use that source to write your own walking the DOM function. Its recursive function depends on returning a single string, in yours you might have it return an array with whatever context you need.
EDIT:
After some discussion on IRC, the web scraping example has been updated, it might help you guide you. http://mojolicio.us/perldoc/Mojolicious/Guides/Cookbook#Web_scraping
There is a module to flattern HTML tree, HTML::Linear.
The explanation of purpose for flatterning HTML tree is a bit long and boring, so here's a picture showing the output of the xpathify tool, bound with that module:
As you see, HTML tree nodes become single key/value list, where the key is the XPath for that node, and the value is the node's text attribute.
In a few keystrokes, this is how you use HTML::Linear:
#!/usr/bin/env perl
use strict;
use utf8;
use warnings;
use Data::Printer;
use HTML::Linear;
my $hl = HTML::Linear->new;
$hl->parse_file(q(vboard.html));
for my $el ($hl->as_list) {
my $hash = $el->as_hash;
next unless keys %{$hash};
p $hash;
}

MVC3 and Razor - How to place a dynamic value for hidden field?

I'm a beginner about Razor, and sometimes I get stuck with really simple things.
I have this foreach loop:
#foreach (dynamic item in ViewBag.EAList)
{
<li>
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
}
</li>
}
This line:
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
Doesn't work, and I don't know how to make it work, I tried many ways, without #, with (--), with #(--)...
Could someone help me to display the dynamic value in my hidden field?
In addition, if someone know about a good Razor samples websites, I would be very thankful.
I had the same problem, found that a simple cast solved my problem.
#Html.Hidden("id", (string) ViewBag.ebook.isbn)
In Razor, once you are in "C# land", you no longer need to prefix values with # sign.
This should suffice:
#Html.Hidden("EstadoDeAlmaID", item.EAID)
Check out Scott Gu's article covering the syntax for more help.
Update
And I would also move your <li></li> within your using block, as Razor works better when you wrap HTML blocks inside of a code blocks.
Also, your Html.BeginForm should live outside of your loop.
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<ul>
#foreach (? item in ViewBag.EAList)
{
<li>
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
</li>
}
</ul>
}
Where ? in the foreach loop is the type of your items in EAList.
To avoid the Extension methods cannot be dynamically dispatched exception, use a model instead of ViewBag so you will not be using dynamic objects (this will avoid all the unnecessary casting in the View and is more in line with MVC style in general):
In your action when you return the view:
return View("ViewName", db.EAList.ToList());
In your view, the first line should be:
#model IEnumerable<EAListItem> //or whatever the type name is
Then just do:
#foreach(var item in Model)
You got the error, "Extension methods cannot be dynamically dispatched"... therein lies your trouble.
You should declare you loop variable not to be of type dynamic, but of the actual type in the collection. Then remove the # from the item.EAID call inside the #Html.Hidden() call.
The simple solution for me was to use ViewData instead of ViewBag. ViewBag is just a dynamic wrapper around ViewData anyway.
#Html.Hidden("ReportID", ViewData["ReportID"])
but I don't know if this will help in your case or not since you are creating dynamic items in your foreach loop.
I have found that when i want to use the view bag data in the HTML
Getting back to basics has often worked for me
<input type="hidden" name="Data" id="Data" value="#ViewBag.Data" />
this gave the same result.

using Catalyst::Model::MenuGrinder

I am trying to add a dynamic menu to my WebApp using Catalyst::Model::MenuGrinder
the documentation isn't very clear on how to configure access to via roles using this model.
has anyone implemented dynamic menu's using this Model?
I found this article in the Catalyst Advent calendar and have set it up correctly I just don't know how to configure it to restrict menu options based on roles.
Any help would be appreciated
thanks
As documented in the RequirePrivilege module, you restrict a menu entry based on privileges by adding keys like need_priv and need_user_in_realm. MenuGrinder "privileges" correspond to Catalyst authentication "roles", so if you want to require that a user has the role "admin" for a section, you just do
<item>
<label>Admin Section</label>
<need_priv>admin</need_priv>
<item>...</item>
<item>...</item>
</item>
Just take a look at your template file which generates the menu actually. Let's say for example that the "Clothes" menu should be display for admins only.
You can edit you xml file, like:
<item role="adminonly"><label>Clothes</label>...
and after that you should only check in the menuitem BLOCK if the user has the desired role:
[% BLOCK menuitem %]
<ul [%- IF menu.class %]class="[% menu.class %]"[% END %]>
[% FOREACH item = menu.item %]
[% IF item.role %]
[% IF c.user_exists && c.check_user_roles( item.role ) %]
<li [% IF item.active %]class="active"[% END %]>
[% item.label %]
[% IF item.item %]
[% PROCESS menuitem
menu = item
%]
[% END %]
</li>
[% END %]
[% END %]
[% END %]
</ul>
[% END %]
[% PROCESS menuitem %]
PS: Never tested the above code