Layouting rule rationale

10 views
Skip to first unread message

Eric Sunshine

unread,
Dec 16, 2009, 5:10:28 AM12/16/09
to na...@googlegroups.com
Hi Denis,

I am curious about the rationale in nanoc3 for allowing only a single
filter to be applied to a layout. Such a limitation seems both
artificial and arbitrary, especially when compared with content to which
any number of filters can be applied. True, some filters, such as Haml,
internally allow access to other filters, but this is not necessarily a
general property, and simpler filters might not be so accommodating. One
can imagine instances where it would be desirable to apply several of
these lesser filters to a layout.

-- ES

Denis Defreyne

unread,
Dec 16, 2009, 6:13:36 AM12/16/09
to na...@googlegroups.com
Hi,

I am not sure why you need multiple filters for a single layout. Since a layout is only used to put page content in a “frame” with headers/footers/sidebar etc, only a single filter can be used to do this. You can run filters after a item is laid out, though. You can mix and match #layout and #filter calls you see fit. For example:

compile '/whatever/*/' do
filter :erb
layout '/box/'
filter :markdown
layout '/article/'
filter :rubypants
end

Is this what you’re looking for?

Hope this helps,

Denis

--
Denis Defreyne
denis.d...@stoneship.org

Eric Sunshine

unread,
Dec 16, 2009, 7:44:10 AM12/16/09
to na...@googlegroups.com
Hi Denis,

On 12/16/2009 6:13 AM, Denis Defreyne wrote:
> On 16 Dec 2009, at 11:10, Eric Sunshine wrote:
>> I am curious about the rationale in nanoc3 for allowing only a single
>> filter to be applied to a layout.
> I am not sure why you need multiple filters for a single layout.
> Since a layout is only used to put page content in a �frame� with
> headers/footers/sidebar etc, only a single filter can be used to do
> this.

I don't have a definite need at this time, but was wondering about the
rationale since it always struck me as a rather arbitrary limitation,
especially considering the flexibility of the content 'compile'
capability. At a more concrete level, not every conceivable transformer
is or need be capable of invoking Ruby expressions (unlike ERB and
Ruby-based HAML). I can, for instance, imagine crafting a layout in some
higher level language and wanting it processed to HTML via one or more
simple transformers, followed finally by ERB at layout application time.

> You can run filters after a item is laid out, though. You can
> mix and match #layout and #filter calls you see fit. For example:
> compile '/whatever/*/' do
> filter :erb
> layout '/box/'
> filter :markdown
> layout '/article/'
> filter :rubypants
> end

A shortcoming of this approach, with regard to my above theoretical
argument, is that it tightly couples the layout processing specification
with the content processing rule. Stated differently, the content
'compile' rule normally does not need to know how the layout is
processed. Instead, layout processing information is correctly
encapsulated with the layout itself (via the 'layout' rule).

-- ES

Denis Defreyne

unread,
Dec 28, 2009, 9:29:05 AM12/28/09
to na...@googlegroups.com
On 16 Dec 2009, at 13:44, Eric Sunshine wrote:

> I don't have a definite need [for multiple filters] at this time, but was wondering about the rationale since it always struck me as a rather arbitrary limitation, especially considering the flexibility of the content 'compile' capability. At a more concrete level, not every conceivable transformer is or need be capable of invoking Ruby expressions (unlike ERB and Ruby-based HAML). I can, for instance, imagine crafting a layout in some higher level language and wanting it processed to HTML via one or more simple transformers, followed finally by ERB at layout application time.
>
>> You can run filters after a item is laid out, though. You can mix and match #layout and #filter calls you see fit. For example: [..]


>
> A shortcoming of this approach, with regard to my above theoretical argument, is that it tightly couples the layout processing specification with the content processing rule. Stated differently, the content 'compile' rule normally does not need to know how the layout is processed. Instead, layout processing information is correctly encapsulated with the layout itself (via the 'layout' rule).

Hi,

The reason why layouts work the way they work now is mostly because of simplicity. Being able to specify other filters and even sub-layouts in a layouting rule may be useful for large and complex sites, but I believe that for most sites, it would not be useful and perhaps even annoying to handle layouts this way (depending on how it’s implemented and how the DSL looks).

I have never encountered a use case where being able to specify extra filters and sub-layouts in a layouting rule would be a great advantage. Can you share your use-cases for this feature? If this would turn out to be useful, it may indeed be worth adding to a future version of nanoc.

Regards,

Eric Sunshine

unread,
Dec 31, 2009, 8:31:30 AM12/31/09
to na...@googlegroups.com
Hi Denis,

On 12/28/2009 9:29 AM, Denis Defreyne wrote:
> The reason why layouts work the way they work now is mostly because
> of simplicity. Being able to specify other filters and even
> sub-layouts in a layouting rule may be useful for large and complex
> sites, but I believe that for most sites, it would not be useful and
> perhaps even annoying to handle layouts this way (depending on how

> it�s implemented and how the DSL looks).

I would expect that the DSL for a more flexible 'layout' would take its
cue from the existing 'compile' rule. People already understand how a
'compile' rule works, so a similarly-working 'layout' rule would be a
natural fit. Continued support of the present 'layout' rule in
conjunction with the expanded notion is possible: block_given? can
easily distinguish between the two.

> I have never encountered a use case where being able to specify extra
> filters and sub-layouts in a layouting rule would be a great
> advantage. Can you share your use-cases for this feature? If this
> would turn out to be useful, it may indeed be worth adding to a
> future version of nanoc.

I should reiterate that the question was posed out of curiosity, due to
the inconsistency between 'compile' and 'layout', rather than because of
any immediate need. Other features already planned for 3.1.x, such as
generic (binary) asset handling, are far more important than this
(presently) theoretical issue.

Regarding use-cases: Filters allow us to compose resources, including
layouts, at a higher-level than raw HTML or whatever other output format
is needed. Not all filters, however, will be powerful enough to support
the embedded Ruby evaluation needed, at the very least, for the 'yield'
which merges the item content into the layout.

As an example, in the site I am converting, I created several partial
layouts. In some cases, creation and maintenance of these layouts would
be simplified if composed at a level higher than raw HTML or Haml, such
as with Markdown. No Markdown processor, however, supports embedded
Ruby. Consequently, a layout composed in Markdown would first need to be
filtered by a Markdown processor and finally by ERB (to 'yield' the
content).

As another example, I created a simple interpolation filter similar to
the following:

class Interpolator < Nanoc3::Filter
identifiers :interpolator
def run(content, params={})
bindings = params.stringify_keys
content.gsub(/%(?:(\w+)|{(\w+)})/) {|s| bindings[$1||$2] || s}
end
end

This provides basic variable substitution without the syntactic noise
and intrusiveness of ERB. For resources containing many such variables,
the difference in readability and maintainability between the terse
"%foo" and the verbose ERB "<%= foo %>" can be dramatic. Presently, I
employ this filter for some (non-HTML) content, but it is not difficult
to imagine using its use in a layout (HTML or not) for interpolation of
global configuration, or perhaps even item properties. This filter is
not powerful enough to handle embedded Ruby, so for a layout it would
require a pipeline of Interpolator followed by ERB to 'yield' the item
content.

It is true, of course, that such use-cases can be handled by other
mechanisms. Given the above example, an ugly approach would be to employ
Nanoc3::Helpers::Filtering#filter() within the layout itself to invoke
Interpolator upon its own content, though this seems very much a hack.

Alternately, you mentioned that it could be the responsibility of the
'compile' rule to invoke Interpolator on behalf of layouts which require
it. I noted, however, that doing so would incorrectly create a tight
coupling between the compile rule and the layout. Presently, a compile
rule does not need to know how a layout is processed. That information
is correctly encapsulated in the layout rule. Whether a layout is
HTML+ERB or Haml is immaterial to the compile rule which invokes it.
With the above theoretical example, in which a layout may be processed
by multiple filters, such knowledge should remain private to the layout.
It should not be the responsibility of the compile rule to apply the
necessary Interpolator + ERB pipeline.

-- ES

Reply all
Reply to author
Forward
0 new messages