Activate provides a Design by Contract (DbC) mechanism to achieve validation.
It’s possible to define a condition that must be fulfilled before method execution:
override def delete =
preCondition(name != "Undeletable Person") {
super.delete
}
Using method parameters inside a pre-condition:
def modifyName(newName: String) =
preCondition(name != newName) {
name = newName
}
Defining the pre-condition name:
def modifyName(newName: String) =
preCondition(name != newName, "preConditionName") {
name = newName
}
If a pre-condition is not satisfyied, Activate throws a PreCondidionViolationException.
It’s possible to define a condition that must be fulfilled after method execution:
def modifyName(newName: String) = {
name = newName
} postCondition (name == newName)
Post-condition name:
def modifyName(newName: String) = {
name = newName
} postCondition (name == newName, "postConditionName")
Using the method return:
def namePlusA = {
name + "a"
} postCondition(result => result == name + "a")
If a post-condition is not satisfyied, Activate throws a PostCondidionViolationException.
Invariants are validation predicates that are verified in the entity lifecycle. They are special instance methods:
def invariantNameMustNotBeEmpty = invariant {
name != null && name.nonEmpty
}
NOTE: The invariant name is the method name.
It’s a good practice to prefix the invariant method name with “invariant”, thus the client code can easily know the entity invariants.
Passing error params:
def invariantNameMustNotBeEmpty =
invariant(errorParams = List(name)) {
name != null && name.nonEmpty
}
If a pre-condition is violated, Activate throws an InvariantViolationException.
It’s possible to define validation options globally, by transaction, by thread or by entity instance. The available options are:
-
onCreate
Validate invariants after instance creation. If there is an invalid invariant at the end of the constructor, Activate deletes the entity and throws an InvariantViolationException.
-
onWrite
Validate invariants on attribute modifications using a nested transaction. If an invariant fail, the nested transaction aborts and modification is not propagated to outer transaction.
-
onRead
Validate invariants on attribute read. Don’t permit to read attributes from invalid entities, throwing an InvariantViolationException.
-
onTransactionEnd
Validate invariants on transaction end.
By default, Activate has onCreate and onWrite global options.
To define custom options for an entity instance, override entity “validationOptions” method:
override protected def validationOptions =
Some(Set(EntityValidationOption.onTransactionEnd))
Methods of net.fwbrasil.activate.entity.EntityValidation object:
-
def removeAllCustomOptions: Unit
Remove all custom options (does not include entities overridden validationOptions)
-
def addGlobalOption(option: EntityValidationOption): Unit
def removeGlobalOption(option: EntityValidationOption): Unit
def setGlobalOptions(options: Set[EntityValidationOption]): Unit
def getGlobalOptions: Set[EntityValidationOption]
Methods to define global options (default is onCreate and onWrite).
-
def addTransactionOption(option: EntityValidationOption): Unit
def removeTransactionOption(option: EntityValidationOption): Unit
def setTransactionOptions(options: Set[EntityValidationOption]): Unit
def getTransactionOptions(implicit ctx: ActivateContext): Option[Set[EntityValidationOption]]
Methods to define transaction options (default is no options – None).
-
def addThreadOption(option: EntityValidationOption): Unit
def removeThreadOption(option: EntityValidationOption): Unit
def setThreadOptions(options: Set[EntityValidationOption]): Unit
def getThreadOptions(implicit ctx: ActivateContext): Option[Set[EntityValidationOption]]
Methods to define thread options (default is no options – None).
You can also validate an entity at any moment by calling the “validate” method or the invariant methods.