11

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

Edward Barnard
  • 346
  • 3
  • 17
  • 4
    Something I would suggest for the first step is to create a test suite. You might want to do some sort of parsing and comparing of the old and new document in the first step of the outsourcing to ensure that you do not break anything, as well as functionality testing (Selenium comes to mind here). The second part you can keep after you've rewritten the HTML, which in turn you can also unit-test for HTML validity/compliance. – simbabque Nov 24 '12 at 09:36

2 Answers2

11

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.

  1. 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.
  2. 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 ]
  3. 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 ].
  4. 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 %] - <a href="/logout/user/[% $user.id %]">Log Out</a>
[% ELSE %]
   Please <a href="/user/login">Log In</a>
[% 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(<a href="/logout/user/[% $userid %]">Log Out</a>)
}

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.

Len Jaffe
  • 3,442
  • 1
  • 21
  • 28
  • 3
    Thank you, Len! I strongly agree with your four general points (except I have never used HAML). Most of the domain logic is in .pm modules. We would prefer to implement as much as possible in pure Perl. This is a UI (User Interface) refresh as opposed to a rewrite! That's why I was thinking TT would be a good choice in that we are still ALLOWED to use pure Perl. Your example of using Perl underneath the template is exactly what I needed. I was hoping it would be (relatively) that easy. Ed (now I know Return posts the comment) – Edward Barnard Nov 24 '12 at 01:24
  • 1
    SO comment entry is a harsh mistress :-) – Len Jaffe Nov 26 '12 at 14:53
  • 1
    I was a HAML nay-sayer for a while. I avoided it, along with python and ruby, for their use of semantic whitespace. I've since converted, and am quite happy with HAML, and will probably use it on the next project I start form scratch. The one i use it on now started with ERB, and I've been converting as time allows. – Len Jaffe Nov 27 '12 at 19:40
  • I like what you have to say about minimizing logic in templates. But if you go this far limiting templates, why not just use a pure HTML document as your template? You can modify the "template" by parsing the page and munging the DOM. So `
    ` can be replaced using something like HTML::TreeBuilder `my $u_info = $doc->look_down( 'id', 'user_info' ); $u_info->push_content( '...' );` Now, I've only used this technique on small projects, but it works well there.
    – daotoad Dec 03 '12 at 18:49
  • Because a template typically does not have the overhead of HTML parsing, only regex search for delimiters that must follow specific rules to prevent the need to parse HTML. The short answer is, you have to draw a line somewhere. Mostly we use templates because they imply the print statement around all the HTML. we could just as easily have used a bunch of print statements, and depending on how much component decomposition one does, it often seems that way anyway. I guess templates just feel like the correct level of abstraction. – Len Jaffe Dec 03 '12 at 20:14
3

As part of your test suite, consider an HTML validator like HTML::Lint or HTML::Tidy.

Andy Lester
  • 91,102
  • 13
  • 100
  • 152
  • 1
    Andy, question: will HTML::Lint get upset by the html 5 doctype "!DOCTYPE HTML" ? We're not actually using anything html5-specific, so Lint/Tidy should be of great use. I see you have Test::More wrappers. We can run our CGI scripts command line, so running lint against the current output will hopefully provide a good survey of the current situation. And keeps the conversion on the right track, a la Test-Driven Development. Thank you for the suggestion. – Edward Barnard Nov 26 '12 at 19:27
  • 2
    HTML::Lint is unaware of HTML 5. Also, instead of running your CGI scripts from the command line, use a tool like `WWW::Mechanize` to fetch a page via the web server, just like a browser would, and then you can feed that page into HTML::Lint. That way you're testing something a lot closer to the real thing. – Andy Lester Nov 26 '12 at 20:04
  • 1
    Thanks Andy! Mechanize may be an issue for us because of not wanting to build authentication into the test harness. But I'll definitely look at that option. – Edward Barnard Nov 26 '12 at 20:28
  • 2
    The other benefit of using `WWW::Mechanize` is that it also has `Test::WWW::Mechanize` that lets you write test programs easily. Where a Mech program might have `$mech->get($url)`, you put that in a test program and change it to `$mech->get_ok($url)`, and voila, it does the GET and then verifies that it's correct. PLUS, `Test::WWW::Mechanize` can automatically run HTML::Lint on every page for you without even having to think about it. – Andy Lester Nov 26 '12 at 20:34
  • WWW::Mechanize, Test::WWW::Mechanize, HTML::Lint, and Test::HTML::Lint are now on the list. Thanks again Andy! – Edward Barnard Nov 26 '12 at 20:37