Hi,

I must say it's much easier for me to understand Jacopo's solution.

But I'm currently not a Freemarker heavy user, so maybe for those users an 
integrated syntax is better.

When I think about it, I had once to use Freemarker heavily for a small CMS creation. And then indeed I think having .get_optional_template would have been a plus. Because it's not a workaround, it's more flexible and allows more.

To be frank I was not aware of the special variables and the syntax is not 
easy, but all in all I think it's worth it.

Jacques


Le 16/02/2018 à 08:04, Daniel Dekany a écrit :
Some more opinions guys? Especially as we got one opinion against the
feature.


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 <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