Pardon Templates
Pardon’s template/schema engine is the foundation of how Pardon understands and builds requests. The engine’s main operation is “matching”, but the logic includes both conflict detection and progressive enhancement.
The composition of multiple (compatible) templates forms a “schema” But we’ll defer the discussion of exactly what schemas represent and how they are composed for later, because we can use basic templates without understanding them.
Templates
Pardon’s templates are HTTP-like text, but commonly they represent, in part, JSON requests.
But to understand how pardon handles the overall request object we need to first look at how it would operate on a single (string) value in a JSON request.
A “simple” pattern (where the entire string is a single interpolated value)
can match simple values like strings (text), matching these two (in either order)
would be allowed, and additionally resolve name
to "some value"
.
Pardon also allows simple templates to match numbers as values,
here we resolve the value name
to 123
(as a number rather than as a string).
We can also have non-simple templates, a non-simple template can also break/compose a string (this only makes sense for strings, of course).
If multiple templates for the same value are combined, Pardon will try
to resolve everything. Here "hello world"
provides a value for the template,
and so we can resolve both the "{{value}}"
and the "{{greeting}} {{planetoid}}"
template against that.
(This makes sense because /^(.*) (.*)$/
can only match one way).
This resolves the three variables as one might expect.
Attempting to extend/merge an incompatible template fails: Pardon recognizes these conflicts.
Pardon would also reject this combination, as the template would match the regular expression /^(.+) (.+)$/
and "hello"
is missing a space.
Rendering Templates
All by itself, the template "{{hello}}"
cannot be rendered without
providing some value for hello
. If this template were matched against the text "world"
, then
hello
would be resolved to the value "world"
.
The value for hello
could also be provided externally as a value.
We can also provide expressions (javascript!) in templates, which will be evaluated if no value could be resolved.
But these expression-values match as normal, skipping evaluation.
Once a value is resolved (through matching or script evaluation), it can be referenced in other parts of the same template or as values in scripts.
For example, consider the following template and it’s default output
If we merge this schema we can get different results
If we specify the value of a
we resolve hello="planet"
which becomes
and the expression for A
uses it instead of the default.
Alternatively, we can resolve both values which skips any expression evaluation here.
If we only specify A
, then a
retains its default expression and A
gets
overridden.
Pardon can handle any evaluation order that does not result in a cycle (including asynchronous / promise expressions).
Exploring a template
Moving on to a (slightly more) real example: a REST API that creates products with a name and a price.
This is parsed into a structure and applied to a template defined roughly as the following
Anyway, let’s explore how pardon behaves with this template with these quick exercises.
Try updating the name or price values. Note that since
price is a javascript number value, the rendering of e.g., "price": 9.00
will
become "price": 9
.
Conversely, providing arrays or objects for these fields will currently confuse Pardon. (note: I have not decided if this is a bug or just how things work.)
Other fields can be added freely into the template.
We can also add query params and pardon can integrate it into the request.
Headers and query params can also be added, the base template does not restrict additional data so long as the additional data can be merged in.
We’ve covered the basics of how a template supports building a request. Feel free to explore negative cases as well: change the URL path or origin, remove fields from the request, etc… and watch for when pardon can no longer match the template () or render it ().
Scripted values
Expressions can use other values from the template, either resolved through matching or evaluated in other expressions.
For example, since we have a value for {{name}}
matched by the template, we
can use the name
value in an expression for another field.
"pardon‽"
. You can see the price
value change automatically as
you add or remove letters from the name value.
Inflation has dropped the demand for luxury letters: we need to make a drastic price-cut to maintain our order flow rates, price is now 25 cents a letter.
That didn’t drive orders enough and management is desperate: we need to highlight our payment plan offering:
(note that parameter expressions don’t need a name, we just start with {{=
…)
Next Steps
A thorough discussion of the pattern syntax.
Exploring a small collection of endpoints for our service example.
An explanation of how templates are composed into schemas.