Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re: object-oriented composition of model-view with HTML::Seamstress

by Rhandom (Curate)
on Feb 25, 2008 at 18:00 UTC ( [id://670068]=note: print w/replies, xml ) Need Help??


in reply to object-oriented composition of model-view with HTML::Seamstress
in thread RFC - Template::Empty

Thanks for the softball.

Here is your job. I will give you an array of text strings. Without manipulating the data in the perl layer, please provide one template that shows them in a bullet list and another template that shows them in a table with three columns with data oriented in columns with one item per cell. On the column oriented version fill in   wherever there isn't a defined value. Be sure external whitespace is nice and consistent.

For extra credit - make the template decide conditionally that if you have less than n items use the bullet list - otherwise use the table.

For extra extra credit - do this exercise - but do it in a text-only based email that will be sent to a user (ie - no html tags).

----- bullet.tt --------- <ul> [%- FOREACH i IN items %] <li>[% i %]</li> [%- END %] </ul> ----- columns.tt -------- [%- cols = 3; rows = items.size div cols; rows = rows+1 IF items.size % cols %] [%- FOR i IN [0 .. rows - 1] %] <tr> [%- FOR j IN [0 .. cols -1] %] <td>[% items.${ i + rows * j } %]</td> [%- END %] </tr> [%- END %] </table> ------ optional.tt [% PROCESS ${ items.size > 10 ? "columns.tt" : "bullet.tt" } %] ------ my_perl.pl ------ use Template::Alloy; my $t = Template::Alloy->new; my $data = { items => [1 .. 10], }; $t->process("bullet.tt", $data) || die $t->error; $t->process("columns.tt", $data) || die $t->error; __END__ prints <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> </ul> <table> <tr> <td>1</td> <td>5</td> <td>9</td> </tr> <tr> <td>2</td> <td>6</td> <td>10</td> </tr> <tr> <td>3</td> <td>7</td> <td>&nbsp;</td> </tr> <tr> <td>4</td> <td>8</td> <td>&nbsp;</td> </tr> </table>


Update To me the code layer would be your perl modules and your cgi script (or your mod_perl application). The example given is simplified and contrived - but represents real life situations.
Update 2 s/specifying/manipulating the data/

my @a=qw(random brilliant braindead); print $a[rand(@a)];

Replies are listed 'Best First'.
Re^2: object-oriented composition of model-view with HTML::Seamstress
by metaperl (Curate) on Feb 25, 2008 at 19:57 UTC
    Thanks for the softball.
    and thank you for taking a swing :)
    Without manipulating the data in the perl layer, please provide one template that shows them in a bullet list and another template that shows them in a table with three columns with data oriented in columns with one item per cell.
    I read your definition of Perl layer below. The first comment is that Perl layer is not a fine-grained term. It is a monolithic concept which does not accurately point out the (supposed) source of problems with push-style dynamic html generation. The second comment is that Perl is the practical extraction and report language. It excels at manipulating data. I therefore refuse to use a template system for manipulating data. Model actions belong in model, not anywhere else. And the model is best done in pure perl.

    Your sample tt code mixes concerns. Data processing should be testable completely offline.. and completely free of any view. Mine is. Yours isn't.

    On the column oriented version fill in   wherever there isn't a defined value.
    uh... no. I will fill in space in my model code function reformat_data. Then delegate the conversion of space to &nbsp; to the HTML renderer....
    Be sure external whitespace is nice and consistent.
    What is external whitespace?
    For extra credit - make the template decide conditionally that if you have less than n items use the bullet list - otherwise use the table.
    That's really just basic Perl:
    my $model = Model->new; @$model < $n ? render_bullets($model) : render_table($model);
    For extra extra credit - do this exercise - but do it in a text-only based email that will be sent to a user (ie - no html tags).
    Well you didnt do it. I'm not sure how I would do that. Maybe an html2text tool?
    To me the code layer would be your perl modules and your cgi script (or your mod_perl application). The example given is simplified and contrived - but represents real life situations.
    It's a good example. But once you see my code, please point out the perrin-rhandom syndrome in the push-style approach or recant the criticism.
    Update 2 s/specifying/manipulating the data/
    Again a request I cannot comply with. I will never forget the time that Ovid tried to justify things like this, by calling it "presentation logic" and saying that a "presentation tool" like tt would be good for it. I disagreed with him then and with you now. No one should request data processing anywhere but a data processing giant (Perl).

    Now, it has taken me probably 1 hour to write that code. I'm sure it took you less than 5 minutes. I'm a bit rusty in Perl, since I mainly convert Perl ETL scripts to Ab Initio data flow graphs these days. But I would not have been much faster. Push-style development is much slower. And I am grateful to have had a chance to shake the rust off myself :)

    Code post follows.

    I have beheld the tarball of 22.1 on ftp.gnu.org with my own eyes. How can you say that there is no God in the Church of Emacs? -- David Kastrup
    [tag://long,live,html,seamstress,and,template,alloy,too]
    Enforce strict model-view separation in template engines via HTML::Seamstress The car is in the cdr, not the cdr in the car
      I read your definition of Perl layer below. The first comment is that Perl layer is not a fine-grained term.


      I actually thought it was. My perl layer was essentially as simple as
      my $data = get_data(); my $t = Template::Alloy->new; $t->process("bullet.tt", $data);
      I would be inclined to say that there isn't a lot of ambiguity where the Perl layer is in the example I gave. The part where you can't see a fine separation has more to do with the model that Seamstress uses (which is an argument in your favor for using Code layer vs Presentation layer - much of your presentation layer is perl). Seamstress requires you to use perl to manipulate your data. My example using TT syntax didn't.

      The second comment is that Perl is the practical extraction and report language. It excels at manipulating data. I therefore refuse to use a template system for manipulating data.
      I think you are mistaking "changing data" with "manipulating data". You'll notice in my example I didn't change a thing about the data - I just accessed the array in a different order.

      The comment about the "practical extraction and report language" being used to manipulate data is a good one. The native language is the best for obtaining, changing and/or sorting the data. But if we are extolling the merits of the "reporting" language, why do you not also use perl to actually do the reporting. Well, that is obvious - we need to separate our concerns and perl's internal facilities for translating data into HTML are not nearly as easy to use as a templating system.

      Model actions belong in model, not anywhere else. And the model is best done in pure perl.
      I concur. My model - the data arrayref in this case - is entirely in pure perl. And my view is entirely in the templating language.

      Your sample tt code mixes concerns. Data processing should be testable completely offline.. and completely free of any view. Mine is. Yours isn't.
      Um. Check again. All of my "data processing" was accomplished in the [1 .. 10] - I can test that offline completely. It is entirely free of view. Re-formatting how it is displayed is a concern of the view. The data doesn't change.

      What is external whitespace?
      That would be the whitespace outside of the HTML tags. TT syntax gives you full control over whitespace (see the PRE_CHOMP and POST_CHOMP operators in TT documentation). Pretty much all other template systems I've seen make you place your HTML tags/elements in odd configurations to make the output look nice. To some, external whitespace really doesn't matter - but as somebody who has had to debug a large amount of HTML source - it matters to me.

      For extra credit - make the template decide conditionally that if you have less than n items use the bullet list - otherwise use the table. That's really just basic Perl:
      I think you missed the point. Sure I could do that in Perl - the point is that I don't have to. If the HTML design team wants different criteria - I don't have to touch the perl code.

      Well you didnt do it.
      I apologize - I figured it was obvious in the TT languages. The way I would do it is I'd remove the HTML tags from the template. And then I'm done. All of the HTML DOM based template systems work with XML/HTML only and don't have facilities for making other non-XML/HTML things easy. And just for completeness - here is the template for the first example that would be suitable for use in a text email:
      [%- FOR i IN items %] * [% i %] [%- END %]


      It's a good example. But once you see my code, please point out the perrin-rhandom syndrome in the push-style approach or recant the criticism.
      While your code is well written and nicely formatted, and the logic is impeccable - it just doesn't look like fun. Obviously Seamstress is very capable and you have built a very fine piece of software - but I'd rather get my data ready and hand off to the template and let the template do what it is good at. The amount of hoops you had to jump through in the "Perl Layer" to complete some of the sample problem only seems to confirm the issue.

      Update 2 s/specifying/manipulating the data/ Again a request I cannot comply with. I will never forget the time that Ovid tried to justify things like this, by calling it "presentation logic" and saying that a "presentation tool" like tt would be good for it. I disagreed with him then and with you now. No one should request data processing anywhere but a data processing giant (Perl).
      Once again you are confusing processing the data with presenting it. Perhaps my use of "manipulate" has been wrong - I have intended it in the form of "format" the data or "manipulate it into display shape" while you seem to have interpreted it as "change the data." While TT does let you change the data itself - this is normally a bad idea except for the cases when it isn't. But you do have the option when the occasion requires it. Either way - the presentation layer must have the ability to layout the data however it wants to.

      And I am grateful to have had a chance to shake the rust off myself :)
      I'm glad I could in someway provide some benefit to you - even if very indirectly. Seamstress is a fine piece of work. For my use cases it seems more academic than practical - but my use cases probably vary greatly from yours.

      In the end, I should've clued in a little earlier that I shouldn't get involved in a religious debate about templating systems. I apologize for taking up everybody's valueable time. Hopefully some parts of this discussion have been helpful to somebody out there.

      my @a=qw(random brilliant braindead); print $a[rand(@a)];
Re^2: object-oriented composition of model-view with HTML::Seamstress
by metaperl (Curate) on Feb 25, 2008 at 20:08 UTC
    Here is your job.
    And here is the code obtainable via hg clone http://hg.metaperl.com/seamstress

    Driver script

    use strict; use warnings; use Model; use View::bullet; use View::table; my $model = Model->new; # View 1 my $view = View::bullet->new; $view->render($model); warn $view->as_HTML; # View 2 my $cols = 3; my $tabular_model = $model->reform_data($cols); my $view = View::table->new; $view->render($tabular_model); warn $view->as_HTML;

    Model package

    package Model; use Array::Group; use Data::Dumper; # A model is overkill for this example, but lets plan for # scaleability sub new { my $data = [1 .. 10] ; bless $data, __PACKAGE__ ; return $data; } sub reform_data { my $aref = shift; my $cols = shift; my $tabdata = Array::Group::ngroup $cols => $aref ; # This filling of the last row should be an option to # Array::Group... my $last_row = $tabdata->[$#$tabdata] ; my $diff = $cols - @$last_row; my @nbsp = (' ') x $diff; push @$last_row, @nbsp; return $tabdata; } 1 ;

    View code

    bullet

    package View::bullet; use base qw(HTML::Seamstress); my $file = 'html/bullet.html'; sub new { __PACKAGE__->new_from_file($file); } sub render { my $tree = shift; my $model = shift; my $li = $tree->look_down(class => 'nums'); $tree->iter($li => @$model) ; return $tree; } 1;

    table

    package View::table; use base qw(HTML::Seamstress); my $file = 'html/table.html'; sub new { __PACKAGE__->new_from_file($file); } sub render { my $tree = shift; my $data = shift; $tree->table2 ( table_data => $data, td_proc => sub { my ($tr, $data) = @_; my @td = $tr->look_down('_tag' => 'td'); for my $i (0..$#td) { $td[$i]->splice_content(0, 1, $data->[$i]); } } ) ; return $tree; } 1;
    I have beheld the tarball of 22.1 on ftp.gnu.org with my own eyes. How can you say that there is no God in the Church of Emacs? -- David Kastrup
    [tag://html,templating]
    Enforce strict model-view separation in template engines via HTML::Seamstress The car is in the cdr, not the cdr in the car

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://670068]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-19 10:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found