freemarker-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Dekany <ddek...@apache.org>
Subject Re: Proposal for FREEMARKER-84: More flexible handlig of missing templates
Date Sat, 10 Mar 2018 20:37:20 GMT
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


Mime
View raw message