Opened 13 years ago

Closed 13 years ago

Last modified 13 years ago

#2350 closed enhancement (fixed)

dojo.string.substituteParams needs to be more flexable

Reported by: Michael Schall Owned by: Adam Peller
Priority: high Milestone: 0.9beta
Component: String Version: 0.4.1
Keywords: Cc:
Blocked By: Blocking:

Description

I would like a more flexable dojo.string.substituteParams

1) have nested properties 2) call format functions for each replacement 3) allow properties to be null and give replacement

Note: Option 2: Function signature only accepts value to be formated. Option 3: I have only given ability to have a global null replacement, not per match.

Both of these could be solved by a more complex ${propertyName:formatFunction}, but my suggestion solves my issue with being overly complex.

Example

function formatName(obj) {
	return obj.first + " " + obj.last;
}
function formatDate(val) {
	return judo.format.date(val);
}
var p = 
	{name: 
		{first: 'Michael',
		 middle: null,
		 last: 'Schall'}, 
	 address:
		{street: null,
		 city: 'Clive',
		 state: 'Iowa'},
	 birthDate: new Date(1975, 6, 1)
	};

//"Call" external functions to format
alert(judo.lang.substituteParams(
	"${name:formatName} from ${address.state} was born on ${birthDate:formatDate}", 
	p));
	
//Allow property of object to be null and supply string if null
alert(judo.lang.substituteParams(
	"first name: ${name.first} middle name: ${name.middle} last name: ${name.last}", 
	p, 
	{nullStr: 'n/a'}));

New function

judo.lang.substituteParams = function (/* string */template, /* object */map, /* object */ options) {
	if (!options){options={}};
	return template.replace(/${(w+(?:.w+)*(?::w+(?:.w+)*)?)}/g, function(match, key){
		var format = key.split(":");
		var value = dojo.lang.getObjPathValue(format[0], map);
		if (format.length > 1) {
			var fn = dojo.lang.getObjPathValue(format[1]);
			value = fn(value);
		}
		if(typeof(value) != "undefined") {
			if (value != null) {
				return value;
			}
			if (typeof(options.nullStr) != "undefined") {
				return options.nullStr;
			}
		}
		dojo.raise("Substitution not found: " + key);
	});
};

Also, my suggestion changes the signature for dojo.string.substituteParams. The current function will take parameters after the first and create an array out of them. I personally don't like the practice since it doesn't allow for expansion of function. Why not have the user create the array when calling?

	dojo.string.substituteParams("${0} ${1}", ["test", "stuff"])

I read somewhere (sorry, lost the link) that optional parameters should be sent in as an object with named properties since JavaScript? doesn't support function overloading. I like that suggestion, so I have followed that in my suggestion.

Change History (14)

comment:1 Changed 13 years ago by Michael Schall

Sorry, I also forgot to point out that I changed the %{property} syntax to ${property} since that is the way widget string replacement works. I think the widget version was added to svn long before the string version. We should have a standard way of doing this.

comment:2 Changed 13 years ago by Adam Peller

Owner: changed from psowden to Adam Peller

I'm with you on the ${} change, but I'm not thrilled with adding so much complexity to the method...

comment:3 Changed 13 years ago by Adam Peller

Resolution: fixed
Status: newclosed

(In [7502]) Fixes #2350. Mike - this is a compromise. I added compound paths and whacked the polymorphic/varargs thing. ${} in favor of %{}, but formatting would have to be a separate pass prior to calling this method. Sound reasonable?

comment:4 Changed 13 years ago by Adam Peller

(In [7503]) References #2350. Change substituteParams to substitute

comment:5 Changed 13 years ago by Adam Peller

(In [7505]) References #2350. Change a few more %{} substitutions to ${}

comment:6 Changed 13 years ago by Adam Peller

(In [7506]) References #2350. Undo accidental change.

comment:7 Changed 13 years ago by Adam Peller

(In [7507]) one more time. References #2350

comment:8 Changed 13 years ago by Adam Peller

Milestone: 0.9M1

hmm... looks like I did the format thing, too?

comment:9 Changed 13 years ago by Michael Schall

Resolution: fixed
Status: closedreopened

There is a small issue with the regex in this function.

dojo.string.substitute(
   "<span title='${AuditInformation.EnteredBy}'>"
   + "${AuditInformation.EnteredBy}</span>", 
   obj)

finds key of "AuditInformation?.EnteredBy?}'>${AuditInformation?.EnteredBy?"

The regex

/${([^s:]+)(?::(S+))?}/

needs to be

/${([^s:}]+)(?::(S+))?}/

So the capture will stop if a '}' is found.

comment:10 Changed 13 years ago by Adam Peller

Resolution: fixed
Status: reopenedclosed

(In [7787]) Fixes #2350. Thanks again, schallm

comment:11 Changed 13 years ago by guest

Resolution: fixed
Status: closedreopened

I found an issue with current regex. If a format function is specified, the same issue may occur as with the last change...

dojo.string.substitute(
   "<span title='${AuditInformation.EnteredBy:formatName}'>"
   + "${AuditInformation.EnteredBy}</span>", 
   obj)

Will find the format function of "formatName}'>${AuditInformation?.EnteredBy?"

So the format section of the regex needs to match the key section

/${([^s:}]+)(?::(S+))?}/

becomes

/${([^s:}]+)(?::([^s:}]+))?}/g

comment:12 Changed 13 years ago by Adam Peller

Milestone: 0.9M10.9beta

comment:13 Changed 13 years ago by Adam Peller

Resolution: fixed
Status: reopenedclosed

(In [8588]) Fixes #2350

comment:14 Changed 13 years ago by Adam Peller

(In [8646]) Provide a bit more flexibility to allow null references to be run through the format function, as intended. Refs #2350.

Note: See TracTickets for help on using tickets.