I have been feeling a bit blocked — creatively — currently. So, I have been attempting to maintain my thoughts lively with a bit inside house-cleaning on my weblog infrastructure. One of many issues that my weblog has to do is generate search engine optimized (search engine optimization) URLs based mostly on semantic segments. For instance, the deep-link to a weblog submit appears one thing like this:
/weblog/123-coldfusion-is-my-jam.htm
This useful resource is only a concrete implementation of this template:
/weblog/{id}-{slug}.htm
At present, I am hand-coding these URLs in every single place (with some minor efficiencies). Then, I am utilizing common expression patterns to parse these URLs on every request with the intention to route incoming URLs again to the proper ColdFusion Controllers.
There’s plenty of room for enchancment; and one of many first issues I am occupied with it the right way to generate these value-interpolated search engine optimization URLs utilizing centralized mechanics.
Utilizing Java’s Sample Matcher
As with all issues “sample” associated, my thoughts instantly went to common expressions. And in terms of parsing with common expressions (RegEx), there’s nothing fairly as elegant as Java’s Sample and Matcher courses. The truth is, I’ve created a complete ColdFusion library that does nothing greater than wrap these courses (see my ColdFusion JRegEx repository).
As a primary thought, I figured I may outline a pattern-based URL like:
/weblog/{{id}}-{{slug}}.htm
After which create a way that generates a “stamping” operate — ie, returns a closure — that can output interpolated values when given a dictionary of tokens to make use of. The stamping operate is generated utilizing a templateNew() operate, which is meant to supply this as a generic mechanic:
<cfscript>
// ColdFusion language extensions (world features).
embrace "/core/cfmlx.cfm";
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
stamp = templateNew( "Good morning {{identify}}, I hope you {{assertion}}!" );
dump(
label = "Utilizing Java Sample Matcher",
var = [
stamp({ name = "Sarah", assertion = "have a great day" }),
stamp({ name = "Laura", assertion = "stay cool" }),
stamp({ name = "Jason", assertion = "keep on keeping on" }),
]
);
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I return a operate that may stamp-out the given template, interpolating the phrases
* from the offered dictionary into the template string. Placeholders are within the kind
* of `{{token}}`. Any token lacking from the dictionary will likely be interpolated as an
* empty string.
*/
personal string operate templateNew( required string templateSource ) {
return ( required struct dictionary ) => {
var matcher = new java( "java.util.regex.Sample" )
.compile( "{{([a-zA-Z0-9_$]+)}}" )
.matcher( templateSource )
;
var buffer = new java( "java.lang.StringBuffer" )
.init()
;
whereas ( matcher.discover() ) {
var substitute = toString( dictionary[ matcher.group( 1 ) ] ?? "" );
matcher.appendReplacement(
buffer,
matcher.quoteReplacement( substitute )
);
}
matcher.appendTail( buffer );
return buffer.toString();
};
}
</cfscript>
On this ColdFusion code, I am producing one template “stamper” operate, after which I am stamping-out three totally different variations of the template utilizing three totally different enter dictionaries. And, once we run this CFML code, we get the next output:
The good factor concerning the Java Sample and Matcher courses is that you just get actually fine-grain management over the way you traverse and change sample matches. On this case, I haven’t got any lacking tokens; however you’ll be able to see from the logic that any lacking token ends in an empty string due to the null coalescing operator (??).
Utilizing ColdFusion’s Native Interpolation
After I completed the Java-based strategy, I had an epiphany — ColdFusion has easy string interpolation mechanics already! Actually, the one factor that I am attempting to do is defer the analysis of the string template. Which is strictly what we’re already doing with our templateNew() manufacturing unit operate – it is producing a closure that defers analysis.
If I simply make the entire course of much less generic, I can outline the closure in the identical place that I am defining the string template. And as an alternative of utilizing {{token}} patterns, I can use CFML string interpolation straight, #token#:
<cfscript>
// ColdFusion language extensions (world features).
embrace "/core/cfmlx.cfm";
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// A easy closure which defers analysis of the template string.
stamp = () => "Good morning #identify#, I hope you #assertion#!";
// Word: on this model, we're not passing in a "dictionary" - we're leaning on the
// arguments scope (of named parameters) as our dictionary for interpolation.
dump(
label = "Utilizing ColdFusion Closure",
var = [
stamp( name = "Sarah", assertion = "have a great day" ),
stamp( name = "Laura", assertion = "stay cool" ),
stamp( name = "Jason", assertion = "keep on keeping on" ),
]
);
</cfscript>
On this case, stamp() is a ColdFusion closure; and the one factor that this closure is doing is returning an interpolated string. Once I invoke the stamp() operate this time, I am not passing in a dictionary, I am simply utilizing named arguments. Then, when ColdFusion evaluates the string template, it should search by means of the native scope after which the arguments scope for the interpolated variables. Which is how my named arguments find yourself within the interpolated output:
As you’ll be able to see, we get the very same output with the CFML string interpolation as we do with the Java Sample / Matcher strategy. Besides solely a fraction of the code was required.
All Options Are Commerce-Offs
Each of the approaches above generated the very same output. However, to be clear, the 2 approaches aren’t performance equal. The primary strategy is invoked with a dictionary of tokens; and is resilient to a lacking token, falling again to the empty string.
The second strategy is invoked with named arguments as tokens; and can throw a lacking variable error if a token is lacking; or worse, it’d unintentionally pull a lacking token out of the father or mother variables scope.
All options are a trade-off, usually between how generic and reusable one thing is vs. how resilient it’s to failure vs. how simple it’s to make use of and invoke. There is not any “one proper” reply; and it typically has to do with how giant the “blast radius” of your decisions are.
That mentioned, in case you’re utilizing ColdFusion, you are already making good decisions. At that time, your options are simply totally different shades of superior!
Need to use code from this submit?
Take a look at the license.
https://bennadel.com/4906

