Tags:
create new tag
, view all tags

Feature Proposal: Parameterized Variables

Motivation

Like the ParameterizedIncludes, there should be the possibility to pass parameters when using (user defined) variables. When the variable is expanded, the parameters are inserted in place of placeholders.

Description and Documentation

NOTE: Official documentation is at TWiki.ParameterizedVariables.

Variables are user defined by preferences settings on site level (Main.TWikiPreferences), web level (WebPreferences) and topic level using this syntax:

   * Set EXAMPLE = some value

Which an be used by writing %EXAMPLE%. This is existing spec.

This proposal expands on the same syntax to add parameter handling in a similar way ParameterizedIncludes are handled:

   * Set EXAMPLE = Some value using %PARAM1% and %PARAM2%

Which an be used by writing %EXAMPLE{ PARAM1="foo" PARAM2="bar" }%.

Parameters in the variable definition are expanded using the following sequence:

  1. Parameter from variable call. In above example, %PARAM1% gets expanded to foo.
  2. Session variable, preferences settings, plugin tags (existing spec)

The nameless parameter such as %EXAMPLE{ "nameless" }% is supported as well. The variable %DEFAULT% is expanded with the value of the nameless parameter.

Default values can be defined in case of missing parameters. For example,

  • Set DEMO = Demo using %PARAM1{ default="(undefined)" }%

returns (undefined) for PARAM1 if %DEMO{}% is called without the PARAM1 parameter.

Examples

Define variables:

   * Set DRINK = Red Wine
   * (Note that DEFAULT, DISH and DESERT are not defined)
   * Set FAVORITE = My %DEFAULT{ default="favorite" }% dish is %DISH{ default="Steak" }%,
                                 my favorite drink is %DRINK%, my favorite desert is %DESERT%.

Use Variable:

%FAVORITE{ DISH="Sushi" DRINK="Sake" DESERT="Tiramisu" }%
   Returns: My favorite dish is Sushi, my favorite drink is Sake, my favorite desert is Tiramisu.

%FAVORITE{}%
   Returns: My favorite dish is Steak, my favorite drink is Red Wine, my favorite desert is %DESERT%.

%FAVORITE{ "absolutely favorite" }%
   Returns: My absolutely favorite dish is Steak, my favorite drink is Red Wine, my favorite desert is %DESERT%.

Implementation

-- Contributors: PeterThoeny, ChrisLahti - 2010-10-19

Old Proposal

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("filname")" alt="$param("filname") />

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 LINKREV variable 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 already a 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 _DEFAULT in 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 value for 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

Reading this over, I like StefanSteinegger original proposal. It is consistent syntax with VarSEARCH and FormattedSearch:
* Set ATTACHEDIMAGE = *$param(title)* %BR% <img src="%ATTACHURLPATH%/$param(filename)" alt="$param(filename) />

Thinking it over, parameterized variables are similar to the existing parameterized INCLUDE. Hence it might make sense to align the syntax. Examples:

1.a. Example INCLUDE, define:

%STARTSECTION{"favorite"}%
My favorite dish is %dish%, my favorite drink is %drink%.
%ENDSECTION{"favorite"}%

1.b. Example INCLUDE, use:

%INCLUDE{ "MyFavorites" section="favorite" dish="Sushi" drink="Sake" }%

2.a. Example Variable, define:

   * Set FAVORITE = My favorite dish is %dish%, my favorite drink is %drink%.

2.b. Example Variable, use:

%FAVORITE{ dish="Sushi" drink="Sake" }%

This could be the spec of the first implementation.

An enhanced version could support a nameless parameter:

Define:
   * Set FAVORITE = My favorite %_DEFAULT% dish is %dish%, my favorite drink is %drink%.
Use:
  %FAVORITE{ "dinner" dish="Sushi" drink="Sake" }%
Result:
  My favorite dinner dish is Sushi, my favorite drink is Sake.

Another enhancement is to support default values. Possibly simply use the same rule we already have for INCLUDE: A variable within a preferences setting is expanded in this order:

  1. Parameter value when parameterized variable is called (example: dish="Sushi")
  2. Preferences setting or variable of that name (example: * Set dish = stake)
  3. Unexpanded variable (example: %dish%)

Example:

Define:
   * Set dish = stake
   * Set drink = red wine
   * Set FAVORITE = My favorite %_DEFAULT% dish is %dish%, my favorite drink is %drink%, my favorite desert is %desert%.
Use 1:
  %FAVORITE{ "dinner" dish="Sushi" drink="Sake" desert="coffee ice cream" }%
Result 1:
  My favorite dinner dish is Sushi, my favorite drink is Sake, my favorite desert is coffee ice cream.
Use 2:
  %FAVORITE{ "dinner" }%
Result 2:
  My favorite dinner dish is stake, my favorite drink is red wine, my favorite desert is %desert%.

Anyone interested in driving this useful feature?

-- PeterThoeny - 2010-07-13

I added the new proposed spec on top, which I think is intuitive and compatible with the existing syntax.

-- PeterThoeny - 2010-10-19

I enhanced the spec slightly for better usability: Ability to define a default value using a default parameter. For example,

  • Set DEMO = Demo using %PARAM1{ default="(undefined)" }%

returns (undefined) for PARAM1 if %DEMO{}% is called without the PARAM1 parameter.

-- PeterThoeny - 2011-03-27

Edit | Attach | Watch | Print version | History: r39 < r38 < r37 < r36 < r35 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r39 - 2011-03-27 - PeterThoeny
 
  • Learn about TWiki  
  • Download TWiki
This site is powered by the TWiki collaboration platform Powered by Perl Hosted by OICcam.com Ideas, requests, problems regarding TWiki? Send feedback. Ask community in the support forum.
Copyright © 1999-2017 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.