Complex structures
Validating complex structures can quickly become a challenge. Akkurate has been designed from scratch for this task and provides you everything you need for any complexity levels.
Structured programming
Despite using a declarative approach for validation, Akkurate doesn't enforce you to use it and even encourages you to switch to imperative code when needed.
Loops
When validating arrays, maps or iterables, you can use a normal loop to iterate over each item:
Since iterating over a collection to validate its items is a common task, Akkurate also provides the each
helper:
Conditions
You can use conditions to apply constraints only when needed. Let's say our Library
has a maximum capacity:
The maximum capacity is infinite when it's equal to 0, so we want to constrain the size of the book collection only if the maximum capacity is at least 1. To achieve this, we can unwrap the value, add a condition, and create a constraint only if its positive.
Conditional constraints
Sometimes, you need to apply a constraint depending on the result of a previous one, like Twitter has to do in its settings when a user wants to update its username.
When Twitter launched, you could pick a one-character username. However, this is no longer possible; you're now forced to use at least five characters.
Now imagine being Twitter's product owner for today's settings page. Someone tries to change its username to “a”. This is less than five characters, and it already exists. So, what errors do you want to display?
Two errors? One about character count, and another one about an already taken username?
Or only one error about character count?
The answer is the second one:
Since new users can't register with handles shorter than 5 characters anyway, Twitter chose to skip the database check when the username is too short. Avoiding an unnecessary database query and restraining error spamming for the user.
This is a typical use case for conditional constraints. When a constraint is applied, you can read its satisfied
property to check if the constraint is satisfied or not.
Composition
It is common to validate the same properties in different places. To avoid applying the same constraints multiple times, you can use composition to reuse a validator inside another one.
Here we have a Person
class used by two different properties: author
and reviewers
. To avoid code repetition, we create a validator for the Person
class and then call it from Validator<Book>
.
Comparing values
You might sometimes need to check if two values are equal:
However, writing .unwrap()
everywhere can quickly become cumbersome. This is why Validatable<T>
implements equals
and hashCode
as pass-through methods to the underlying value. Which means you can remove the calls to unwrap
to check for equality:
Transformation
Before validating some data, you might need to normalize it.
Imagine you add support for hashtags on the Book
class:
However, your users might prefix a hashtag with a #
, which means the book will be considered valid if a hashtag contains this single character:
To solve this, the solution would be to trim this character before validation, this can be done with the map
function: