Dynamic Repeated Use of Same Snippet With Different Variables

I've figured out how to do something that's been bugging me for a while, and I thought I'd post it here for others to use or comment on -- is there a better way I could be doing this?

The problem I'm solving here is reusing (by importing) the same snippet repeatedly within another snippet, but each time with different "variables" set and used within the imported snippet. I have a few use cases for this, but in particular is the snippet I use for inserting links. Depending on the context, I need for links to be displayed differently; e.g. in an email, it needs to be a regular HTML link, but on reddit, I need for it to be in markdown format, etc.

First, some necessary contextual info: the following "/blaze_format" snippet sets the text/reddit/markup format and is imported at the top of most of my snippets; it automatically detects the reddit and facebook domains and chooses the correct format accordingly, elsewise it gives a dropdown to select it manually:

{note}{if:{site:domain}=="www.reddit.com"}{format="reddit"}{elseif:{site:domain}=="www.facebook.com"}{format="markup"}{else}{formmenu: name=format;default=text;reddit;markup}{endif}{endnote: trim=yes}

With that in place (in the "parent" snippet), then this "/blaze_link" snippet can generate a link based on the selected "format"; this snippet also depends on the "blaze_url" keyed list variable having been assigned before it's invoked, with the "url" and "title" keys set on it:

{if: format == "text"}{link: {=blaze_url["url"]}}{=blaze_url["title"]}{endlink}{elseif: format == "reddit"}{=blaze_url["title"]}{elseif: format == "markup"}{=blaze_url["title"]} (ref: {=blaze_url["url"]}){endif}

Now, one might think that you could import and use this snippet like this:

{import:/blaze_format}
One link: {blaze_url=["url"="https://linkone.com", "title"="Link One"]}{import:/blaze_link}
Another link: {blaze_url=["url"="https://linktwo.com", "title"="Link Two"]}{import:/blaze_link}

However, this results in two identical links, both of them to Link Two. This is because TextBlaze markup isn't evaluated in a serial fashion, but rather makes several passes; first all the variables are set, then the code using those variables are executed. Later settings of variables override previous ones, so the last-most setting of the "blaze_url" variable is used for all invocations of the imported "/blaze_link" snippet.

Getting around this requires that we get the "import" of the "/blaze_link" snippet to execute within its own scope. AFAIK, the only way to do that with the currently available TextBlaze function is by using the "repeat" function, which can be done like this:

{import:/blaze_format}
One link: {repeat: 1}{blaze_url=["url"="https://linkone.com", "title"="Link One"]}{import:/blaze_link}{endrepeat}
Another link: {repeat: 1}{blaze_url=["url"="https://linktwo.com", "title"="Link Two"]}{import:/blaze_link}{endrepeat}

This places each of the imports of "/blaze_link" in its own scope separate from the overall snippet execution, and results in a link to Link One and to Link Two.

Pretty cool, eh? With this in hand, I can now write snippets like the following, and it will work regardless of context:

{import:/blaze_format}I once visited the page at {repeat: 1}{blaze_url=["url"="https://linkone.com", "title"="Link One"]}{import:/blaze_link}{endrepeat}, but got board and tried out {repeat: 1}{blaze_url=["url"="https://linktwo.com", "title"="Link Two"]}{import:/blaze_link}{endrepeat} before deciding that I like {repeat: 1}{blaze_url=["url"="https://linkthree.com", "title"="Link Three"]}{import:/blaze_link}{endrepeat} the best.

But again, if there's a better way to achieve this result, I'd love to know it.

Of note, my actual implementation of the "/blaze_link" snippet is slightly more complicated, and looks like this:

{isemail=testregex(blaze_url["url"], "([-!#-'+/-9=?A-Z^-~]+(\.[-!#-'+/-9=?A-Z^-~]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@0-9A-Za-z?(\.0-9A-Za-z?)+", "i")}{bold={=catch(blaze_url["bold"], {="no" if format == "markup" else "yes"})}}{italic={=catch(blaze_url["italic"], "no")}}{variant={=catch(blaze_url["variant"], "paren")}}{bracket={=catch(blaze_url["bracket"], "paren")}}{suffix={=catch(concat(" ", blaze_url["suffix"]), "")}}{if: format == "text"}{if: isemail == "yes"}{=catch(concat(blaze_url["title"], ": "), "")}{=blaze_url["url"]}{else}{link: {=blaze_url["url"]}}{repeat: 1}{blaze_markup=["text"={=blaze_url["title"]}, "italic"={=italic}, "bold"={=bold}]}{import:/blaze_markup}{endrepeat}{endlink}{endif}{elseif: format == "reddit"}[{repeat: 1}{blaze_markup=["text"={=catch(blaze_url["title"], blaze_url["url"])}, "italic"={=italic}, "bold"={=bold}]}{import:/blaze_markup}{endrepeat}]({if: isemail == "yes"}mailto:{endif}{=blaze_url["url"]}){elseif: format == "markup"}{if: isemail == "yes"}{=catch(concat(blaze_url["title"], ": "), "")}{=blaze_url["url"]}{else}{if: variant=="colon"}{title=concat({=blaze_url["title"]}, {=suffix})}{suffix=""}{else}{title={=blaze_url["title"]}}{endif}{url_href={=extractregex(blaze_url["url"], "https?://w?w?w?[.]?(.+)")}}{repeat: 1}{blaze_markup=["text"={=title}, "italic"={=italic}, "bold"={=bold}]}{import:/blaze_markup}{endrepeat}{if: variant == "paren"} {if: bracket == "paren"}({else}[{endif}ref: {else}: {endif}{=url_href}{if: variant == "paren"}{if: bracket == "paren"}){else}]{endif}{endif}{endif}{endif}{=suffix}

This provides the "variant" and "bracket" options, allowing me to change the format for "markup" style formatted links; e.g.

{import:/blaze_format}
{repeat: 1}{blaze_url=["url"="https://emanaton.com", "title"="Emanaton Dot Com", "bracket"="square"]}{import:/blaze_link}{endrepeat}
{repeat: 1}{blaze_url=["url"="https://csmaccath.com", "title"="CSMacCath Dot Com", "variant"="colon"]}{import:/blaze_link}{endrepeat}
{repeat: 1}{blaze_url=["url"="https://www.youtube.com/watch?v=3HAMk_ZYO7g", "title"="This video", "variant"="colon", "suffix"="addresses that question nicely"]}{import:/blaze_link}{endrepeat}{if: format <> "markup"}.{endif}

Which outputs links like so (when "markup" is selected):
Emanaton Dot Com [ref: https://emanaton.com]
CSMacCath Dot Com: https://csmaccath.com
This video addresses that question nicely: youtube.com/watch?v=3HAMk_ZYO7g

2 Likes

Awesome Sean! Nice use of {repeat} to create a separate scope.

It's not available yet, but long term we expect your use case to be solved well by command packs (see, February Development Update). These are in an early stage currently and for now TB users can use but not develop them. Longer term though we want to open them up command pack development to everyone.

Technically, command pack commands are like a more powerful {import}. Each command pack command is just a snippet but it has its own scope and there is a well-defined interface to configure them and pass variables between the command and the enclosing snippet.

2 Likes

YES! This will be brilliant, and I look forward to them -- thanks for letting me know, @scott!

1 Like