Lightweight, fast and easily extensible string interpolation engine
Template literals, a.k.a. template strings, string interpolation, or sometimes simply called expressions, are part of ECMAScript for development, but can not be parsed during runtime. The Pythoneers amongst you might know this paradigm as String formats or "F-Strings".
I created this tiny project to read (and parse) collections from JSON files that contained simple conditionals. As my architecture grew, I started using the same expressions for almost anything – even to query several databases. Keeping one simplistic language throughout my ecosystem simplified both development and maintenance for me.
Install using npm:
$ npm install micro-expressions
Newly created MicroExpressions take one argument: the expression
.
The input is being parsed automagically and turned into functions that take a data container as argument for rendering.
const { MicroExpression } = require('micro-expressions');
const data = {
name: 'Dan',
foo: {
bar: 4
},
group: ['Vlad', 'Bartmoss'],
test: 8
};
console.log( new MicroExpression("foo.bar").render_with(data) );
// outputs 4
console.log( new MicroExpression("foo.bar < 3").render_with(data) );
// outputs false
console.log( new MicroExpression("foo.bar >= 4").render_with(data) );
// outputs true
console.log( new MicroExpression("foo.bar!=test").render_with(data) );
// outputs true
console.log( new MicroExpression("name in group").render_with(data) );
// outputs false
console.log( new MicroExpression("Vlad in group").render_with(data) );
// outputs true
Use render_with( data_container )
to render all templates using data_container
as context.
new MicroExpression(expression).render_with( data_container );
As MicroExpression is using lodash's get helper, it supports object paths in dot notation:
new MicroExpression('foo.bar < 10').render_with({ foo: { bar: 4 } });
To create micro expressions with your own set of operators, you can extend the MicroExpression class and overwrite the default behavior by declaring an override_operators()
and/or a get( expression )
method.
const { MicroExpression } = require('micro-expressions');
class MyExpression extends MicroExpression {
override_operators() {
...
}
get(expression) {
...
}
}
The override_operators
method takes no arguments, but must return an object with the operators as keys and according callbacks as values. The callback takes 2 arguments: left
and right
, representing both sides of the operator. If your operator e.g. doesn't have a left side, you must still declare a left parameter, but can ignore the passed argument.
{
'!': function(left, right) {
return !right;
}
}
The get
method takes one argument: expression. All operands are passed to this method before the entire expression is being evaluated.
In its simplest form, you can override get
like so:
const { MicroExpression } = require('micro-expressions');
class SQLExpression extends MicroExpression {
get(expression) {
return data => data[expression] || expression;
}
}
You can declare a method on_render
that gets passed the current rendering results as single argument.
This package comes with a set of standard JEST cases located in [tests/all.test.js][]
Run them using:
$ npm test
In the following example, I used the MicroExpression class to parse conditions loaded from a JSON file:
[steps.json]
[
{
"if": "answer=yes",
"message": "affirmative."
},
{
"if": "answer=no",
"message": "negative."
}
]
[index.js]
let message = 'maybe.';
const steps = require('./steps.json');
const answer = 'no';
_.each(steps, (step, i) => {
const condition = new MicroExpression(step.if);
if(condition.render_with({ answer })) {
message = step.message;
}
});
console.log('The outcome is', message);
The output will be:
The outcome is negative
Here is an example of extending MicroExpression:
class MyExpression extends MicroExpression {
override_operators() {
return {
'=': (left, right) => left === right,
'!=': (left, right) => left !== right,
'!': (left, right) => !right,
'~': (left, right) => Math.abs(left - right) < 10
}
}
}
Make sure you check out the subclass MongoExpression and the tests to see examples of implementation.