Discussion:
[Freemarker-devel] Per-Template-configuration feature in FM 2.3.24
Daniel Dekany
2015-08-08 15:29:39 UTC
Permalink
Any insight with the below new feature? It's implemented and committed
BTW (2.3-gae), but can be seen as a proof-of-the-concept...


Something FM was missing is a way of specifying different
Configuration settings for different templates. You have a common
Configuration, and all templates that you get from that via
getTemplate(...) has the same configuration settings, inherited from
the Configuration (apart from locale that you can specify to
getTemplate(...)). While Template always had setter methods to
override the settings, with Configurable.getTemplate(...) you have no
chance to call them. (You could call them after getTemplate, but that
would not thread safe, etc.)

Planned features, such as true auto-escaping will need such
functionality. Like you might want *.ftlh to be automatically HTML
escaped, but not for *.ftl. (That's something I ran into recently,
though I have used Velocity there...)

So I came up with TemplateConfigurer, which is an object on which you
can set settings (from freemarker.core.Configurable), which it can set
in Template like myTemplateCfger.configure(template). A
TemplateConfigurer can also hold settings that influence parsing (like
tag_syntax, etc., from freemarker.core.ParserConfiguration, which is
also new in 2.3.24), and can even specify the charset to used for
loading the template (if you ever had a mix of UTF-8 and
ISO-8859-<x>/CP<x> templates...).

Then, I have added a new Configuration setting, called
"templateConfigurers". It's type is
freemarker.cache.TemplateConfigurerFactory, which is basically a
collection of TemplateConfigurer-s, plus rules that tell that
depending on the name of the template (the "source name" that the
TemplateLoader uses), when to apply which. Using the Properties-based
configuration syntax of FreeMarker for brevity (the Java config would
be the same, but with bunch of `new`-s and setXxx-s), it's something
like this:

MergingTemplateConfigurerFactory(
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.de.*'),
TemplateConfigurer(timeZone=TimeZone('GMT+01'), locale=Locale('de', 'DE'))),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.fr.*'),
TemplateConfigurer(timeZone=TimeZone('GMT'), locale=Locale('fr', 'FR'))),
allowNoMatch=true
),
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlh'),
TemplateConfigurer(outputFormat='HTML')),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlx'),
TemplateConfigurer(outputFormat='XML')),
noMatchErrorDetails='Unrecognized template file extension'
),
ConditionalTemplateConfigurerFactory(
PathGlobMatcher('stat/**')
TemplateConfigurer(timeZone=TimeZone('UTC'))
)
)

The "outputFormat" setting above is hypothetical, we don't yet have
it.

So if you set this as the "templateConfigurers", and you get a
template called "foo.de.ftlh", then that Template will have locale
de_DE, time zone GMT+01 and outoutFormat (again, hypothetical) "HTML".
For "stats/foo.de.ftlh" the time zone will be UTC instead (because
when merging, the last setting value assignment wins).

So that's it. Opinions, ideas?
--
Best regards,
Daniel Dekany


------------------------------------------------------------------------------
Woonsan Ko
2015-08-10 12:35:04 UTC
Permalink
Is it about a setting overriding option in "Layer 2: Template" level" [1]?
Regarding the "hypothetical" HTML or XML format configuration option, is
it similar to automatic addition of escape directive [2]?

If that can be done easily in programmatic way as well as configuration
way, then it seems good to me.

Regards,

Woonsan

[1] http://freemarker.org/docs/pgui_config_settings.html
[2] http://freemarker.org/docs/ref_directive_escape.html
Post by Daniel Dekany
Any insight with the below new feature? It's implemented and committed
BTW (2.3-gae), but can be seen as a proof-of-the-concept...
Something FM was missing is a way of specifying different
Configuration settings for different templates. You have a common
Configuration, and all templates that you get from that via
getTemplate(...) has the same configuration settings, inherited from
the Configuration (apart from locale that you can specify to
getTemplate(...)). While Template always had setter methods to
override the settings, with Configurable.getTemplate(...) you have no
chance to call them. (You could call them after getTemplate, but that
would not thread safe, etc.)
Planned features, such as true auto-escaping will need such
functionality. Like you might want *.ftlh to be automatically HTML
escaped, but not for *.ftl. (That's something I ran into recently,
though I have used Velocity there...)
So I came up with TemplateConfigurer, which is an object on which you
can set settings (from freemarker.core.Configurable), which it can set
in Template like myTemplateCfger.configure(template). A
TemplateConfigurer can also hold settings that influence parsing (like
tag_syntax, etc., from freemarker.core.ParserConfiguration, which is
also new in 2.3.24), and can even specify the charset to used for
loading the template (if you ever had a mix of UTF-8 and
ISO-8859-<x>/CP<x> templates...).
Then, I have added a new Configuration setting, called
"templateConfigurers". It's type is
freemarker.cache.TemplateConfigurerFactory, which is basically a
collection of TemplateConfigurer-s, plus rules that tell that
depending on the name of the template (the "source name" that the
TemplateLoader uses), when to apply which. Using the Properties-based
configuration syntax of FreeMarker for brevity (the Java config would
be the same, but with bunch of `new`-s and setXxx-s), it's something
MergingTemplateConfigurerFactory(
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.de.*'),
TemplateConfigurer(timeZone=TimeZone('GMT+01'), locale=Locale('de', 'DE'))),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.fr.*'),
TemplateConfigurer(timeZone=TimeZone('GMT'), locale=Locale('fr', 'FR'))),
allowNoMatch=true
),
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlh'),
TemplateConfigurer(outputFormat='HTML')),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlx'),
TemplateConfigurer(outputFormat='XML')),
noMatchErrorDetails='Unrecognized template file extension'
),
ConditionalTemplateConfigurerFactory(
PathGlobMatcher('stat/**')
TemplateConfigurer(timeZone=TimeZone('UTC'))
)
)
The "outputFormat" setting above is hypothetical, we don't yet have
it.
So if you set this as the "templateConfigurers", and you get a
template called "foo.de.ftlh", then that Template will have locale
de_DE, time zone GMT+01 and outoutFormat (again, hypothetical) "HTML".
For "stats/foo.de.ftlh" the time zone will be UTC instead (because
when merging, the last setting value assignment wins).
So that's it. Opinions, ideas?
--
***@onehippo.com www.onehippo.com
Boston - 745 Atlantic Ave, 8th Floor, Boston MA 02111
Amsterdam - Oosteinde 11, 1017 WT Amsterdam
US +1 877 414 4776 (toll free)
Europe +31(0)20 522 4466

------------------------------------------------------------------------------
Daniel Dekany
2015-08-10 19:02:35 UTC
Permalink
Post by Woonsan Ko
Is it about a setting overriding option in "Layer 2: Template" level" [1]?
Exactly.
Post by Woonsan Ko
Regarding the "hypothetical" HTML or XML format configuration option, is
it similar to automatic addition of escape directive [2]?
As far as most users are concerned, yes. It will work very differently
though, if it will work as I have planned but that's another subject.
(A noticeable effect will be that #noescape will give an error,
instead you can use ${foo?noEsc}, which is generally more convenient.
Though I also plant to add block directives like
<#autoEsc false>...</#autoEsc>. The point is, that #escape/#noescape
with expression rewriting, while the new feature meant to use a more
specialized approach. So #escape/#noescape will be probably banned if
the new auto-escaping is on, to prevent confusion.)

I also intend to introduce a few new "standard" file extensions (in
additionally to "ftl"), which are: "ftlh" which sets the output format
to HTML, "ftlx" which sets it to "XML", maybe "ftlxh" for "XHTML" (I
will check if we still need that with today's browsers), and "ftlr"
for "RTF" (because, we traditionally support that...). So if someone
sets incompatibleImprovemetns to at least "2.3.24", these will just
work. You will also be able to register your own output formats and
associate them with file extension (or whatever name pattern) with
cfg.setTemplateConfigurer(...).

It's also possible to write <#ftl outputFormat="HTML">, to specify the
output format right in the template.
Post by Woonsan Ko
If that can be done easily in programmatic way as well as configuration
way, then it seems good to me.
For the lowest level of control, you can pass a ParserConfiguration to
the Template constructor, and in the ParserConfiguration you can set
the outputFormat and autoEscaping properties. Of course most users
never instantiate Templates in their own code
(Configuration.getTemplate does that for them), and that's when
cfg.setTemplateConfigurers comes into play.

Even if you are relying on cfg.setTemplateConfigurers, you can
implement your own TemplateConfigurerFactory that can decide what
TemplateConfigurer, and hence, what ParserConfiguration to use.
Post by Woonsan Ko
Regards,
Woonsan
[1] http://freemarker.org/docs/pgui_config_settings.html
[2] http://freemarker.org/docs/ref_directive_escape.html
Post by Daniel Dekany
Any insight with the below new feature? It's implemented and committed
BTW (2.3-gae), but can be seen as a proof-of-the-concept...
Something FM was missing is a way of specifying different
Configuration settings for different templates. You have a common
Configuration, and all templates that you get from that via
getTemplate(...) has the same configuration settings, inherited from
the Configuration (apart from locale that you can specify to
getTemplate(...)). While Template always had setter methods to
override the settings, with Configurable.getTemplate(...) you have no
chance to call them. (You could call them after getTemplate, but that
would not thread safe, etc.)
Planned features, such as true auto-escaping will need such
functionality. Like you might want *.ftlh to be automatically HTML
escaped, but not for *.ftl. (That's something I ran into recently,
though I have used Velocity there...)
So I came up with TemplateConfigurer, which is an object on which you
can set settings (from freemarker.core.Configurable), which it can set
in Template like myTemplateCfger.configure(template). A
TemplateConfigurer can also hold settings that influence parsing (like
tag_syntax, etc., from freemarker.core.ParserConfiguration, which is
also new in 2.3.24), and can even specify the charset to used for
loading the template (if you ever had a mix of UTF-8 and
ISO-8859-<x>/CP<x> templates...).
Then, I have added a new Configuration setting, called
"templateConfigurers". It's type is
freemarker.cache.TemplateConfigurerFactory, which is basically a
collection of TemplateConfigurer-s, plus rules that tell that
depending on the name of the template (the "source name" that the
TemplateLoader uses), when to apply which. Using the Properties-based
configuration syntax of FreeMarker for brevity (the Java config would
be the same, but with bunch of `new`-s and setXxx-s), it's something
MergingTemplateConfigurerFactory(
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.de.*'),
TemplateConfigurer(timeZone=TimeZone('GMT+01'), locale=Locale('de', 'DE'))),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.fr.*'),
TemplateConfigurer(timeZone=TimeZone('GMT'), locale=Locale('fr', 'FR'))),
allowNoMatch=true
),
FirstMatchTemplateConfigurerFactory(
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlh'),
TemplateConfigurer(outputFormat='HTML')),
ConditionalTemplateConfigurerFactory(
FileNameGlobMatcher('*.ftlx'),
TemplateConfigurer(outputFormat='XML')),
noMatchErrorDetails='Unrecognized template file extension'
),
ConditionalTemplateConfigurerFactory(
PathGlobMatcher('stat/**')
TemplateConfigurer(timeZone=TimeZone('UTC'))
)
)
The "outputFormat" setting above is hypothetical, we don't yet have
it.
So if you set this as the "templateConfigurers", and you get a
template called "foo.de.ftlh", then that Template will have locale
de_DE, time zone GMT+01 and outoutFormat (again, hypothetical) "HTML".
For "stats/foo.de.ftlh" the time zone will be UTC instead (because
when merging, the last setting value assignment wins).
So that's it. Opinions, ideas?
--
Thanks,
Daniel Dekany


------------------------------------------------------------------------------
Loading...