Table of Contents
- Introduction
- Using the Library
- Template Expression Syntax
- Built in Objects and Members
A Dart library to process string based templates using expressions.
Add the repo to your Dart pubspec.yaml
file.
dependencies:
template_expressions: <<version>>
Then run...
dart pub get
The template engine supports different syntax options for how the expressions are discovered within the template. There are three built in syntax options as described below. To provide your own syntax, simple implement the ExpressionSyntax class and pass that to the template at construction.
The hash expression syntax begins and ends with a double hash symbol. This syntax is compatible with many different forms of code and text file templates without much need for escape characters.
Example
{
"firstName": "##firstName.toUpperCase()##",
"lastName": "##lastName.toUpperCase()##"
}
The mustache expression syntax begins with a double open curly brace and ends with a double close curley brace. This syntax is relatively common as it is a highly simplified version of the mustache template. Only the double curly braces are supported, no other aspects of the mustache syntax are.
Example
{
"firstName": "{{firstName.toUpperCase()}}",
"lastName": "{{lastName.toUpperCase()}}"
}
The pipe expression syntax begins and ends with a single pipe symbol. This syntax is compatible with many different forms of code and text file templates without much need for escape characters.
Example
{
"firstName": "|firstName.toUpperCase()|",
"lastName": "|lastName.toUpperCase()|"
}
The standard expression syntax follows the Dart string interpolation pattern. This is the default syntax all templates will use unless a separate syntax list is provided. It is the default as it is likely to be the most familiar with Dart developers, however it also has some conflicts that require special escaping. In Dart code, either the dollar sign must be escaped \${...}
or the string must be tagged as a regular string (r'...'
). In all forms, if there is a map defined in the expression, the close curly braces must be escaped like: r'${createName({"firstName": "John", "lastName": "Smith"\})}
Example
{
"firstName": "${firstName.toUpperCase()}",
"lastName": "${lastName.toUpperCase()}"
}
The Codex class is supported for encoding and decoding values.
base64.encode(value)
base64url.encode(value)
hex.encode(value)
json.encode(value)
utf8.encode(value)
base64.decode(value)
base64url.decode(value)
hex.decode(value)
json.decode(value)
utf8.decode(value)
Function | Example |
---|---|
decode | ${base64.decode(value)} |
encode | ${base64.encode(value)} |
The Crypto functions exist for things like md5
, sha256
, sha512
, and hmac
functionality. Each function returns a lower case HEX encoded string of the resulting hash.
md5(string)
hmac(secret, message) // synonym to hmac256
hmac256(secret, message) // synonym to hmac
hmac512(secret, message)
sha(string) // synonym to sha256
sha256(string) // synonym to sha
sha512(string)
The DateFormat class is supported for parsing and formatting functions.
DateFormat(String format)
Function | Example |
---|---|
format | ${DateFormat('yyyy-MM-dd').format(now())} |
parse | ${DateFormat('yyyy-MM-dd').parse('2022-01-01')} |
parseUTC | ${DateFormat('yyyy-MM-dd').parseUTC('2022-01-01')} |
parseUtc | ${DateFormat('yyyy-MM-dd').parseUtc('2022-01-01')} |
The DateTime class is supported for performing date time related functionality.
now()
DateTime(int utcMillis)
DateTime(int year, int month, [int date, int hour, int minute, int second, int millisecond])
DateTime({int year, int month, int date, int hour, int minute, int second, int millisecond})
DateTime(List<int> yearMonthDateHourMinuteSecondMillisecond)
Function | Description | Example |
---|---|---|
now | Alias for the dart code of DateTime.now() |
${now()} |
Function | Example |
---|---|
add | ${now().add(minutes(5))} |
add(int millis) | ${now().add(30000)} |
compareTo | ${now().compareTo(other)} |
format(String pattern) | `${now().format('yyyy-MM-dd')} |
isAfter | ${now().isAfter(other)} |
isBefore | ${now().isBefore(other)} |
isUtc | ${now().isUtc} |
millisecondsSinceEpoch | ${now().millisecondsSinceEpoch} |
subtract | ${now().subtract(minutes(5))} |
subtract(int millis) | ${now().subtract(30000)} |
toIso8601String | ${now().toIso8601String()} |
toLocal | ${now().toLocal()} |
toUtc | ${now().toUtc()} |
The Duration class is supported for duration related calculations.
now()
Duration(int milliseconds)
DateFormat(int days, int hours, [int minutes, int seconds, int milliseconds])
DateFormat({int days, int hours, [int minutes, int seconds, int milliseconds})
DateFormat(List<int> daysHoursMinutesSecondsMilliseconds)
Function | Description | Example |
---|---|---|
days(int value) |
Alias for Duration({"days": value}) |
${days(5).inMilliseconds} |
hours(int value) |
Alias for Duration({"hours": value}) |
${hours(5).inMilliseconds} |
milliseconds(int value) |
Alias for Duration({"milliseconds": value}) |
${milliseconds(5000).inSeconds} |
minutes(int value) |
Alias for Duration({"minutes": value}) |
${minutes(5).inMilliseconds} |
seconds(int value) |
Alias for Duration({"seconds": value}) |
${seconds(5).inMilliseconds} |
Function | Example |
---|---|
add(Duration duration) |
${Duration(1000).add(minutes(5))} |
add(int milliseconds) |
${Duration(1000).add(1000)} |
compareTo | ${Duration(1000).compareTo(other)} |
inDays | ${Duration({"hours": 48}).inDays} |
inHours | ${Duration(30000).inHours} |
inMilliseconds | ${Duration(30000).inMilliseconds} |
inMinutes | ${Duration(30000).inMinutes} |
inSeconds | ${Duration(30000).inSeconds} |
subtract(Duration duration) |
${Duration({minutes: 5}).subtract(seconds(5))} |
subtract(int milliseconds) |
${Duration({minutes: 5}).subtract(5000)} |
The Encrypt functions exist for AES
and RSA
cryptography functions.
AES().key(key).encrypt(plainText)
AES().key(key).decrypt(encrypted)
RSA().publicKey(publicKey).encrypt(data).toBase64()
RSA().privateKey(privateKey).decrypt(data)
RSA().privateKey(privateKey).sign(data).toBase64()
RSA().publicKey(publicKey).verify(data, signature)
Class | Function | Param(s) | Returns | Description |
---|---|---|---|---|
AES |
key |
(String | Uint8List | List<int> | SecureRandom : key) |
AES |
Sets the secret key on the AES object. If the input is a String, it must be base64 encoded. |
AES |
iv |
(String | Uint8List | List<int> | IV : iv) |
AES |
Sets the IV on the AES object (should only be used for testing, for production code always use a generated IV and never a common one). If the input is a String, it must be base64 encoded. |
AES |
decrypt |
(String : encrypted) |
List<int> |
Decrypts the value. Either the IV must be prior to calling this or it must be passed in base64 encoded at the start of the string followed by a colon and then the encrypted string. The encrypted string must also be base64 encoded |
AES |
encrypt |
(String | Uint8List | List<int> : unencrypted) |
String |
Encrypts the passed in value, prepends the base64 encoded IV + : and returns the base64 encoded encrypted value. |
RSA |
publicKey |
(String | RSAPublicKey : publicKey) |
RSA |
Accepts a String encoded PEM file or a public key object and sets it on the RSA object. |
RSA |
privateKey |
(String | RSAPrivateKey : privateKey) |
RSA |
Accepts a String encoded PEM file or a private key object and sets it on the RSA object. |
RSA |
decrypt |
(String |
List<int> : encrypted) |
Decrypts the value. This works by reversing the values from the RSA.encrypt . |
RSA |
encrypt |
(String | Uint8List | List<int> : unencrypted) |
String |
This is a multi-step process. Either an AES object must have already been passed in, or a new one with a random key and random IV will be created. The key from the AES object will be encrypted using the RSA Public Key, base64 encoded, and added to the resulting key. Next the result from encrypting the value using AES will be appended to the returned string. The resulting string is: ${rsaEncryptedAesKey}:${aesIV}:${aesEncryptedValue} . |
RSA |
sign |
String | Uint8List | List<int> |
List<int> |
Signs the given message and returns the bytes list. |
RSA |
verify |
(String | Uint8List | List<int> : message, String | Uint8List | List<int> : signature) |
List<int> |
Signs the given message and returns the bytes list. |
Several member functions from the Iterable class are supported.
Function | Example |
---|---|
contains | ${value.contains('string')} |
elementAt | ${value.elementAt(1)} |
first | ${value.first} |
isEmpty | ${value.isEmpty ? 'null' : value.first} |
isNotEmpty | ${value.isNotEmpty ? value.first : 'null'} |
last | ${value.last} |
length | ${value.length} |
join | ${value.join(',')} |
single | ${value.single} |
skip | ${value.skip(1).join(',')} |
take | ${value.take(3).join(',')} |
toList | ${value.toList().sort()} |
toSet | ${value.toSet().first} |
In addition to the items supported by the Iterable class, a List additionally supports the following functions...
Function | Example |
---|---|
asMap | ${list.asMap()[2]} |
path(String jsonPath) | Applys the JSON path to return the first matching value. |
reversed | ${list.reversed.first} |
toJson([int padding]) | ${list.toJson(2)} |
sort | ${list.sort().first} |
Additionally, if the list is a List<int>
or a Uint8List
then there are additional helper functions that can be used:
Function | Description | Example |
---|---|---|
toBase64 | Base64 encodes the byte array | ${list.toBase64()} |
toHex | Hex encodes the byte array | ${list.toHex()} |
toString | UTF8 encodes the byte array | ${list.toString()} |
The JsonPath class is supported to allow for walking JSON-like values.
JsonPath(String expression)
Function | Description | Example |
---|---|---|
json_path(dynamic value, String path) |
Alias for JsonPath(path).read(value).first.value |
${json_path(object, '$.person.firstName')} |
Function | Example |
---|---|
read | ${JsonPath('$.person.firstName').read(obj).first.value} |
readValues | ${JsonPath('$.person.firstName').values(obj).first} |
The JsonPathMatch class is supported to allow for walking JSON-like values. It is unlikely you will want to create this class yourself and it is expected it will come from using JsonPath.
Function | Example |
---|---|
parent | ${JsonPath('$.person.firstName').read(obj).first.parent.value} |
path | ${JsonPath('$.person.firstName').read(obj).first.path} |
value | ${JsonPath('$.person.firstName').read(obj).first.value} |
The following Map members are supported.
Function | Example |
---|---|
containsValue | ${map.containsValue('value')} |
keys | ${map.keys.first} |
isEmpty | ${map.isEmpty ? 'null' : map.values.first} |
isNotEmpty | ${map.isNotEmpty ? map.values.first : 'null'} |
length | ${map.length} |
remove | ${map.remove('key')} |
path(String jsonPath) | Applys the JSON path to return the first matching value. |
toJson([int padding]) | ${map.toJson(2)} |
values | ${map.values.first} |
The following MapEntry members are supported.
Function | Example |
---|---|
key | ${entry.key} |
value | ${entry.value} |
The following num members are supported.
Function | Example |
---|---|
abs | ${number.abs()} |
ceil | ${number.ceil()} |
ceilToDouble | ${number.ceilToDouble()} |
clamp | ${number.clamp(lower, upper)} |
compareTo | ${number.compareTo(other)} |
floor | ${number.floor()} |
floorToDouble | ${number.floorToDouble()} |
isFinite | ${number.isFinite} |
isInfinte | ${number.isInfinte} |
isNaN | ${number.isNaN} |
isNegative | ${number.isNegative} |
remainder | ${number.remainder(other)} |
round | ${number.round()} |
roundToDouble | ${number.roundToDouble} |
sign | ${number.sign} |
toDouble | ${number.toDouble()} |
toInt | ${number.toInt()} |
toStringAsExponential | ${number.toStringAsExponential(fractionDigits)} |
toStringAsFixed | ${number.toStringAsFixed(fractionDigits)} |
toStringAsPrecision | ${number.toStringAsPrecision(precision)} |
truncate | ${number.truncate()} |
truncateToDouble | ${number.truncateToDouble()} |
The random
function will operate in two modes. If a number is passed in, it will return a random integer between 0 and that number - 1. Otherwise, it will return a random double that is greater or equal to 0 and less than 1.
random(100) // returns 0 - 99
random() // returns >= 0 < 1
The following String members are supported.
Function | Example | Description |
---|---|---|
compareTo | ${str.compareTo(other)} |
|
contains | ${str.contains('other')} |
|
decode | ${str.decode()["firstName"]} |
Decodes the string first trying as JSON and second as YAML to convert it to a map or a list |
endsWith | ${str.endsWith('other')} |
|
indexOf | ${str.indexOf('other')} |
|
isEmpty | ${str.isEmpty ? 'null' : str} |
|
isNotEmpty | ${str.isNotEmpty ? str : 'null'} |
|
lastIndexOf | ${str.lastIndexOf('/')} |
|
length | ${str.length} |
|
padLeft | ${str.padLeft(2, ' ')} |
|
padRight | ${str.padRight(2, ' ')} |
|
path(String jsonPath) | ${str.path($.firstName)} |
Attempts to decode the string using JSON or YAML and then applys the JSON path to return the first matching value. |
replaceAll | ${str.replaceAll('other', 'foo')} |
|
replaceFirst | ${str.replaceFirst('other', 'foo')} |
|
split | ${str.split(',').join('\n')} |
|
startsWith | ${str.startsWith('other')} |
|
substring | ${str.substring(begin, end)} |
|
toBool | ${str.toBool()} |
Converts the string to a bool . The result will be true if and only if the lower case version of the string equals the value of "true" . |
toDouble | ${str.toDouble()} |
Attempts to convert the string into a double . Should the parsing fail, the result will be null . |
toInt | ${str.toInt()} |
Attempts to convert the string into an int . Should the parsing fail, the result will be null . |
toLowerCase | ${str.toLowerCase()} |
|
toLowerCase | ${str.toLowerCase()} |
|
toLowerCase | ${str.toLowerCase()} |
|
toUpperCase | ${str.toUpperCase()} |
|
trim | ${str.trim()} |
|
trimLeft | ${str.trimLeft()} |
|
trimRight | ${str.trimRight()} |
Function | Description | Example |
---|---|---|
decode |
Attempts to decode the string using both a JSON and a YAML parser. If either is successful, the resulting map or list is returned. | ${str.decode()["firstName"]} |
The following Object members are supported.
Function | Example |
---|---|
hashCode | ${obj.hashCode} |
runtimeType | ${obj.runtimeType.toString()} |
toString | ${obj.toString()} |