Discussion:
[Mason-devel] Leading/trailing newlines
Alex Robinson
2007-09-23 13:24:11 UTC
Permalink
Prompted by CJ's mention of stripping out redundant whitespace I
thought I'd revisit the subject.

I note the two tickets on the todo list (405, 459).

I'd like to suggest a potential solution.

If line 1219 of Request.pm (1.37) was changed like so:

-my $stack_buffer = $comp->{has_filter} ? \$filter_buffer : $top_buffer;
+my $stack_buffer =\$filter_buffer;

this would then allow something like this (assuming the existence of
a configuration parameter filter_newlines) to be added after line 1280
else {
if ($self->{interp}->{filter_newlines}) {
$filter_buffer =~ s/^\n+//;
$filter_buffer =~ s/\n+$//;
}
$$top_buffer .= $filter_buffer;
}
<<<

which would soak up all leading and trailing whitespace on components
without explicit filters which would take precedence. So to not
remove the space would simply require adding an empty filter block


Although that would perhaps do, I'd like to suggest that the
configuration parameter shouldn't be a simple on/off toggle, but
allow the following options (with 'none' the default for backwards
compatibility)

filter_newlines => none | one | all

And allow for component flags block to have the same parameter with
the same possible values to allow for overriding on a component by
component basis.

This would allow for something like:
my $filter_newlines = $comp->{flags}->{filter_newlines} ||
$self->{interp}->{filter_newlines};

if ($filter_newlines and $filter_newlines ne 'none') {
my $lines_to_filter = $filter_newlines eq 'all' ? '' : '1';
$filter_buffer =~ s/^\n{1,$lines_to_filter}//;
$filter_buffer =~ s/\n{1,$lines_to_filter}$//;
}

$$top_buffer .= $filter_buffer;
<<<

If done this way, this could be done in addition to any filter the
component actually has. Or again, the filter could be used to
override the auto stripping entirely.



Thoughts?
Alex Robinson
2007-09-23 13:39:16 UTC
Permalink
(Sorry - this should really have been a postscript to my previous
message, but I forgot)

I'm guessing that there's some big reason that I'm not understanding
behind the decision to assign \$filter_buffer or $top_buffer to
$stack_buffer.

But in the absence of there not being one, if $filter_buffer was used
in all cases, this would open the way for the component output to be
passed to the plugins that run at the end of the component's
execution. Since the follwoing would no longer be an obstacle:

"It would be desirable for this hook to have access to the
component's output as well as its return value, but this is currently
impossible because output from multiple components combine into a
single buffer"
http://www.masonhq.com/docs/manual/Plugin.html


Of course, this leaves me feeling that I am missing something. But if
you don't ask...
C.J. Adams-Collier
2007-09-23 15:02:31 UTC
Permalink
If I'm picking up what you're laying down, this would happen for each
comp() call. This would be too expensive. The filter would need to
be applied once, most likely at compile time.

sorry for the terse. baby in left hand

oh, and write tests
Post by Alex Robinson
Prompted by CJ's mention of stripping out redundant whitespace I
thought I'd revisit the subject.
I note the two tickets on the todo list (405, 459).
I'd like to suggest a potential solution.
-my $stack_buffer = $comp->{has_filter} ? \$filter_buffer : $top_buffer;
+my $stack_buffer =\$filter_buffer;
this would then allow something like this (assuming the existence of
a configuration parameter filter_newlines) to be added after line 1280
else {
if ($self->{interp}->{filter_newlines}) {
$filter_buffer =~ s/^\n+//;
$filter_buffer =~ s/\n+$//;
}
$$top_buffer .= $filter_buffer;
}
<<<
which would soak up all leading and trailing whitespace on components
without explicit filters which would take precedence. So to not
remove the space would simply require adding an empty filter block
Although that would perhaps do, I'd like to suggest that the
configuration parameter shouldn't be a simple on/off toggle, but
allow the following options (with 'none' the default for backwards
compatibility)
filter_newlines => none | one | all
And allow for component flags block to have the same parameter with
the same possible values to allow for overriding on a component by
component basis.
my $filter_newlines = $comp->{flags}->{filter_newlines} ||
$self->{interp}->{filter_newlines};
if ($filter_newlines and $filter_newlines ne 'none') {
my $lines_to_filter = $filter_newlines eq 'all' ? '' : '1';
$filter_buffer =~ s/^\n{1,$lines_to_filter}//;
$filter_buffer =~ s/\n{1,$lines_to_filter}$//;
}
$$top_buffer .= $filter_buffer;
<<<
If done this way, this could be done in addition to any filter the
component actually has. Or again, the filter could be used to
override the auto stripping entirely.
Thoughts?
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Mason-devel mailing list
https://lists.sourceforge.net/lists/listinfo/mason-devel
--
moo.
Dave Rolsky
2007-09-23 16:53:43 UTC
Permalink
Post by C.J. Adams-Collier
If I'm picking up what you're laying down, this would happen for each
comp() call. This would be too expensive. The filter would need to
be applied once, most likely at compile time.
It would be expensive, but I'm not sure it'd be _too_ expensive.

However, if the goal is to provide a feature similar to what TT provides
for controlling newlines/whitespace _around template directives_, then it
should be done at compile time only.

What Alex was describing would affect all the output, including the output
from interpolated variables/functions/etc. I think this would be too much
for most people.


-dave

/*===================================================
VegGuide.Org www.BookIRead.com
Your guide to all that's veg. My book blog
===================================================*/
Alex Robinson
2007-09-23 17:42:59 UTC
Permalink
Post by Dave Rolsky
It would be expensive, but I'm not sure it'd be _too_ expensive.
Well if it really was, what about having the default being 'never'
(with no overrides allowed) and using the lazy function definition
pattern (sorry, don't know what it's called in Perl) to return an
anonymous sub that redefines the relevant bit to match the
user-chosen default behaviour?

Though as it's just checking 2 hash values (directly rather than
through the api) and then doing a simple substitution, I would have
thought that paled into insignifance compared to the overhead of
actually calling the component in the first place.
Post by Dave Rolsky
However, if the goal is to provide a feature similar to what TT provides
for controlling newlines/whitespace _around template directives_, then it
should be done at compile time only.
OK. I'm just throwing it out there, you know.... Stirring what's on
the back burner.
Post by Dave Rolsky
What Alex was describing would affect all the output, including the output
from interpolated variables/functions/etc. I think this would be too much
for most people.
But it would only apply to component calls. How would it affect
interpolated variables? As for functions, I'm not clear as to what
you mean (my bad, not yours ;). My proposed solution, targets
components pure and simple (be they regular components, top level
components or def or method components). I don't see what else would
get affected. I'm not suggesting we patch $m->print ;)

Oh, I see what you mean. I think.

If, for absurd scenario, a component simply consisted of
<% $ARGS{output} %>
<<<

and $ARGS{output} purposefully contained additional newlines at start
and end, you'd be screwed.

Unless of course you set

<%flags>
filter_newlines => 'none
</%flags>
# or even 'one' in the more likely situation that it's in a def or method block

And hell, it only takes wrapping the newlines with any character at
all for it to resist destruction. Previous discussion have broken
down on the premise that output would not be very predictable,
figuring out which bits of the compiler to twiddle. The solution I've
sketched out seems to be utterly predictable. And easily
controllable. If that can be achieved in a Compiler-based solution,
then that's great :)

So, leaving aside the issue of Compiler vs Request, what about the
very concept of 'filter_newlines' (or better name) as a) Interp
parameter and b) per-component flag? Would those still be good to go
for a compiler-based version or does someone have a better way?




The other thing I forgot, is that something similar should probably
happen to $m->content as well. (In this case, Dave's point about
interpolated values does come home to roost, but again the flag of
choice could be set on the component with content to prevent overly
greedy stripping.)



By the way CJ, I haven't provided tests, because, well I wanted to
run this up the flagpole first and see whether it a) met with
approval and b) if so, what form it should take first.

Currently, I think it's falling down at a). *I* like it, but that's
because it works for me and I haven't yet seen a working
compiler-based version. But I'm not one of the core Mason dev team,
but rather a designer who codes and who wants to not have to think
about whether a given component needs a filter block to soak up
surplus space ;)
C.J. Adams-Collier
2007-09-23 18:44:12 UTC
Permalink
heh. sounds useful to me. it would be nice to have > 1 field for the
<%flag> block :)

can you cobble together a couple of real world use cases, determine
the "correct" name for the flag, write up some docs, put together some
.t bits and submit a udiff to the list?

You'd probably see some traction then.

oh, and no flag set means status quo
Post by Alex Robinson
Post by Dave Rolsky
It would be expensive, but I'm not sure it'd be _too_ expensive.
Well if it really was, what about having the default being 'never'
(with no overrides allowed) and using the lazy function definition
pattern (sorry, don't know what it's called in Perl) to return an
anonymous sub that redefines the relevant bit to match the
user-chosen default behaviour?
Though as it's just checking 2 hash values (directly rather than
through the api) and then doing a simple substitution, I would have
thought that paled into insignifance compared to the overhead of
actually calling the component in the first place.
Post by Dave Rolsky
However, if the goal is to provide a feature similar to what TT provides
for controlling newlines/whitespace _around template directives_, then it
should be done at compile time only.
OK. I'm just throwing it out there, you know.... Stirring what's on
the back burner.
Post by Dave Rolsky
What Alex was describing would affect all the output, including the output
from interpolated variables/functions/etc. I think this would be too much
for most people.
But it would only apply to component calls. How would it affect
interpolated variables? As for functions, I'm not clear as to what
you mean (my bad, not yours ;). My proposed solution, targets
components pure and simple (be they regular components, top level
components or def or method components). I don't see what else would
get affected. I'm not suggesting we patch $m->print ;)
Oh, I see what you mean. I think.
If, for absurd scenario, a component simply consisted of
<% $ARGS{output} %>
<<<
and $ARGS{output} purposefully contained additional newlines at start
and end, you'd be screwed.
Unless of course you set
<%flags>
filter_newlines => 'none
</%flags>
# or even 'one' in the more likely situation that it's in a def or method block
And hell, it only takes wrapping the newlines with any character at
all for it to resist destruction. Previous discussion have broken
down on the premise that output would not be very predictable,
figuring out which bits of the compiler to twiddle. The solution I've
sketched out seems to be utterly predictable. And easily
controllable. If that can be achieved in a Compiler-based solution,
then that's great :)
So, leaving aside the issue of Compiler vs Request, what about the
very concept of 'filter_newlines' (or better name) as a) Interp
parameter and b) per-component flag? Would those still be good to go
for a compiler-based version or does someone have a better way?
The other thing I forgot, is that something similar should probably
happen to $m->content as well. (In this case, Dave's point about
interpolated values does come home to roost, but again the flag of
choice could be set on the component with content to prevent overly
greedy stripping.)
By the way CJ, I haven't provided tests, because, well I wanted to
run this up the flagpole first and see whether it a) met with
approval and b) if so, what form it should take first.
Currently, I think it's falling down at a). *I* like it, but that's
because it works for me and I haven't yet seen a working
compiler-based version. But I'm not one of the core Mason dev team,
but rather a designer who codes and who wants to not have to think
about whether a given component needs a filter block to soak up
surplus space ;)
--
moo.
Loading...