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.

Jacopo

On Sun, Feb 11, 2018 at 10:02 PM, Daniel Dekany <ddek...@apache.org> wrote:

> See the RFE here:
> https://issues.apache.org/jira/browse/FREEMARKER-84
>
> 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
> https://freemarker.apache.org/docs/ref_specvar.html) 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
> https://freemarker.apache.org/docs/ref_directive_include.
> 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
>
>

Reply via email to