Akkurate 0.10.0 Help

Extend Akkurate

Although Akkurate focuses on providing all the essential constraints, you might need to write your own constraints for custom business validation.

Inline constraints

Sometimes you might need a constraint for only one specific case across the whole codebase; this is where inline constraints shine. To create one, use constrain inside your validator:

@Validate data class Book(val title: String) Validator<Book> { title.constrain { it.split(" ").size > 2 } otherwise { "The title must contain more than two words" } }

The constrain function accepts a lambda, which must return a Boolean; if true, the constraint is satisfied; otherwise a violation is raised.

Named constraints

If the same constraint is used multiple times, stop repeating yourself and extract it to an extension function.

Let's reuse our previous example, extract the constraint to an extension function with a Validatable<String> receiver, and name it hasWordCountGreaterThanTwo:

Validator<Book> { title.hasWordCountGreaterThanTwo() otherwise { "The title must contain more than two words" } } private fun Validatable<String>.hasWordCountGreaterThanTwo() = constrain { it.split(" ").size > 2 }

However, named constraints can do more than inline ones. First, we can add a parameter to count a variable number of words:

Validator<Book> { title.hasWordCountGreaterThan(2) otherwise { "The title must contain more than two words" } } private fun Validatable<String>.hasWordCountGreaterThan(count: Int) = constrain { it.split(" ").size > count }

You can also add a default message to explain what happened, in case the developer using your constraint doesn't provide one:

private fun Validatable<String>.hasWordCountGreaterThan(count: Int) = constrain { it.split(" ").size > count } otherwise { "Must contain more than $count words" }

Finally, it might be useful to support nullable values like in Akkurate's built-in constraints. The constrainIfNotNull function can be used to create constraints that are always satisfied when the value is null (like described by the Vacuous Truth principle):

private fun Validatable<String?>.hasWordCountGreaterThan(count: Int) = constrainIfNotNull { it.split(" ").size > count } otherwise { "Must contain more than $count words" }

Test your code

When writing your own constraints, you might want to write unit tests to verify their behaviors.

This is done by installing a new artifact:

Install akkurate-test

testImplementation("dev.nesk.akkurate:akkurate-test:0.10.0")

    This artifact provides the Validatable function, which returns a value ready to be constrained:

    val validatable = Validatable("The Lord of the Rings") val satisfiedConstraint = validatable.hasWordCountGreaterThan(4) assertTrue(satisfiedConstraint.satisfied) val unsatisfiedConstraint = validatable.hasWordCountGreaterThan(5) assertFalse(unsatisfiedConstraint.satisfied)
    Last modified: 24 September 2024