Ch. 9 Creating a Scenario Verb
The SD language uses scenarios to declare behavior. As discussed in Modeling with the System Descriptor, scenarios are made up of steps. Each step uses a verb to convey specific meaning. It’s possible to create custom verbs along the same lines as custom validators. Custom verbs are useful when a team wants to convey specific meaning with a scenario or step.
For this example, we’ll create a new verb called willEmail
which can be used in a when
step in a scenario like this:
Example willEmail scenario
scenario notifyRequestorOfStatus {
when receiving installationStatus
then willEmail emailMessage
}
Creating the Verb
Verbs are created in almost the same manner as when creating a custom validator. Configure the dependencies for the containing project the same way:
build.gradle
group = 'com.mystuff'
version = '1.0.0-SNAPSHOT'
dependencies {
api "com.ngc.seaside:systemdescriptor.model.api:$jellyfishVersion"
api "com.ngc.seaside:systemdescriptor.service.api:$jellyfishVersion"
implementation "com.google.inject:guice:4.1.0"
implementation "com.google.inject.extensions:guice-multibindings:4.1.0"
}
New scenario verbs implement the IScenarioStepHandler
interface. Most of the time, the abstract class
AbstractStepHandler
can be extended.
EmailStepHandler.java
public class EmailStepHandler extends AbstractStepHandler {
private final static ScenarioStepVerb PAST = ScenarioStepVerb.pastTense("emailed");
private final static ScenarioStepVerb PRESENT = ScenarioStepVerb.presentTense("emailing");
private final static ScenarioStepVerb FUTURE = ScenarioStepVerb.futureTense("willEmail");
public EmailStepHandler() {
register(PAST, PRESENT, FUTURE);
}
@Override
protected void doValidateStep(IValidationContext<IScenarioStep> context) {
// No extra validation needed right now.
}
}
First, the 3 tenses of the verb are declared as ScenarioStepVerb
objects. They are registered in the constructor with
the register call. The willEmail
keyword can be now be used in a Then step. Likewise the emailing
and emailed
keywords can be used in When and Given steps.
Step handlers that extend AbstractStepHandler
are also validators. Step handlers must validate that the verbs are used
correctly and the step is valid. Extending classes accomplish this validation by implementing the doValidateStep
method.
This uses the same validation API as discussed in the previous chapter. Most of the time, verbs need parameters. Simply
call requireStepParameters
in doValidateStep
to check for this:
EmailStepHandler.java
@Override
protected void doValidateStep(IValidationContext<IScenarioStep> context) {
requireStepParameters(context, "The 'email' verb requires parameters!");
}
You can also do additional validation. For example, maybe you don’t want to email any data types whose name ends in “Spam”:
EmailStepHandler.java
@Override
protected void doValidateStep(IValidationContext<IScenarioStep> context) {
requireStepParameters(context, "The 'email' verb requires parameters!");
IScenarioStep step = context.getObject();
String dataTypeName = step.getParameters().get(0);
if (dataTypeName.endsWith("Spam")) {
context.declare(Severity.ERROR, "Spam can't be emailed!", step).getParameters();
}
}
Like validators, step handlers are managed by Guice and can get dependencies injected via @Inject
.
Packaging the Verb
The verb is declared in a Module
the same as validators. However, unlike validators, step handlers are usually
bound twice because they are both IScenarioStepHandlers
and ISystemDescriptorValidators
:
EmailStepHandlerModule.java
public class EmailStepHandlerModule extends AbstractModule {
@Override
protected void configure() {
// Most of the time, verbs and validators are singletons.
bind(EmailStepHandler.class).in(Singleton.class);
// Bind EmailStepHandler as a handler.
Multibinder<IScenarioStepHandler> handlers = Multibinder.newSetBinder(
binder(),
IScenarioStepHandler.class);
handlers.addBinding().to(EmailStepHandler.class);
// Bind EmailStepHandler as a validator.
Multibinder<ISystemDescriptorValidator> validators = Multibinder.newSetBinder(
binder(),
ISystemDescriptorValidator.class);
validators.addBinding().to(EmailStepHandler.class);
}
}
As before, be sure to create a new file in src/main/resources/META-INF/services/
named com.google.inject.Module
. Its
contents should contain the fully-qualified class name of the module:
com.google.inject.Module
com.mystuff.modeling.validation.module.EmailStepHandlerModule
Creating both validators and verbs
It's possible to package both validators and verbs in a single JAR. Simply register all verbs and scenarios with
the JAR's Guice module. In this case, it will only be necessary to list a single JAR as a dependency when configuring
the build script of an SD project.
Using the New Verb
Verbs are deployed the same way as validators. Simply list the containing JAR as a Gradle buildscript
dependency,
copy the JAR to the JELLYFISH_USER_HOME
plugins/
directory, or include the JAR in an Eclipse update site.
Using the New Verb within Eclipse
The Eclipse tooling will automatically declare an error if a scenario verb is used that can’t be found. As a result, custom verbs need to be installed into Eclipse. This can be done in one of two ways:
- Create an Eclipse update site that includes the JAR with the verbs. Users can create a single update site that
installs both Jellyfish and any custom verbs or validators. See
build.gradle
in the Jellyfish update site for an example project that creates an update site with Gradle. - Using Jellyfish user home: simply copy the JAR to
~/.jellyfish/plugins
(or$JELLYFISH_USER_HOME/plugins
). This option is simpler than the update site option and allows users to quickly install custom extensions. Use this option with care. The update site requires more up front setup but is usually a better option in the long term.