This entire system revolves around "messages". A message can map into a few concepts:
An event or message has the form
$msg subject.verb (parm1=value,...). It associates a verb to a subject and a list of attributes.
The subject can be qualified and can be:
my.system.email- use this for packages or other grouping or hierarchy of entities
The verb is a single word (not qualified) can be:
Messages can be received, generated, sent or mocked:
$msgand a known protocol
Messages can and should be declared, inside a Spec like so:
$msg home.guest_arrived(name:String ~= "Jane") : (greetings:String)
The type annotations and default values for arguments are optional - they are helpful though and it's good practice to provide them!
If messages are not declared, their respective rules are still applied, but there is less information for the engine or the integration points (receiving messages) to process them, some of the effects may include:
Here are the annotations you can use for parameters/arguments:
name = "John"
name ~= "Jane"
Types are generally inferred, but when ingesting data, declaring types for messages is a good idea.
The sample values are used when generating the messages automatically, especially in
sketch mode, see Flags and modes.
Messages can be decomposed in a Spec, via matcher rules, with the
$when subjectMatch.verbMatch (parmMatch) IF => IF subject.verb (parms)
When a suitable match is found, the resulting message will be created. IF is an optional condition to apply this decomposition rule. You can see more details about Expressions and pattern matching.
$when home.guest_arrived(name) => lights.on $when home.guest_arrived(name=="Jane") if (isRaining == "true") => chimes.welcome(name="Jane") $when lights.* => lights.check $when *.sendtest => (rule12=true) $when dieseltestsendtest.* => (rule16=true) $when dieseltest.send.multiple.* => (rule18=true) $when /dieseltest.*/ => (rule19=true) $when /dieseltest\..*/ => (rule19a=true)
There's also a multiline version:
$when home.guest_arrived(name) => if (name!="Jane") lights.on => if (name=="Jane" && isRaining == "true") => chimes.welcome(name="Jane")
$whenis the message implementation, while
$mockis the message's mock.
$msg subject.verb (parms) is used to simulate a message.
$sample subjectMatch.verbMatch (parmMatch) is used to sample messages.
When either is seen (simulated or sampled), and it meets the conditions of the first message in the story, the story is "triggered" and the expectations are checked.
Messages can be triggered via the Diesel API as well.
If the message was simulated as a result of running the story in test mode, then the following messages are also triggered and tested.
If the message was observed during normal runtime and we only need to test it, the story will progress as further messages are observed during the same runtime (i.e. their appearance is tested).
When a story is executed, its expectations are being tested:
$expect subjectMatch.verbMatch (parmMatch)
$expect (greetings=="Greetings, Jane") $expect chimes.welcome(name=="Jane")
Note the difference between checking for a message with values versus the version to check just for values.
When running in
mockMode, messages can be mocked instead of actually "executed":
$mock subjectMatch.verbMatch (parmMatch) => (parms)
$mock lights.check => (lights="bright") $mock chimes.welcome => (greetings="Greetings, "+name)
When not running in mock mode, messages will be executed. A suitable executor will be found and passed the message.
Some executors are defined by default, such as
wiki, see Default executors.
Otherwise, you can extend the system by creating your own executors, see [The SDK]].
Message execution is naturally asynchronous, in the engine's internal implementation. However, logically, the execution is treated as synchronous. The engine uses Akka actors internally, to execute each message individually, in its own separate execution context.
This represents the ultimate in flexibility but also ads some complexity for the users:
For instance, the message
$msg <POST> subDb.createSub is an asynchronous REST call, consisting of a request and then a reply on a different actor/thread.
Thus, when the message is triggered initially, it is not in fact complete, as logically, it should wait for the response.
The executor of the message is responsible for notifying the engine when the message is completed. At this point, the engine will consider the next messages in a sequence.
See more in Concurrency, asynchronous and distributed.