Inject

inject is a special type of flow that allows you to include the content of one blueprint into another.

Sometimes specific parts of code or just generated content in general repeat and the same code is required to produce the result at multiple places. In programming languages, this unnecessary duplication would be solved using functions (which you can do as well using javascript extensions), however, in Pulsar, you can solve it using nested blueprints.

You can think about any blueprint as a function that generates content (ultimately produces string as a result). Using inject, you can invoke any blueprint to provide its content.

Syntax

Basic syntax of inject flow is as follows:

{[ inject [blueprintId] context [dataToPass] /]}

The inject takes blueprint referenced by blueprintId and invokes it with extra data passed by dataToPass argument.

Basic inclusions

The simplest case is including static content. Static content doesn't take any data and you don't have to worry about passing anything to it. For example, let's define a header that we want to include on every page:

header.pr
// 
// Project: Top Secret!
//

This blueprint has no dynamic data and will simply insert extra text when injected. Using the following blueprint, we can include a header to any other blueprint within the same exporter.

{* Include header in this page *}
{[ inject "header" context self /]}

{* Generate content for this specific blueprint *}
My content

We've referenced another blueprint using its ID and passed self as a data. As a result, both blueprints will get merged into one, resulting in the following output:

//
// Project: Top Secret!
//

My Content

self is a special variable that references the current data stack - meaning you are passing exactly the same data to the injected blueprint as you have available at the time of invocation.

But because the header blueprint doesn't use any of the data, we don't have to worry about it.

Passing and using data inside the injected blueprint

In most cases, including static content is not enough. For example, let's assume we are building an HTML exporter and need to always convert some data structure (like a color object) into a raw code representation. We'll likely need that conversion on hundreds of places - and so we can write a blueprint for that conversion and then just reference the blueprint, the advantage being that you can just modify the blueprint, and the result will change on all the places.

In this case, we will need to pass the actual data to the injected blueprint. First, let's create a blueprint that will serve for that conversion:

{* Create rgb or rgba representation based on alpha value
{[ if @equals(alpha, 1) ]}
rgb({{ red }}, {{ green }}, {{ blue }})
{[ else ]}
rgb({{ red }}, {{ green }}, {{ blue }}, {{ alpha }})
{[/]}

Now everywhere we use this blueprint, either rgb or rgba representation will be used. However, as you have noticed, we are using red, green, blue and alpha values that were never defined in this blueprint. We achieve that by passing the data at the point of injection:

text-color: {[ inject "conversions.color" context color ]}

Instead of using self as a data attribute, we will provide another dictionary containing the data we need, in this case color. color would be a dictionary containing the following data:

"color": {
   "red": 1,
   "green": 1,
   "blue": 1,
   "alpha": 1
}

At the point of injection, the entire content of the provided dictionary is converted into variables that you can use within the blueprint. Note that all variables defined by injecting the blueprint are immutable.

Last updated