Re: Call macro with new root

"John Redford" <[email protected]>
Newsgroups gmane.comp.web.freemarker.user
Message-ID <[email protected]>
Indeed, it's clearly not possible. This is more of a "feature request". The
suggestion of using a passed parameter won't quite solve the problem here,
because there is a fundamental inconsistency between how macros are called
from other macros and how they are called from Java.

+ When invoked from Java, you pass a "root", which _itself_ cannot be
accessed, but which provides names to the macro.
+ When invoked from a macro, you (can) pass one-or-more named objects which
can contain names & other objects.

I am using macros both directly from Java and from other macros, and I don't
want to replicate a complex set of macros to be able to accommodate both
calling conventions. This is really two or three feature requests:

1. It should be possible to invoke a macro from a macro as if it were being
invoked from Java with a given "root" value. This will facilitate
reusability of macros.
2. When invoked from Java, the "root" object itself should be available
(i.e. ${root}). This may not be useful when the root is an actual Map, but
it would be useful when the root is an actual object/bean.
3. It should be possible to invoke a top-level macro from Java with passed
parameters, if as it were being invoked by another macro. (I have no
immediate use for this, but I am sure it would be useful for someone.)

Between the three, I am most interested in #1, because I think that the
simpler a macro is, the better  -- and while having the complex
parameter-passing features is great when they're needed, I'd prefer to not
use them excessively -- particularly as the content of the macros themselves
is somewhat complicated.

Thus I can have a macro:

  <#macro Foo>...long, complicated text, coditionals, etc....
${name}...${url}...${something}...</#macro>

And then call it from Java (Where I have a wrapper around a Configuration
with an auto-import that takes a macro name and simply returns the string)
as:

  for (Widget w : widgets ) { String s = fm.macro("Library.Foo", w); ... }

And call it from within another macro (here in the same namespace):

  <#list widgets as w>
    <@Foo=w/>
  </#list>

If #2 were implemented, then I could write the somewhat uglier version: 

  <#macro Foo c>...long, complicated text, coditionals, etc....
${c.name}...${c.url}...${c.something}...</#macro>
  <#macro JavaFoo><@Foo c=${.root}</#macro>

The present implementation necessitates having the Java layer wrap the root
object in a hash, pass that, and then use the uglier macros:

  <#macro Foo c>...long, complicated text, coditionals, etc....
${c.name}...${c.url}...${c.something}...</#macro>
  <#macro JavaFoo><@Foo c=${.root}</#macro>

  Map<String,T> wrapRoot(T root) { ...return map with key "root" mapped to
the root value }

  for (Widget w : widgets ) { Map<String,Widget> wrapper = wrapRoot(w);
String s = fm.macro("Library.Foo", wrapper); ... }

The "macro" wrapper I am using could be updated do this wrapping
automatically, so the Java code could still look reasonably nice; but having
to have "Java entry point" macros and qualifier parameters would be nice to
do without.


Daniel Dekany wrote:
> 
> It's not possible to do as far as I see. Especially as it should be
lexically scoped
> (in most applications at least). The best you can do is something like:
> 
>   <#macro Product>
>     <#local c = latestProduct> <#-- c for context -->
>     ${c.name} (${c.url})
>   </#macro>
> 
> Then at least it's obvious what's going on...
 
> 
> Thursday, July 28, 2011, 9:24:14 PM, John Redford wrote:
> 
> > I want to be able to call a macro with a given node used as the
effective
> "root"
> > for the called macro.  For example, given the data model:
> >
> > (root)
> >   |
> >   +- user = "Big Joe"
> >   |
> >   +- latestProduct
> >       |
> >       +- url = "products/greenmouse.html"
> >       |
> >       +- name = "green mouse"
> >
> > I want to have these macros:
> >
> > <#macro Product>
> >  ${name} (${url})
> > </#macro>
> >
> > <#macro Describe>
> >  ${user}'s latest product is <@Product=latestProduct/>.
> > </#macro>
> >
> > Such that the result is:
> >
> > Big Joe's latest product is green mouse (products/greenmouse.html).
> >
> > I have looked into ways to achieve something like this using
> > TemplateDirectiveModel to writing a directive that re-roots the
> environment, but
> > that does not seem possible.


------------------------------------------------------------------------------
Got Input?   Slashdot Needs You.
Take our quick survey online.  Come on, we don't ask for help often.
Plus, you'll get a chance to win $100 to spend on ThinkGeek.
http://p.sf.net/sfu/slashdot-survey
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.