-
-
Save padolsey/6008842 to your computer and use it in GitHub Desktop.
/** | |
* Outputs a new function with interpolated object property values. | |
* Use like so: | |
* var fn = makeInterpolator('some/url/{param1}/{param2}'); | |
* fn({ param1: 123, param2: 456 }); // => 'some/url/123/456' | |
*/ | |
var makeInterpolator = (function() { | |
var rc = { | |
'\n': '\\n', '\"': '\\\"', | |
'\u2028': '\\u2028', '\u2029': '\\u2029' | |
}; | |
return function makeInterpolator(str) { | |
return new Function( | |
'o', | |
'return "' + ( | |
str | |
.replace(/["\n\r\u2028\u2029]/g, function($0) { | |
return rc[$0]; | |
}) | |
.replace(/\{([\s\S]+?)\}/g, '" + o["$1"] + "') | |
) + '";' | |
); | |
}; | |
}()); | |
var url = makeInterpolator('http://something.com/{param}/{foo}?x={n}'); | |
url({ | |
param: '123', | |
foo: '456', | |
n: 789 | |
}); |
Why not do the first 3 replace
calls with a single one instead? (Or am I missing something?)
leaflet has a more compact version that turns it into one regex
function leafletInterpolater(str, data) {
return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
var value = data[key];
if (value === undefined) {
throw new Error('No value provided for variable ' + str);
} else if (typeof value === 'function') {
value = value(data);
}
return value;
});
}
in this case you'd either do
leafletInterpolater('http://something.com/{param}/{foo}?x={n}',{
param: '123',
foo: '456',
n: 789
})
or
var url = leafletInterpolater.bind(undefined,'http://something.com/{param}/{foo}?x={n}');
url({
param: '123',
foo: '456',
n: 789
});
.replace(/\{([\s\S]+?)\}/g, function($0, $1) {
return '" + o["' + $1 + '"] + "';
})
// =>
.replace(/\{([\s\S]+?)\}/g, '" + o["$1"] + "')
Unless I'm missing something? (In other words, you can use $1
inside the string for replace
to do the same thing, young padolseywan.)
Also, why .replace(/"/g, '\\$&')
and not just .replace(/"/g, '\\"')
? Easier to read.
@qfox -- ah please excuse the verbose code -- tis remnant of legacy iterations. I shall correct it :)
@JamesMGreene: I tried something like this:
replace(/["\r\n]/g, '\\$&')
But since we would then be replacing a literal \n
with \\\n
it won't work. The compiled Function requires the actual literal string \\n
but this cannot be generated in such a meta way without directly writing it AFAIK. (brain silently implodes)
Give it a try though -- I may have missed something very obvious...
@calvinmetcalf AFAICT that runs the replace(...)
at runtime when passing the data, right? My intent with this interpolator was runtime performance, sacrificing creation-time performance, hence the creation of a compiled Function with a straight-up concatenation inside.
@mathiasbynens: tbh I wasn't inspired by RFC6570 -- but looking at it now it seems v. useful. I also found https://github.com/fxa/uritemplate-js
@padolsey FWIW, @rodneyrehm’s URI.js also supports it: http://medialize.github.io/URI.js/uri-template.html
Only special-casing \n
and \r
is not enough. Having \u2028
or \u2029
in the input string still throws an error. Just replace them with their (double-)escaped equivalents and you’ll be fine.
@mathiasbynens: cool, thanks! -- just edited it.
@padolsey I got what you were going for after I read your blog post
Blog post with explanation: http://james.padolsey.com/javascript/straight-up-interpolation/
Really clever code.
P.S. Nice use of URI templates syntax.
P.P.S. I use
$0
,$1
, … as identifiers for arguments toString#replace
callbacks, too! :)