There are three kinds of expressions available in different contexts:
$send$expect or $when`$ifThese are used when you're computing values to pass to messages and generally work as you would expect. Here are some examples. See more working tests with supported expressions at expr story++ and expr spec++.
The expressions are meant to feel natural, so simple stuff like 1 + 2 > 5 works as expected (yeah, false). You can have named operators, such as 1 contains 5, as the language pretends to be human friendly, for instance $expect (result is number).
Also, you can throw in JS expressions any time you need extra expression power or access to basics already provided there, by using a js: prefix, see examples below. Note that this is best avoided, as JS is much slower.
Associativity is left to right and precedence is as follows:
*,/+,-,||,|>,<,>=,<=,==,!=,~=,~path,?=,is,not,in,notIn,contains,containsNotand,or,xormap,flatMap,flatten,filter,exists,foldas1 + 2 is 3 works ok, but 1 > 2 is false doesn't in some contexts, although you may have expected it to... so when outside the bounds of normal, either stay conservative and don't skimp on brackets (1 > 2) is false and/or double-check things in the fiddle - it's super-easy to!
This is a quick reference guide for expressions - there are lots of examples with comments in expr story++.
// constants and values
$send ctx.set (
constants = 321,
addition = last+first,
jsonBlock={
"accountNumber": "1729534",
"start_num": 123
},
qualified = jsonBlock.accountNumber,
interpolation="is${1+2}",
url="${HOST}/search?q=dieselapps",
builtInFunctions1 = sizeOf(x=cart.items),
builtInFunctions2 = typeOf(x=cart.items),
anArray = [1,"a",3],
anotherArray = [5 .. (99/2)], // will generate an array easily
elem1 = anArray[1],
// json operations
cart=cart || {id:customer, items:[] } ,
cart=cart+{items:{sku : sku, quantity : quantity} } ,
sku1 = cart["items"].sku,
sku2 = cart[someVariableFieldName].subfield,
// embedded Javascript expressions
simpleJs = js:wix.diesel.env ,
res78=js:cart.items[0].sku ,
res40=js:email.replace(/(\w+)@(\w+).com/, "$1") ,
later=js:{var d = new Date(); d.setSeconds(d.getSeconds() + 10); d.toISOString();} ,
a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1),
a285 = [1 .. sizeOfStuff] map (x=> x + 1),
isThere = x in [1,2,3],
notThere = x not in [1,2,3],
size = size || 4 // JS style if defined OR default. WARN: Do not use || for "or", use "or" for boolean expr
)
$when something.hapenned
=> $if (AUTH ~= "Basic[^ ].*") (AUTH = "Basic " + AUTH[5..])
$when query.elk (search, size?) // add _id to the set of results from an elasticsearch query
=> elk.query(index=TABLE, search, size = size || 9)
=> (payload = payload.hits.hits map (z => z._source.report + {_id:z._id} ))
The built-in types understood are:
booleannumberstringjsonarraydateFor each of these types, the operators behave in a straight forward manner.
The expressions are type-aware. Common sense type casts are done (string plus number is a string).
Use as to force a typecast 5 as string.
Use typeOf to get the type of something.
Use is or not to check the type of something age is Number.
Custom types and classes can be defined and used, see Domain Modelling. All defined types are derived from Json and look at feel like Json.
Strings are simple constants, with escaping and natural inline expressions:
\b, \n, \t, \r, \f\ is escaped, and not interpreted
\\, \", \'\ is escaped"${name}" is expanded - note that the curly braces are mandatory.
"$${name}" is not expanded - it's escapedUse """ to delimitate multi-line strings - same rules above apply. This is quite good for simple templates, as interpolation works anywhere.
Arrays can be statically defined, generated or come from a function/method:
$val arr1 = ["345", "asdf"]
$val arr2 = [5 .. 8]
$val arr3 = split (a="1,2,3,4", separator=",")
You can test if an element is in or not in an array:
$when test.diesel.isin(x in ["123", "234"], y not in [1,2])
Dates are ISO format, there is a built-in function now() and the operators work intuitively. Here are some examples of date-related expressions. This is equivalent to this js expression: js:new Date().toISOString().
$var date = now()
$var future = now() + "5 seconds"
$var sameFuture = now() + "5s"
$var past = now() - "5s"
$val millis = toMillis(a=now())
$val alsoNow = millis as Date
Operators:
+ you can add a date with a duration- you can subtract a date with a duration==, > etc)Conditions are used in $ifc and here are some examples:
=> if (not false) (b=b+1, a02)
=> if (not a ~= b ) (b=b+1, a07)
=> if ( a < b or a > b ) (b=b+1, a12)
=> if ( a < b and (a > b or a < b) ) (b=b+1, a13)
There is also another if, using the match expressions: $ifm. The default for $if is the $ifc with boolean expressions.
See expr story++, which is a live test of all expressions supported.
Besides the regular comparison operators, we have these.
$when test.diesel.isin(x in ["123", "234"], y not in [1,2])
$expect (["123", "234"] contains x, [1,2] containsNot y)
Note that you can't use contains with $when - the limitation there is that the named parameter has to be first element in the expression.
These are type-aware:
String they simply look if the string contains or not the substringArray they compare the entire elementJSON or Map, they look if there is a key with the nameComplex types can be defined and used like so:
$class DieselTestStudent (name, address:DieselTestAddress)
$class DieselTestAddress (street, no)
$send ctx.echo(m = new DieselTestStudent {
name: "John",
address: new DieselTestAddress {
street: "Baker St",
no: "121b"
}
})
See Domain Modelling. All defined types are derived from Json and look at feel like Json.
map will work on objects too - it takes a function with one argument which will be an object with two attributes: key and value:
$val query={key:"elf", age:1000}
$send ctx.set (y = query map (x => x.key + ":\"" + x.value + "\""))
$send ctx.echo (x = y mkString " AND ")
Objects are JSON and are used much like a sensical Javascript would:
$val x = {a:1,b:"asdf"}
$val y = {a:1,b:"asdf"}
$send ctx.echo(z = x + y) // merge two objects
$val n = x.a
Arrays (or lists) are dynamic in size and look and feel much like in JS:
a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)
x in [1,2]
x not in [1,2]
[3,4] contains y
[1,2] containsNot y
sizeOf([1,2,3])
The array operations are: map,flatMap,flatten,filter,exists. The right side must be a lambda function:
map will transform the valuesflatten will flatten an array of arrays into a single arrayflatMap transform and flattenfilter keeps only the items meeting the predicateexists true if an element meets the criteriamkString takes a right-hand constant and will create a nice stringfold start from payload and reduce the array using the lambdaindexBy turn an array of objects into an object indexed by a key a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)
a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)
$send ctx.set (payload = 0)
$send ctx.set (z51 = [1,2,3,4] fold x => (payload + x))
$expect (z51 is 10)
$val z = (activeAlarms indexBy x => x.deviceId)
$val zz = (activeAlarms indexBy "deviceId")
Note that map can also be applied to objects, in which case the x will be a tuple with {key, value} as in the expression below.
$val AUTH_PERMS = payload.roles indexBy "name" map x => (x.value.permissions + {name:x.key}) indexBy "name"
as - typecastsYou can typecast an expression, to change the type or make sure it's of a certain type. The right side expression can be:
x as numberx as Studentx as avalue where avalue is "Student" will be re-evaluated as x as StudentThis is a special transformation that can be applied to a json document, to flatten it into a list of parameters that can be used to call a message:
$when something.hapenned ()
=> (j = {a:1, b:2})
=> something.else (j.asAttrs)
j would be flattened and a and b would be parameters to the new message something.else.
You can get system properties with diesel.props.system which will populate the payload with an object containing all system properties, IF running in self-hosted mode.
Likewise, you can load a property file with diesel.props.file only in self-hosted mode. Or a JSON file via diesel.props.jsonFile message, also only in self-hosted mode.
Matching rules for decomposition is done according to these pattern matching rules.
$when dieseltest.rule1(a,b)
=> (rule1ab=a+b)
This matches the message by name and list of values, no ambiguity.
You can match the entity with a * - this will match a.sendtest as well as a.b.c.sendtest:
$when *.sendtest
=> (rule12=true)
Or, you can match the action with a * - but note that this matches only the last word, i.e. the "action" - so it will match dieseltestsendtest.dothis, but not dieseltestsendtest.a.b.c.dothis:
$when dieseltestsendtest.*
=> (rule16=true)
$when dieseltest.send.multiple.*
=> (rule18=true)
You can use a full and more complex js-like match for both entity and action together:
$when /dieseltest.*/
=> (rule19=true)
$when /dieseltest\..*/
=> (rule19a=true)
The second rule above will match dieseltest.a.b.c.action but not dieseltests.a.b.c.action.
...can be builtinto the list of arguments, like diesel.isLocalhost is false below - that is not an argument that is passed in, but a general condition:
$when diesel.realm.configure(realm is "devnetlinq", diesel.isLocalhost is false)
$expect and matchesThese are match expressions, used in $expect, $when and $mock. Their purpose is to match a parameter by name with a value expression. Some can also extract more parameters (say using regular expressions wiht named groups).
The full options around the expect are: $expect not IF MSG (matches) IF
not does what you think it doesIF you can add conditions - when this expect should be taken into accountNote that the expressions in the expect are NOT regular conditional expressions, but match expressions, which are quite alike but not the same thing...
$expect (name) // name is present
$expect (name == "Jane") // equal
$expect (name is "Jane") // equal
$expect (subId ~= "sub[0-9]*") // matches
$expect (subId matches "sub[0-9]*") // matches
$expect (subId matches "sub[0-9]*") // matches
Type checking
$expect (age is Number)
$expect (persons is Array)
$expect (name is String)
$expect (x1 is Student) // class defined in a domain model
$expect (x1 is Json)
// extract more parameters - will also populate an acctId when matching the regex
$mock diesel.rest (path ~= "/getAccount1/(?<acctId>\\d+)") => (payload={
"status": "ok",
"someStats": "67",
"accountId" : acctId
})
Note that while `diesel.rest` is a reserved message, you can use this implicit group parsing into variable in any message:
$when expr.captureGroups (path ~= "/getAccount1/(?<acctId>\\d+)") => (payload={
"accountId" : acctId
})
To avoid having to declare a rule just to do regex group decomposition, there is a `ctx.regex`:
$send ctx.set (payload = "/getAccount1/99")
$send ctx.regex(regex = "/getAccount1/(?<acctId3>\\d+)")
$expect (acctId3 is "99")
Or if you don't want to overwrite payload:
$send ctx.set (payload = "pa")
$send ctx.regex(regex = "/getAccount1/(?<acctId4>\\d+)", payload="/getAccount1/999")
$expect (acctId4 is "999")
$expect (payload is "pa")
Negative and other ideas:
$send ctx.set(d1 = now() - "1 second", d2 = now() + "1 second")
$expect (d1 < d2)
$expect not (d1 > d2)
$expect ((d1 > d2) is false)
Here's some other variants, with a more intuitive language:
$expect (name is empty) // name is NOT present OR is present but empty (type-aware)
$expect (name is defined) // name is present but may be empty
$expect (name not defined) // name is NOT present
$expect (name not empty) // name is present AND has some value non-empty (type-aware)
Note that you can also use other values for comparison - so take care to not use common reserved words for values:
$val jane = new Person{name:"Jane"}
$expect (guest is jane) // name is NOT present
Note that $expect uses a match expression, so you can use a list of parameters and the result is composed:
$expect (name is "Jane", subId is defined) // both must match
There are a few built-in functions you can use in a variety of situations in a common manner x=sizeOf(anArray)
NOTE that these are sensitive to the order of arguments, unlike messages!
nicej(a=ajson) Formats a nice json if the value was a json.
uuid() Returns a UUID using Mongo helpers (see object id in Mongo) - it is shorter than other UUIDs, when you don't need to encode the world!
now() Returns current time in ISO format
Returns current time in ISO format, date portion only
Returns hashcode of argument.
select either b or c, depending on first condition iff (a=payload>2, b=payload+2, c=payload+3)
Compare two arguments, returning -1, 0, 1: cmp(op=">", a, b)
Classic string format: sprintf(format="%d", value=x)
Test if first parm matches second - equivalent of the matches, replaceAll and replaceFirst Java/Scala String method. The name of the parameters does not matter, the order does.
$send ctx.set (re176=matches(a="abc", b="[abc]+"))
$expect (re176 is true)
$send ctx.set (re176=matches(a="abc", b="[bc]+"))
$expect (re176 is false)
$send ctx.set (re176=replaceAll(a="abc", b="[bc]+", c="xx"))
$expect (re176 is "axx")
Encrypt the argument.
UTF-8 URL encoder.
Trim a string. Convert to upper or lower.
Split a string into array: split(string, regex)
Create a list from a range. The range is exclusive: rangeList(from=1, to=3) would be [1,2]
Calculates the size of an array/list (number of elements) or of an object (number of attributes) or length of a string.
$val builtInFunctions1 = sizeOf(x=cart.items),
$val builtInFunctions2 = typeOf(x=cart.items),
Returns the type of a parameter: typeOf(x=4)
See as operator as well, for typecasts.
Use it to access a JSON object with an expression:
$send ctx.echo(x5=accessor(obj={a:{a:{a:1}}, b:{b:{b:2}}}, accessor="a.a.a"))
Flatten a lists of lists (or array of arrays).
Convert an ISO Z-datetime to milliseconds.
Are: math.min, math.max, math.average, math.sum
=> (theBigOne = math.max (x = [3,2,1]))
They take an array of numbers.
You need to log in to post a comment!