freemarker-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Dekany <>
Subject Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates
Date Fri, 16 Feb 2018 07:04:40 GMT
Some more opinions guys? Especially as we got one opinion against the

Tuesday, February 13, 2018, 9:59:41 AM, Daniel Dekany wrote:

> Tuesday, February 13, 2018, 9:28:18 AM, Jacopo Cappellato wrote:
>> For less common use cases like this my preference is to defer the
>> implementation to the template developer rather than adding complexity to
>> the language.
>> If I understand the use case that originated this request, something
>> similar could be achieved with a simple trick like the following:
>> 1) the calling code would be:
>> <#include "possibly-missing-template.ftl" ignore_missing=true>
>> <#if !processed??>
>>         The template was not found or processed!
>> </#if>
>> 2) somewhere in possibly-missing-template.ftl (i.e. at the bottom of it) we
>> add an assign directive like:
>> <#assign processed=true>
>> There are some cons to this approach (the most relevant one is that the
>> referenced template has to contain the #assign directive) but FM users
>> could live with this approach and in the meantime we could try to get their
>> feedback to better understand how much this requirement is desired to
>> justify a change to the codebase.
> The need for optional includes is something that was brought up for
> several times during the years. It's mostly needed for some custom
> fallback logic as far as I can tell. (While there's #include
> ignore_missing=true for a while, it doesn't let you to take some extra
> action depending on if the template is missing.)
> As of it's important enough to add a new feature of this weight (which
> low, as it's just yet another special variable, no new directive or
> syntax): It's a template language, and in that context
> including/importing other templates is probably an important enough
> topic to warrant some extras.
>> Jacopo
>> On Sun, Feb 11, 2018 at 10:02 PM, Daniel Dekany <> wrote:
>>> See the RFE here:
>>> As you see, the first consensus was introducing `.last_include_found`,
>>> but it has two problems:
>>> * Sometimes it happens that if, and only if the template exists then
>>>   you want to do (usually print) something *before* it. Problem is, by
>>>   the time you know that from `.last_include_found`, it's too late, as
>>>   the template was already processed.
>>> * Like many global state variables in general, this can lead to some
>>>   confusing edge cases and hard-to-follow code. Like, if `#include`
>>>   throws an exception, which is then handled by the user with
>>>   `#attempt`/`#recover`, then `.last_include_found` may or may not be
>>>   updated, as perhaps we haven't yet reached the point where it can be
>>>   told if the template exists. (Consider an expression evaluation
>>>   error in the `#include` parameter, or an I/O error due to which we
>>>   can't access the template directory). Also there are some public
>>>   `include` methods in the `Environment` API, but they can't set this
>>>   variable, as they return a `Template`, and the variable must be set
>>>   after the `Template` was processed, unless the template was missing.
>>>   (If you can't figure out why it must be done that way, that proves
>>>   again how tricky this is... think about includes inside includes.)
>>> So, I propose the solution below. Maybe somewhat difficult to grasp
>>> first, but it meant to be used rarely, and mostly by "experts"...
>>> Let's hope SO and examples in the Manual will help people though. (-;
>>> Introduce a new special variable (see
>>> called
>>> "get_optional_template", which is a TemplateMethodModelEx with these
>>> parameters:
>>> 1. template name (maybe a relative path, resolved as #include/#import
>>> does it) 2. A hash that can have the following keys: "parse",
>>> "encoding" (similarly to
>>> html#ref.directive.include).
>>> Example method call (the `.` prefix is the special variable reference
>>> syntax):
>>>   <#assign t = .get_optional_template("foo.ftl", { 'encoding': 'utf-8' })>
>>> The method returns a hash (`t` above) that contains the following keys:
>>> - "include": directive (or missing); `<@t.include />` has similar
>>>   effect as `<#include "foo.ftl">`
>>> - "import": method (or missing); returns a namespace. `<#assign f =
>>>   t.import()>` has similar effect as `<#import 'foo.ftl' as f>`
>>> - "exists": boolean; returns if the template was found.
>>> The method call loads the target template eagerly, i.e., it won't wait
>>> until `t.include`, `t.exist` etc. is actually used.
>>> Note that the hash is returned even if the template wasn't found (but
>>> then it won't contain "include" and "import", and "exists" will be
>>> `false`). If some other error occurs, like an I/O error other than a
>>> "template not found", or the template has invalid syntax, it will
>>> throw exception (just like #include).
>>> Use cases:
>>> - `#include` with fallback templates or fallback macro (note how we
>>>   can use the `exp!defaultExp` operator):
>>>   <@.get_optional_template('foo.ftl')
>>>       !.get_optional_template('bar.ftl').include
>>>       !defaultMacro  />
>>> - Doing something before `#include` if the template exists:
>>>     <#assign t = .get_optional_template('foo.ftl')>
>>>     <#if t.exists>
>>>       Do before existing template
>>>       <@t.include />
>>>     </#if>
>>> Opinions?
>>> --
>>> Thanks,
>>>  Daniel Dekany

 Daniel Dekany

View raw message