You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@freemarker.apache.org by Daniel Dekany <dd...@apache.org> on 2018/02/11 21:02:30 UTC

Proposal for FREEMARKER-84: More flexible handlig of missing templates

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


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
Over .get_optional_template(name[, options]),
(https://freemarker.apache.org/builds/fm2.3.28/ref_specvar.html#ref_specvar_get_optional_template)
I have also added:

- .caller_template_name (https://issues.apache.org/jira/browse/FREEMARKER-83)
  https://freemarker.apache.org/builds/fm2.3.28/ref_specvar.html#ref_specvar_caller_template_name

- ?absolute_template_name (you will soon see the connection...)
  https://freemarker.apache.org/builds/fm2.3.28/ref_builtins_expert.html#ref_builtin_absolute_template_name

In
https://freemarker.apache.org/builds/fm2.3.28/ref_builtins_expert.html#ref_builtin_absolute_template_name
there's an example where all three are used together:

  <#--
    <@smileyInclude name /> behaves like <#include name>, but prints a "(:" before the
    template, or prints "):" instead if the template is missing.

    Note that just like with #include, if name is relative, it's resolved based on the
    directory of the caller template, not of the template that defines this macro. As
    .get_optional_template resolves relative names based on the current template, we
    had to convert the name to an absolute name based on the caller template before
    passing it to it.
  -->
  <#macro smileyInclude name>
    <#local t = .get_optional_template(
        name?absolute_template_name(.caller_template_name))>
    <#if t.exists>
      (:
      <@t.include />
    <#else>
      ):
    </#if>
  </#macro>

Any opinions?


Wednesday, February 28, 2018, 8:57:06 PM, Daniel Dekany wrote:

> .get_optional_template(name[, options]) now implemented in the 2.3-gae
> and 2.3 branches. Testing/feedback is welcome!
>
> See commit:
> https://github.com/apache/incubator-freemarker/commit/51c2476621809d8f4183f23e894be0106cabe810
>
> You can find some examples in the tests:
> https://github.com/apache/incubator-freemarker/blob/2.3-gae/src/test/java/freemarker/core/GetOptionalTemplateTest.java
>
>
> Sunday, February 11, 2018, 10:02:30 PM, Daniel Dekany 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


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
.get_optional_template(name[, options]) now implemented in the 2.3-gae
and 2.3 branches. Testing/feedback is welcome!

See commit:
https://github.com/apache/incubator-freemarker/commit/51c2476621809d8f4183f23e894be0106cabe810

You can find some examples in the tests:
https://github.com/apache/incubator-freemarker/blob/2.3-gae/src/test/java/freemarker/core/GetOptionalTemplateTest.java


Sunday, February 11, 2018, 10:02:30 PM, Daniel Dekany 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


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
Monday, February 12, 2018, 12:52:26 AM, jprice@seznam.cz wrote:

> Opinion:
>
> It seems reasonable. As you suggested, it also seems a little advanced in
> style for most cases.
> It's very flexible, a bit verbose for common use, a bit of a learning curve
> for template author.
>
> I would prefer a shorter name, like ".get_template". Isn't it up to the
> template author to decide if the template is optional and what to do if it's
> not found?

If it's not optional, then using this new method is (almost) useless,
as you can just use #include or #import. Also note that we have a
problem with handling a returned null, so I went for something that's
more like Optional<Template> in Java 8, that is, always returning a
non-object that has a property to check if it represents something or
nothing.

> It seems that, from an author's perspective, all we're doing here
> is deciding whether to throw an exception when the template is not found (or
> include nothing if ignore_missing is set) or to let the author handle the
> situation his own way.

Yes.

> For me it might eliminate the need to provide Java support for default
> templates, something that is on my list to build. So it's a useful addition,
> if not super pretty.
>
>
> Other thoughts...
>
> The first thing that came to mind to ask whether it's practical to also use
> existing freemarker idiom. <#list> has similar use scenarios related to
> present, missing, alternate output.
>
> Sketchy conceptual example...
>
> <#include path,options>
>   Stuff you want to output only if include is successful.
>   <#included>
>   Maybe more stuff to output for successful includes. 
> <#else>
>   Stuff you want to do if include failed, for whatever reason.
> </#include>
>
> I guess a problem with this is that <#include> is currently expected to be a
> self closing element with no content, and since ftl does not appear to use
> or require self closing elements like <#include/> it would be a pain to
> interpret whether <#include> has content or not.

Exactly. It's also not very good for the user (and for tooling).

> Forcing it to have content or include a closing tag would be a big
> breaking change. Is that the reasoning you would apply to this
> question?

Not only that. The problem is with the case where you need to do
something before the inclusion if the template exists. For that, above
you had to introduce a new directive, #included, which is pretty a
"heavy" solution for an otherwise quite language-agonistic problem. I
mean, in any usual programming language you do something like
`Template tOrNull = cfg.getTemplate(blah, allowMissing=true)`, then if
the result is non-null, do the include/import with some API using that
earlier returned Template. Pretty basic thing, no special statement
was needed. The solution proposed by me does the same, except that in
FTL we have to:

- Fight the inability of assigning null to a target variable (hence
  it's a hash). (FM3: This will be solved there.)

- Fight the inability to call advanced FreeMarker API-s on a Template
  object from a template (which is fine), but hence it's a hash again,
  so it can contain those "utilities".

- Face how strange the special-variable syntax is (initial dot). BTW,
  we have the same problem with
  https://issues.apache.org/jira/browse/FREEMARKER-86 (FM3: This
  syntax has to go, especially as it also leads to ambiguities, as in
  the case `maybeNull!.someProp`.)

> Is Freemarker's approach to self-closing elements like <#include> likely to
> change in Freemarker 3?

There's no such plan for the default (HTML-ish) syntax at least.

> I didn't see anything about that in a quick review of the web page
> on FM3. Maybe you don't even think of these directives as elements.
> For a user familiar with HTML or XML it's hard to think of them
> otherwise.

Yes, that was the intention. I never liked that though. But I intend
to remain true to the idea of the original authors anyway. However, I
will want to play with an alternative syntax, one that doesn't pretend
to be similar to HTML while it's actually not similar at all.
Something that a feels bit like WebMacro syntax.

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by jp...@seznam.cz.
Opinion:

It seems reasonable. As you suggested, it also seems a little advanced in 
style for most cases.
It's very flexible, a bit verbose for common use, a bit of a learning curve 
for template author.

I would prefer a shorter name, like ".get_template". Isn't it up to the 
template author to decide if the template is optional and what to do if it's
not found? It seems that, from an author's perspective, all we're doing here
is deciding whether to throw an exception when the template is not found (or
include nothing if ignore_missing is set) or to let the author handle the 
situation his own way.

For me it might eliminate the need to provide Java support for default 
templates, something that is on my list to build. So it's a useful addition,
if not super pretty.


Other thoughts...

The first thing that came to mind to ask whether it's practical to also use 
existing freemarker idiom. <#list> has similar use scenarios related to 
present, missing, alternate output.

Sketchy conceptual example...

<#include path,options>
  Stuff you want to output only if include is successful.
  <#included>
  Maybe more stuff to output for successful includes. 
<#else>
  Stuff you want to do if include failed, for whatever reason.
</#include>

I guess a problem with this is that <#include> is currently expected to be a
self closing element with no content, and since ftl does not appear to use 
or require self closing elements like <#include/> it would be a pain to 
interpret whether <#include> has content or not. Forcing it to have content 
or include a closing tag would be a big breaking change. Is that the 
reasoning you would apply to this question?

Is Freemarker's approach to self-closing elements like <#include> likely to 
change in Freemarker 3? I didn't see anything about that in a quick review 
of the web page on FM3. Maybe you don't even think of these directives as 
elements. For a user familiar with HTML or XML it's hard to think of them 
otherwise.



Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
Saturday, February 17, 2018, 11:56:43 AM, Jacques Le Roux wrote:

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

That's not really a solution though, rather a work around that works
in some cases, but not in others. Jacopo basically says, as far as I
see, that we don't have to make FreeMarker even bigger only to address
such a rare use case.

> 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 <dd...@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
>>>>>
>>>>>
>
>

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Jacques Le Roux <ja...@les7arts.com>.
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 <dd...@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
>>>>
>>>>


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Taher Alkhateeb <sl...@gmail.com>.
I feel clumsy now :) Thank you for the explanation, I'm beginning to
understand this a bit. I'll follow up on the JIRA

On Sat, Feb 17, 2018 at 7:57 PM, Daniel Dekany <dd...@apache.org> wrote:
> Saturday, February 17, 2018, 2:36:37 PM, Taher Alkhateeb wrote:
>
>> Ahh I see, I guess that's the layer of complexity Jacopo was pointing
>> to (fall back mechanism). Every time I engage here I learn something
>> :) In this case I stand neutral. it sounds a bit challenging but I
>> have zero knowledge of the code base and perhaps the architecture
>> allows for that.
>
> It allows that. It's already possible to do with the public
> Environment Java API. It's just that then you had to implement a
> custom TemplateDirectiveModel and all... and it might not be so easy
> to do (or to do correctly) for an average user.
>
> Anyway, an important selling point of FreeMarker is that you generally
> have to run less circles to make developers add this-and-that tool,
> also to find the typically lacking documentation of in-house
> directives, because lot of the stuff is there out-of-the-box and is
> documented in the Manual. (Except that people enjoy using ancient
> FreeMarker versions... like 2.3.19(!) and 2.3.23 are big favorites.)
>
>> I find this over all intriguing and interesting, if you intend to work
>> on this feature would you mind sharing the JIRA number so I can follow
>> on the code changes?
>
> It's in the thread Subject... :)
> https://issues.apache.org/jira/browse/FREEMARKER-84
>
>
>> On Sat, Feb 17, 2018 at 4:06 PM, Daniel Dekany <dd...@apache.org> wrote:
>>> Saturday, February 17, 2018, 9:36:48 AM, Taher Alkhateeb wrote:
>>>
>>>> Wrong link, sorry, correcting.
>>>>
>>>> http://jinja.pocoo.org/docs/2.10/templates/#include
>>>
>>> Just be sure it's clear, we also have an ignore_missing option (in the
>>> released versions). But it's often not very useful if you can't do
>>> some action in case the template is missing.
>>>
>>> Jinja also allows you to specify a list of template names instead of
>>> just one, and uses that as a fallback list. That's something I have
>>> considered as well, and I believe it covers most use-cases for the
>>> proposed feature. However, it falls into the same mistake as
>>> ingnore_missing, as it focuses on a concrete use-case while missing
>>> another (when you have to do something before the template if it
>>> exists). I prefer less but more generic devices. With that could have
>>> avoid adding ignore_missing as well.
>>>
>>>> On Feb 17, 2018 11:34 AM, "Taher Alkhateeb" <sl...@gmail.com>
>>>> wrote:
>>>>
>>>>> For a point of comparison, the python jinja2 template engine (widely used)
>>>>> has an "include" directive that has an attribute named "ignore missing"
>>>>> [1]. I also remember seeing similar behavior in other engines.
>>>>>
>>>>> So I guess perhaps from a usability point of view this seems to be a
>>>>> desired feature by users. However, I don't know what the impact in terms of
>>>>> complexity would reflect in the code base.
>>>>>
>>>>> http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions
>>>>>
>>>>> On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
>>>>> wrote:
>>>>>
>>>>>> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org>
>>>>>> wrote:
>>>>>>
>>>>>> > Some more opinions guys? Especially as we got one opinion against the
>>>>>> > feature.
>>>>>> >
>>>>>>
>>>>>> Just to clarify my opinion: I am not against this feature; I simply don't
>>>>>> consider it a must since there are some workaround to get a similar
>>>>>> behavior. But if it will be implemented I will be happy too.
>>>>>>
>>>>>> Jacopo
>>>>>>
>>>>>
>>>
>>> --
>>> Thanks,
>>>  Daniel Dekany
>>>
>>
>
> --
> Thanks,
>  Daniel Dekany
>

Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
Saturday, February 17, 2018, 2:36:37 PM, Taher Alkhateeb wrote:

> Ahh I see, I guess that's the layer of complexity Jacopo was pointing
> to (fall back mechanism). Every time I engage here I learn something
> :) In this case I stand neutral. it sounds a bit challenging but I
> have zero knowledge of the code base and perhaps the architecture
> allows for that.

It allows that. It's already possible to do with the public
Environment Java API. It's just that then you had to implement a
custom TemplateDirectiveModel and all... and it might not be so easy
to do (or to do correctly) for an average user.

Anyway, an important selling point of FreeMarker is that you generally
have to run less circles to make developers add this-and-that tool,
also to find the typically lacking documentation of in-house
directives, because lot of the stuff is there out-of-the-box and is
documented in the Manual. (Except that people enjoy using ancient
FreeMarker versions... like 2.3.19(!) and 2.3.23 are big favorites.)

> I find this over all intriguing and interesting, if you intend to work
> on this feature would you mind sharing the JIRA number so I can follow
> on the code changes?

It's in the thread Subject... :)
https://issues.apache.org/jira/browse/FREEMARKER-84


> On Sat, Feb 17, 2018 at 4:06 PM, Daniel Dekany <dd...@apache.org> wrote:
>> Saturday, February 17, 2018, 9:36:48 AM, Taher Alkhateeb wrote:
>>
>>> Wrong link, sorry, correcting.
>>>
>>> http://jinja.pocoo.org/docs/2.10/templates/#include
>>
>> Just be sure it's clear, we also have an ignore_missing option (in the
>> released versions). But it's often not very useful if you can't do
>> some action in case the template is missing.
>>
>> Jinja also allows you to specify a list of template names instead of
>> just one, and uses that as a fallback list. That's something I have
>> considered as well, and I believe it covers most use-cases for the
>> proposed feature. However, it falls into the same mistake as
>> ingnore_missing, as it focuses on a concrete use-case while missing
>> another (when you have to do something before the template if it
>> exists). I prefer less but more generic devices. With that could have
>> avoid adding ignore_missing as well.
>>
>>> On Feb 17, 2018 11:34 AM, "Taher Alkhateeb" <sl...@gmail.com>
>>> wrote:
>>>
>>>> For a point of comparison, the python jinja2 template engine (widely used)
>>>> has an "include" directive that has an attribute named "ignore missing"
>>>> [1]. I also remember seeing similar behavior in other engines.
>>>>
>>>> So I guess perhaps from a usability point of view this seems to be a
>>>> desired feature by users. However, I don't know what the impact in terms of
>>>> complexity would reflect in the code base.
>>>>
>>>> http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions
>>>>
>>>> On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
>>>> wrote:
>>>>
>>>>> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org>
>>>>> wrote:
>>>>>
>>>>> > Some more opinions guys? Especially as we got one opinion against the
>>>>> > feature.
>>>>> >
>>>>>
>>>>> Just to clarify my opinion: I am not against this feature; I simply don't
>>>>> consider it a must since there are some workaround to get a similar
>>>>> behavior. But if it will be implemented I will be happy too.
>>>>>
>>>>> Jacopo
>>>>>
>>>>
>>
>> --
>> Thanks,
>>  Daniel Dekany
>>
>

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Taher Alkhateeb <sl...@gmail.com>.
Ahh I see, I guess that's the layer of complexity Jacopo was pointing
to (fall back mechanism). Every time I engage here I learn something
:) In this case I stand neutral. it sounds a bit challenging but I
have zero knowledge of the code base and perhaps the architecture
allows for that.

I find this over all intriguing and interesting, if you intend to work
on this feature would you mind sharing the JIRA number so I can follow
on the code changes?

On Sat, Feb 17, 2018 at 4:06 PM, Daniel Dekany <dd...@apache.org> wrote:
> Saturday, February 17, 2018, 9:36:48 AM, Taher Alkhateeb wrote:
>
>> Wrong link, sorry, correcting.
>>
>> http://jinja.pocoo.org/docs/2.10/templates/#include
>
> Just be sure it's clear, we also have an ignore_missing option (in the
> released versions). But it's often not very useful if you can't do
> some action in case the template is missing.
>
> Jinja also allows you to specify a list of template names instead of
> just one, and uses that as a fallback list. That's something I have
> considered as well, and I believe it covers most use-cases for the
> proposed feature. However, it falls into the same mistake as
> ingnore_missing, as it focuses on a concrete use-case while missing
> another (when you have to do something before the template if it
> exists). I prefer less but more generic devices. With that could have
> avoid adding ignore_missing as well.
>
>> On Feb 17, 2018 11:34 AM, "Taher Alkhateeb" <sl...@gmail.com>
>> wrote:
>>
>>> For a point of comparison, the python jinja2 template engine (widely used)
>>> has an "include" directive that has an attribute named "ignore missing"
>>> [1]. I also remember seeing similar behavior in other engines.
>>>
>>> So I guess perhaps from a usability point of view this seems to be a
>>> desired feature by users. However, I don't know what the impact in terms of
>>> complexity would reflect in the code base.
>>>
>>> http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions
>>>
>>> On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
>>> wrote:
>>>
>>>> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org>
>>>> wrote:
>>>>
>>>> > Some more opinions guys? Especially as we got one opinion against the
>>>> > feature.
>>>> >
>>>>
>>>> Just to clarify my opinion: I am not against this feature; I simply don't
>>>> consider it a must since there are some workaround to get a similar
>>>> behavior. But if it will be implemented I will be happy too.
>>>>
>>>> Jacopo
>>>>
>>>
>
> --
> Thanks,
>  Daniel Dekany
>

Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
Saturday, February 17, 2018, 9:36:48 AM, Taher Alkhateeb wrote:

> Wrong link, sorry, correcting.
>
> http://jinja.pocoo.org/docs/2.10/templates/#include

Just be sure it's clear, we also have an ignore_missing option (in the
released versions). But it's often not very useful if you can't do
some action in case the template is missing.

Jinja also allows you to specify a list of template names instead of
just one, and uses that as a fallback list. That's something I have
considered as well, and I believe it covers most use-cases for the
proposed feature. However, it falls into the same mistake as
ingnore_missing, as it focuses on a concrete use-case while missing
another (when you have to do something before the template if it
exists). I prefer less but more generic devices. With that could have
avoid adding ignore_missing as well.

> On Feb 17, 2018 11:34 AM, "Taher Alkhateeb" <sl...@gmail.com>
> wrote:
>
>> For a point of comparison, the python jinja2 template engine (widely used)
>> has an "include" directive that has an attribute named "ignore missing"
>> [1]. I also remember seeing similar behavior in other engines.
>>
>> So I guess perhaps from a usability point of view this seems to be a
>> desired feature by users. However, I don't know what the impact in terms of
>> complexity would reflect in the code base.
>>
>> http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions
>>
>> On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
>> wrote:
>>
>>> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org>
>>> wrote:
>>>
>>> > Some more opinions guys? Especially as we got one opinion against the
>>> > feature.
>>> >
>>>
>>> Just to clarify my opinion: I am not against this feature; I simply don't
>>> consider it a must since there are some workaround to get a similar
>>> behavior. But if it will be implemented I will be happy too.
>>>
>>> Jacopo
>>>
>>

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Taher Alkhateeb <sl...@gmail.com>.
Wrong link, sorry, correcting.

http://jinja.pocoo.org/docs/2.10/templates/#include

On Feb 17, 2018 11:34 AM, "Taher Alkhateeb" <sl...@gmail.com>
wrote:

> For a point of comparison, the python jinja2 template engine (widely used)
> has an "include" directive that has an attribute named "ignore missing"
> [1]. I also remember seeing similar behavior in other engines.
>
> So I guess perhaps from a usability point of view this seems to be a
> desired feature by users. However, I don't know what the impact in terms of
> complexity would reflect in the code base.
>
> http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions
>
> On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
> wrote:
>
>> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org>
>> wrote:
>>
>> > Some more opinions guys? Especially as we got one opinion against the
>> > feature.
>> >
>>
>> Just to clarify my opinion: I am not against this feature; I simply don't
>> consider it a must since there are some workaround to get a similar
>> behavior. But if it will be implemented I will be happy too.
>>
>> Jacopo
>>
>

Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Taher Alkhateeb <sl...@gmail.com>.
For a point of comparison, the python jinja2 template engine (widely used)
has an "include" directive that has an attribute named "ignore missing"
[1]. I also remember seeing similar behavior in other engines.

So I guess perhaps from a usability point of view this seems to be a
desired feature by users. However, I don't know what the impact in terms of
complexity would reflect in the code base.

http://jinja.pocoo.org/docs/2.10/templates/#list-of-global-functions

On Feb 17, 2018 11:18 AM, "Jacopo Cappellato" <ja...@gmail.com>
wrote:

> On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org> wrote:
>
> > Some more opinions guys? Especially as we got one opinion against the
> > feature.
> >
>
> Just to clarify my opinion: I am not against this feature; I simply don't
> consider it a must since there are some workaround to get a similar
> behavior. But if it will be implemented I will be happy too.
>
> Jacopo
>

Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Jacopo Cappellato <ja...@gmail.com>.
On Fri, Feb 16, 2018 at 8:04 AM, Daniel Dekany <dd...@apache.org> wrote:

> Some more opinions guys? Especially as we got one opinion against the
> feature.
>

Just to clarify my opinion: I am not against this feature; I simply don't
consider it a must since there are some workaround to get a similar
behavior. But if it will be implemented I will be happy too.

Jacopo

Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
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 <dd...@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
>>>
>>>
>

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Daniel Dekany <dd...@apache.org>.
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 <dd...@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
>>
>>

-- 
Thanks,
 Daniel Dekany


Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates

Posted by Jacopo Cappellato <ja...@gmail.com>.
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 <dd...@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
>
>