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
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:
- Have you read this now?
- What do you think?
- How flexible are you on the naming of the 'essence of macros'
Related is
AreVariablesReallyDirectives.
--
MartinCleaver - 25 Jul 2006
Two things:
- 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.
- 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.
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:
%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?
%HEART2% |
I like coffee |
%HEART2{"a big" WHAT="$quot"}% |
I like a big " |
OR |
I like a big $quot |
What if I redefine things?
%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.
%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 |
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
So I guess I need to think about how to address these points.
- 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).
- 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.
- 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.
- 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)
--
PaulHarvey - 03 Sep 2011