Parametrized Variables

Basic concept

Like the ParameterizedIncludes, there should be the possibility to pass parameters when including (user definied) variables. When the variable is expanded, the parameters are inserted in place of placeholders (like $param("name")).

Examples

For example, there is a variable definition in the TWikiPreferences, looking like this:
      * Set ATTACHEDIMAGE = *$param("title")* %BR% <img src="%ATTACHURLPATH%/$param("filename")" alt="$param("filename") />

I have only to write
%ATTACHEDIMAGE{filename="mypicture.gif" title="This is my favorite picture"}%

in a topic to inline an attached image. There would be less html code and more readable text in the topics.

Another example, I want to link to a certain revision of a topic. The only way I know is to pass URL paramters. So I would define the variable
      * Set LINKREV = [[%SCRIPTURL%/view/%WEB%/$param("topic")?rev=$param("rev")][$param("topic") r$param("rev")]]

My link would look like this:
I refer to %LINKREV{topic="ParameterizedIncludes" rev="1.12"}%.

And would expand to:

I refer to ParameterizedIncludes 1.12.

A great enhancement could be made by defining search strings, so people don't have to use the whole syntax. What about writing
%TASKLIST{state="New"}%
%TASKLIST{state="InProgress"}%

That expands to somethink like
---++ Tasks New
%SEARCH{"META:FIELD{...ahorribleunreadableregularexpression ... New..." ... manymoreparameters ...}%

---++ Tasks InProgress
%SEARCH{"META:FIELD{...ahorribleunreadableregularexpression ... InProgress..." ... manymoreparameters ...}%

This could be done with ParameterizedIncludes as well, but this way there would be less small abstract topics containing only one or to lines of text.

Default Values

Default values could be practical. The above example of the LINKREVvariable could be definied like this:
      * Set LINKREV = [[%SCRIPTURL%/$param("action" default="view")/$param("web" default="%WEB%")/$param("topic")?rev=$param("rev")][$param("topic") r$param("rev")]]

There would be a thousand possibilities to improve usability.

-- StefanSteinegger - 04 Nov 2004

Discussion

See TopicVarsPlugin and MacrosPlugin - one of the two might do what you want.

-- MartinCleaver - 04 Nov 2004

If you have CommentPlugin (16 Oct 2004 - 3.008) installed then you could probably use that with the noform parameter.

-- SamHasler - 05 Nov 2004

Ok, TopicVarsPlugin should work, but actually doesn't. The webserver doesn't respond anymore. (mod_perl?) If it would work, this topic would still be a syntax enhancement request :).

MacrosPlugin should also work (I have to try it first) and sounds more like what i need. It doesn't solve the problem of many small abstract topics, because macros are definied in topics, not in variables. But variables have some problems with linefeeds anyway.

Shouldn't there be a consolidation of TopicVarsPlugin, MacrosPlugin and ParameterizedIncludes? Many simple plugins could be defined just as a set of macros or includes. And html code, that is now everywhere in the topics, could be completly moved into macros. I mean, there could be a clean, simple syntax to do almost everything you want.

-- StefanSteinegger - 05 Nov 2004

MacrosPlugin works! BUT there is always a linefeed at the end of every topic. So I can't use it as a single expression, like my LINKREV example. It wouldn't work in tables for instance, I would always have to define a whole line. So this kind of macro can't be nested like variables. What I want is much simpler and possibly more powerful.

-- StefanSteinegger - 05 Nov 2004

Stefan, this an excellent idea, and based on the DEVELOP codebase would be very easy to do smile

It feels rather clunky to have two separate syntaxes for doing this. We should try to fold this spec together with ParameterizedIncludes spec. How about just using the same syntax? It would mean the * Set would look a bit strange in a normal view, but it should work otherwise. For example:
%UNLESS("cereal" default="Grape Nuts"}%
%UNLESS("juice" default="Ananas"}%
   * Set BREAKFAST = %cereal% and %fruit% juice
%cereal% is my favourite, especially with %fruit% juice. Today I'm having %BREAKFAST{cereal="Muesli" fruit="Orange"}%. Tomorrow I will have %BREAKFAST{juice="Carambola"}%

would appear when rendered as:

  • Set BREAKFAST = Grape Nuts and Ananas juice
Grape Nuts is my favourite, especially with Ananas juice. Today I'm having Muesli and Orange juice. Tomorrow I will have Grape Nuts and Carambola juice.

Does that make sense to you? If a parameter value is not given, the closest enclosing UNLESS applies.

BTW if you use %STRIP% in a MacrosPlugin macro, that final linefeed should disappear.

-- CrawfordCurrie - 05 Nov 2004

I found out about %STRIP%. It solves many of my problems. Thanks.

I still would like to have my macros in variables, rather then topics. Im not sure if the %UNLESS% syntax fits in this situation. It seems to define default values on topic level (or even more globally), not variable (= macro) level. In other words: if there is another macro that uses cereal or fruit, it wants to define its own defaults. Maybe, the $param("name" default="value") syntax is more appropriate, even if you have to retype the default value several times. May be you know a better solution for that.

I think, most important is the usage of the macro, beacuse the definition could be done by administrators and experienced users. It should be possible to define easy-to-use macros to encapsulate more complicated expressions. I write lots of variables to define stuff like urls, table headers, search strings and so on. They're easy to use even for newbies ( %INCLUDE{}% and %CALLMACRO{}% look more cryptic). But it's just not generic enough.

-- StefanSteinegger - 05 Nov 2004

The best idea is probably to think in terms of an existing, effective template system, and model on that. For example, cpp has a very effective approach where parameters to a macro are formally declared before being used. That would allow the assignment of a default in the formal parameters, rather than in the body. For example:
   * Set BREAKFAST(cereal="Grape Nuts" fruit) = %cereal% and %fruit% juice

BTW I am dead set against the $param(...) syntax. I think it's really, really horrible, not least because it has all the same expansion order problems as the %% syntax, without the advantage of being able to share the same parser.

-- CrawfordCurrie - 05 Nov 2004

I understand. I like your syntax. If you declare all the parameters you need for a macro, you should not be able to mix them with other TWikiVaribles:
      * Set juice = "Orange"
      * Set breakfast(juice) = %juice% juice

In another topic you write
Today, I'm having %juice{"apple"}% for breakfast.
Will I have %juice% tomorrow?

This should not work. Default values should always be in the scope of the macro, never taken from other TWikiVariables. The output could be like this:

Today, I'm having apple juice for breakfast. Will I have <missing parameter =juice=> juice tomorrow?

What are we doing if someone writes
      * Set SPECIALSEARCH(REGEX, TOPIC="MyDefaultTopic") = %SEARCH{"%REGEX%" topic="%TOPIC%", format="..." header="..." ...}%

There are the expansion order problems, of course. Is there any topic discussing / documenting this?

-- StefanSteinegger - 08 Nov 2004

Agreed about the scoping, though I think your example is wrong? Shouldn't is be %breakfast{juice="apple"}%.? Note that if a parameter is given without a key (as in %breakfast{"apple"}%) then that value is always associated with the key _DEFAULT. For consistency I think this use of _DEFAULT should require declaration as well, as in
   * Set breakfast(_DEFAULT juice="orange") = %_DEFAULT% and %juice% juice
Today, I'm having %breakfast{"porridge" juice="grape"}% for breakfast.

There should be no expansion order problems if the code on DEVELOP is used, because it always fully expands parameters to a macro before the macro itself. It won't work with MAIN, of course. It will always be possible to defeat, as any macro language is, but as long as the user is sensible it should be OK.

-- CrawfordCurrie - 08 Nov 2004

You're right, my example is wrong. I forgot the params name.

_DEFAULT looks nice. But what are you doing to simulate behaviour of %SEARCH%, where you can write %SEARCH{search="..."}% or just %SEARCH{"..."}%. To make TWiki truly expansible, users shouldn't have to distinguish between built in and user defined variables. (Maybe %SEARCH% should be redefined?)

-- StefanSteinegger - 09 Nov 2004

I suppose one could always dosomething like this:
   * Set guide(_DEFAULT="city" city country="Sweden") = %city% is a city in %country%
%guide{"Frankfurt" country="Poland"}%
%guide{city="Dusseldorf"}%

but TBH, I think this is a bit confusing. Let's keep it simple and quietly ignore %SEARCH%.

-- CrawfordCurrie - 09 Nov 2004

Although I have not yet studied this proposal, parametrized variables are useful for TWikiApplications. In essence these are macros. I suggest not to diverge too much from the existing syntax.

-- PeterThoeny - 09 Nov 2004

You mean the %TMPL:P syntax? From a user perspective, IMHO the proposal here is a heck of a lot less divergent than suddenly introducing TMPL:DEF and TMPL:P to end users. The existing * Set is alreadya macro definition:
   * Set MYVARIABLE = MyValue

is almost exactly equivalent to
%TMPL:DEF{"MYVARIABLE"}%MyValue%TMPL:END%

(yes, I know about the whitespace problems, but both implementations currently have issues)

Also, %TMPL:P doesn't support parameters ATM, so would end up having to be extended in the same way.

I'm all in favour of normalising to a single syntax, but from a perspective of making life easier for end users (and TWikiApplications), this is the more sensible route. Replacing %TMPL:XX in templates would be a lot less work than replacing every = Set= with a %TMPL:DEF and every %VARIABLE% with a %TMPL:P, surely?

Here is the full spec of what we have been discussing, suitable for inclusion in the documentation: #ProposedSpec

Using TWiki Variables as Macros

The "* Set" syntax can also be used to define a macro. A macro is a piece of text with placeholders. These placeholders are filled in with text you supply when the macro is expanded.

You must list the placeholders that your macro uses, before you use them. Placeholders used by a macro must be listed in curly braces immediately after the macro name, like this:
   * Set CARS{mycar hercar pourcar}% = My car is a %mycar%, my partner's car is an %hercar% and our shared car is a %ourcar%

   %CARS{hercar="Audi TT" ourcar="Land Rover Discovery" mycar="Aston-Martin DB9"}%

In this example, the last line will expand to My car is an Aston-Martin DB9, my partner's car is an Audi TT and our shared car is a Land Rover Discovery. Note that the convention is to give parameters lower case names. This is to avoid the risk of confusion with other TWiki variables.

Macros can also take a single unnamed placeholder, like this: %MYMACRO{"myvalue"}%. The unnamed placeholder will be assigned to the special name _DEFAULT. If you want your macro to support the unnamed placeholder, you have to declare _DEFAULTin the curly braces, same as any other placeholders.
   * Set CRUSTACEAN{_DEFAULT} = A %_DEFAULT% is a crustacean

   %CRUSTACEAN{"lobster"}%

A placeholder that is not defined when the macro is called will be given the value "" (i.e. the empty string). This includes _DEFAULT. So:
   * Set CRUSTACEAN{_DEFAULT predator} = A %_DEFAULT% is a crustacean, eaten by %predator%

   %CRUSTACEAN{"lobster" predator="octopus"}% will expand to "A lobster is a crustacean eaten by octopus"
   %CRUSTACEAN{"lobster"}% will expand to "A lobster is a crustacean eaten by "
   %CRUSTACEAN{predator="octopus"}% will expand to "A  is a crustacean eaten by octopus"
   %CRUSTACEAN% will expand to "A  is a crustacean eaten by "

This rule applies even when a placholder has the same name as a TWiki variable.

You can also specify a default valuefor the placeholder, to be used if no value is given. You do this in the macro definition. For example,
   * Set CRUSTACEAN{_DEFAULT="prawn" predator="cuttlefish"} = A %_DEFAULT% is a crustacean, eaten by %predator%

   %CRUSTACEAN{"lobster" predator="octopus"}% will expand to "A lobster is a crustacean eaten by octopus"
   %CRUSTACEAN{"lobster"}% will expand to "A lobster is a crustacean eaten by cuttlefish"
   %CRUSTACEAN{predator="whales"}% will expand to "A prawn is a crustacean eaten by whales"
   %CRUSTACEAN% will expand to "A prawn is a crustacean eaten by cuttlefish"

See also %INCLUDE for another way to define macros, more suitable for larger amounts of text.

-- CrawfordCurrie - 09 Nov 2004

That's it!

-- StefanSteinegger - 11 Nov 2004

What happened to this feature? Did it make it into Cairo or Dakar?

-- PankajPant - 09 Mar 2006

nope

-- RafaelAlvarez - 09 Mar 2006

Shame, this would be very useful for a TWiki Knowledgebase application I'm developing. It would make things like uniformly formatted literature references much easier (e.g. like the cite templates used in Wikipedia, see http://en.wikipedia.org/wiki/Wikipedia:Template_messages/Sources_of_articles/Generic_citations)

-- LevienVanZon - 23 Jun 2006

So, a case of the TWiki community thinking of something way before the competition but executing way after.

-- MartinCleaver - 22 Jul 2006

We really need to stop calling the variables. I understand that there's history for calling stuff TWikiVariables, but variables do not take parameters; functions do.

-- MeredithLesly - 24 Jul 2006

Or macros.

-- ArthurClemens - 24 Jul 2006

On 09 Nov 2004 PeterThoeny said:

> Although I have not yet studied this proposal, parametrized variables are useful for TWikiApplications. In essence these are macros. I suggest not to diverge too much from the existing syntax.

Two issues:
  1. Have you read this now?
  2. What do you think?
  3. How flexible are you on the naming of the 'essence of macros'
Related is AreVariablesReallyDirectives.

-- MartinCleaver - 25 Jul 2006

Two things:

  1. I disagree with how _DEFAULT is spec'd. It should be
    * Set CEPHALOPOD{_DEFAULT="predator" predator="cuttlefish"} = %predator% is a cephalopod
    IOW, setting a "default" for _DEFAULT should be to name which parameter is to get the value when no parameter name is specified at invocation. Thus, %<nop>CEPHALOPOD{"lobster"}% would yield lobster is a cephalopod.
  2. Why hasn't this been implemented? I was beginning to look into creating a Plugin just to provide this capability. The UsingSnippets approach is awkward and heavy for many uses.
-- RobStewart - 04 Sep 2008

On examining Rob's request on RelaxRegisterTagHandlerCallingContext, I wonder if we should bring parameters (ie function call like aliases) into the Variable settings system, or to adda paralell aliasing system

When though about in light of the ResultSets and ExtractAndCentralizeFormattingRefactor twiki5 features, I think we need to add this to twiki5 (Alias or Set, I don't mind - i'm just adding the keyword for consideration)
   * Alias WEBCHANGES{web="%BASEWEB%"} = %SEARCH{".*" web="$web" order="modified" reverse="on" format="$dollarweb.$dollartopic"}%
and / or
   * Set LINKREV{topic="%BASEWEB%.%BASETOPIC%" rev=""} = [[%SCRIPTURL%/view/%topic%?rev=%rev%][%topic% r%rev%]]

which is similar to the future (twiki5) 
      %FORMAT{"[[%SCRIPTURL%/view/$topic?rev=$rev][$topic r$rev]]" topic="%BASEWEB%.%BASETOPIC%" rev=""}% of ExtractAndCentralizeFormattingRefactor

The WEBCHANGES eg shows a consequence of useing $topic variable type for this feature - ie. $dollarweb.$dollartopic (quite a bit confusing).

There seem to me to be many many useful side effects of this proposal - code based registerTags are a global and finalised 'Alias/Setting', so a Contrib could use a %DOSOMETHING{}% Setting, which would be over-ridden by a Plugin that defines a real DOSOMETHING coded tag (ie, rapid development as a TML alias, and then when more speed is needed, a plugin can be used to optimse it)

neat.

-- SvenDowideit - 07 Sep 2008

Please remember a date in date of commitment field so the proposal app can work. Added todays date

-- KennethLavrsen - 11 Sep 2008

Implemented somewhat differently in EasyMacroPlugin. This uses a
%REGISTERMACRO{"NEWMACRO" topic="<web.topic>" param="<unnamed parameter>" ...}%

or
%REGISTERMACRO{"NEWMACRO" format="..." param="<unnamed parameter>" ...}%

The reason not to use the normal * Set NEWMACRO notation is that registering a macro can now be done as part of the normal TML parsing, e.g. inside a view template or generated with whatever means you like.

Bunches of macros can be registered using the EASYMACROS preference variable pointing to a list of topics to harvest all macro registrations.

For instance: %RED% can be defined more generically by implementing a %COLOR{"<name or hexcode>"}% like this:
%REGISTERMACRO{"COLOR" format="<font color='$color'>" param="color"}%

During registration, default values can be specified. So let the %COLOR macro default to red you do something like:
%REGISTERMACRO{"COLOR" format="<font color='$color'>" param="color" color="red"}%

The set of parameters of the REGISTERMACRO call are merged with the actual call to %COLOR with the latter having precedence. So a %COLOR%...%ENDCOLOR% makes it a red text whereas %COLOR{"blue"}%...%ENDCOLOR% changes it to blue.

-- MichaelDaum - 03 Nov 2009

I don't understand how the difference between named parameters and the default parameter is managed. Can you explain?

-- CrawfordCurrie - 19 Nov 2009

The default paramenter (the unnamed one) is mapped onto a variable using the param param. In the above example %COLOR{"red"}% will call the registered macro by binding color=red. Thats because we wrote %REGISTERMACRO{...param="color" ...}%.

In addition the %REGISTERMACRO can defined defaults for any parameters, i.e. color itself. That is, the above registration will use color=red when calling %COLOR%.

Technically the $params hash of the perl handler for %COLOR% is merged with the $params hash of its %REGISTERMACRO thus blending in defaults.

-- MichaelDaum - 19 Nov 2009

Nice work, Michael. I'll be using this one.

-- MartinCleaver - 31 Jan 2010

Trademark Wiki possibly will have this in its next release too. Which of Foswikis extensions does implement this feature?

-- FranzJosefGigler - 14 Dec 2010

EasyMacroPlugin

-- MichaelDaum - 15 Dec 2010

EasyMacroPlugin does not implement the feature proposed here, and IMHO is somewhat awkward to use. I'm re-opening this feature request.

If it's considered appropriate, I can implement my 05 Nov 2004 proposal above in the core in a few lines of code - and it works. Patch available on request. Re-opening the feature request. Set myself as developer and reset the clock.

(For those in doubt, I mean:
   * Set BLAH = %DEFAULT% in a %place%
%BLAH{"you're" place="pickle"}%

-- CrawfordCurrie - 15 Dec 2010

EasyMacroPlugin is far more powerful the the very simple, but limited solution Crawford is proposing.

But often the simple one line macro does the trick just fine. I would welcome this alternative approach, particularly as for the user it does not matter, how the macro is defined. And as developer, I have the choice to use either implementation.

-- AndreLichtsteiner - 15 Dec 2010

Andre is right, the users should have the choice. smile And due to Crawford and Michael they now will have the choice. Thanks!

-- FranzJosefGigler - 15 Dec 2010

No negative feedback yet, so I guess that bodes well. I have to checkin so that my changes can reflected in my mobile dev system over Christmas, but it can be reverted any time.

Tasks.Item10187

-- CrawfordCurrie - 22 Dec 2010

This looks pretty interesting.

I had a quick play at http://trunk.foswiki.org/Sandbox/ParameterizedVariablesTest, because I wanted to see what would happen if I used a parameter that was already a macro, such as %DATE%. For example:
   * Set FOO = Hello from %DATE%

%FOO{DATE="1980"}%
%FOO%

It works as I would expect. If I pass a DATE, then it will render as Hello from 1980. By default, it will expand the usual macro, as: Hello from 22 Dec 2010.

Looking forward to using this.

-- AndrewJones - 22 Dec 2010

I think it is essential that Foswiki gets this feature in 1.2 - now that our competitor has it. And we should make it compatible. It is fine that there is an extension that can do the same function and more. But this should really go into core in its simple form and then let the extension take care of those in need of more.

-- KennethLavrsen - 30 Jun 2011

"And we should make it compatible" - only if what they have done is sensible, which would mean it should already be consistent with the code we already have.

-- CrawfordCurrie - 30 Jun 2011

The only quirk we need to sort out is that VarIF can't tell if a parameter coming from a parameterized variable is defined or not (it's always undef, even though you passed in a var).

This is inconsistent with VarINCLUDE behaviour, where you can use VarIF to test if a parameter from a parameterised include is defined

-- PaulHarvey - 01 Jul 2011

So it might make sense to adopt their %DEFAULT{}% thingy, as building IfStatements to check/set defaults are clumsy anyway

-- PaulHarvey - 01 Jul 2011

This looks like a nice idea at first glance, but the specification is incomplete.

The trivial case is easy:
  • Set HEART=I like turkey
%HEART% I like coffee
%HEART{}% I like coffee
=%HEART{WHAT="peanuts"}% I like peanuts
What if I want to put a " in the parameter? May I use $quot ? May I use $percent?
  • Set HEART2=I like turkey
%HEART2% I like coffee
%HEART2{"a big" WHAT="$quot"}% I like a big " OR I like a big $quot
What if I redefine things?
  • Set WHAT=turkey
%HEART% ?! I like turkey OR I like coffee
Does it matter in which order these things are defined? It might not have mattered before, but I think this proposal makes the order matter.
  • Set HEART3=I like turkey
%HEART3% ?! I like coffee OR I like turkey
%HEART3{WHAT="tea"}% ?! I like tea OR I like turkey
I don't know what the answer is, but we ought to be able to say in advance what outcome to expect. I cannot do that.

Does it matter how deeply we are nested?
  • Set BOLDER=innerdefault
  • Set OUTER=innerdefault
%OUTER% innerdefault
%OUTER{"foo"}% innerdefault
  • Set OUTER2=DEFAULT
%OUTER2{"foo"}% foo OR  
Is there a way I may use the default value for BOLDER's parameter, without repeating it in OUTER*'s definition?

It turns out that our parser does let you redefine macros. I chose INCLUDE here because it is a commonly-used standard macro, but it could just as well be a macro provided by an obscure plugin. (It is "commented out" so that it does not actually redefine INCLUDE.)
  • #Set INCLUDE=No, you may not include another topic
Note that tmwiki went with %DEFAULT%, not %_DEFAULT%. Also note that my concerns are not really new. They are very similar to ones raised in 2004, which as far as I can tell were never addressed.

-- MichaelTempest - 01 Jul 2011

I was just about to make the same comments, and Michael beat me to it smile So I guess I need to think about how to address these points.

  1. Yes, $quot and all the other quoted character standards should apply. But what does "default" mean? If I call %SNOOD% then the default clearly applies. But how about if I call %SNOOD{""}%? (I would say no, the default does not apply, but I have no idea what the tmwiki code would do with this).
  2. A macro parameter should always override a macro defined at any other scope (including session scope). If the macro parameter is not given a value in the call, then any default should apply. If no default is given, then the outer scope should apply.
  3. The standard left-right-inside-out rules for macro evaluation must continue to apply - so macro parameters work exactly like INCLUDE parameters. For consistency, the default concept should be promoted to all macros (thus %FOO{default="snood"}% will expand to snood if %FOO is not set in all contexts, not just parameterized macros). And this of course includes INCLUDE parameters.
  4. I don't think it matters much whether you use %_DEFAULT or %DEFAULT - it's much of a muchness, as _DEFAULT (the true parameter name) is never seen by end users anyway. But our existing code uses %DEFAULT.
Note that macro parameters should override all other macros. So where you have * Set FOO = %INCLUDE{"flub"}% then %FOO{INCLUDE="blah"}% will expand to blah.

I updated trunk and Release 01x01 with code and unit tests to cover cases as described.

-- CrawfordCurrie - 01 Jul 2011

When I said compatible with competition then naturally I meant as good OR BETTER.

And since we are the superior product better is the right way. I agree with the above

-- KennethLavrsen - 01 Jul 2011

Eagerly waiting for this. If there was a way to vote, I would vote for this feature to be included in Core. Way to go!

-- AlexWayne - 31 Aug 2011

No need to vote - Crawford implemented it in core, for trunk (to be Foswiki 1.2/2.0) and Release01x01 (to be Foswiki 1.1.4) smile

-- PaulHarvey - 03 Sep 2011
 
Topic revision: r27 - 24 Oct 2011, PaulHarvey
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy