Skip to content

Commit

Permalink
Allow customization for a whole chain
Browse files Browse the repository at this point in the history
Usually, a chain of assertions serves the purpose of validating a single
thing. Because of that, it's helpful to allow a single customization for
the whole chain.

This commit will also update the documentation to reflect the current
changes and clarify what the library has to offer.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
  • Loading branch information
henriquemoody committed Apr 6, 2023
1 parent 07f534f commit e3bcb48
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 19 deletions.
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
[![Total Downloads](https://img.shields.io/packagist/dt/respect/assertion.svg?style=flat-square)](https://packagist.org/packages/respect/assertion)
[![License](https://img.shields.io/packagist/l/respect/assertion.svg?style=flat-square)](https://packagist.org/packages/respect/assertion)

The power of [Validation][] into an assertion library with
**more than 1k assertions**.
The power of [Validation][] into an assertion library.

* More than 1.5k assertions
* Support for custom messages
* Support for custom exceptions

For a complete list of assertions, check all the [mixin interfaces][], and read
[Validation][] to understand how each rule/assertion works.
Expand Down Expand Up @@ -61,12 +64,14 @@ Assert::intType('string');
Assert::odd(5);
```

By default, it throws exceptions that are instances of [ValidationException][],
which means you can catch [InvalidArgumentException][] (or [LogicException][]).

### Custom messages

The exceptions that `Assert` throws are the same that [Validation][] throws.
That allows you to customize the error messages using templates:


```php
// will throw an exception => I was expecting 5, but you gave be 1
Assert::equals(1, 5, 'I was expecting {{compareTo}}, but you gave be {{input}}');
Expand Down Expand Up @@ -101,6 +106,23 @@ In the example above, as soon as any assertion fails, it will throw an
exception. If you wish to chain validations and only check them all
simultaneously, we suggest you use the API from [Validation][].

You can also customize a message or exception for the whole chain.

```php
// will throw an exception => The number must be valid
Assert::that(0, new DomainException('The number must be valid'))
->positive()
->greaterThan(5);

// will throw an exception => But it is not greater than 5, though
Assert::that(3, 'The number must be valid')
->positive()
->greaterThan(5, 'But it is not greater than 5, though');
```

Note that the customization on a specific assertion will overwrite the
customization on the whole chain.

## Prefixes

With Assertion, you can use any [Validation][] rule, but it also allows
Expand Down Expand Up @@ -374,9 +396,12 @@ Assert::nullOr(1, 2);
[Composer]: http://getcomposer.org
[Countable]: http://php.net/countable
[Equals]: https://respect-validation.readthedocs.io/en/latest/rules/Equals/
[InvalidArgumentException]: https://www.php.net/InvalidArgumentException
[iterable]: http://php.net/types.iterable
[LogicException]: https://www.php.net/LogicException
[Malukenho]: https://github.com/malukenho
[mixin interfaces]: src/Mixin/Static
[Packagist]: http://packagist.org/packages/respect/assertion
[Validation]: https://respect-validation.readthedocs.io
[Validation]: https://github.com/Respect/Validation
[ValidationException]: https://github.com/Respect/Validation/blob/master/library/Exceptions/ValidationException.php
[webmozart/assert]: https://github.com/webmozart/assert
5 changes: 3 additions & 2 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Respect\Assertion\Creator\StandardCreator;
use Respect\Assertion\Exception\CannotCreateAssertionException;
use Respect\Assertion\Mixin\Static\Mixin;
use Throwable;

use function array_shift;

Expand All @@ -36,9 +37,9 @@ final class Assert
{
private static ?AssertionCreator $assertionCreator = null;

public static function that(mixed $input): ChainAssert
public static function that(mixed $input, null|string|Throwable $description = null): ChainAssert
{
return new ChainAssert($input);
return new ChainAssert($input, $description);
}

private static function getAssertionCreator(): AssertionCreator
Expand Down
12 changes: 9 additions & 3 deletions src/ChainAssert.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Respect\Assertion;

use Respect\Assertion\Mixin\Dynamic\Mixin;
use Throwable;

use function array_unshift;

Expand All @@ -22,16 +23,21 @@
*/
final class ChainAssert
{
public function __construct(private readonly mixed $input)
{
public function __construct(
private readonly mixed $input,
private readonly null|string|Throwable $description = null
) {
}

/**
* @param array<mixed> $arguments
* @param array<int, mixed> $arguments
*/
public function __call(string $name, array $arguments): self
{
array_unshift($arguments, $this->input);
if ($this->description !== null) {
$arguments[] = $this->description;
}

Assert::__callStatic($name, $arguments);

Expand Down
5 changes: 2 additions & 3 deletions src/Creator/KeyCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use Respect\Validation\Rules\Key;
use Respect\Validation\Rules\Not;

use function array_pop;
use function array_shift;
use function lcfirst;
use function str_starts_with;
Expand All @@ -43,11 +42,11 @@ public function create(string $name, array $parameters): Assertion

$key = array_shift($parameters);
if ($name === 'keyPresent') {
return new Standard(new Key($key), array_pop($parameters) ?? null);
return new Standard(new Key($key), array_shift($parameters) ?? null);
}

if ($name === 'keyNotPresent') {
return new Standard(new Not(new Key($key)), array_pop($parameters) ?? null);
return new Standard(new Not(new Key($key)), array_shift($parameters) ?? null);
}

$assertion = $this->assertionCreator->create(lcfirst(substr($name, 3)), $parameters);
Expand Down
5 changes: 2 additions & 3 deletions src/Creator/PropertyCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use Respect\Validation\Rules\Attribute;
use Respect\Validation\Rules\Not;

use function array_pop;
use function array_shift;
use function lcfirst;
use function str_starts_with;
Expand All @@ -43,11 +42,11 @@ public function create(string $name, array $parameters): Assertion

$key = array_shift($parameters);
if ($name === 'propertyPresent') {
return new Standard(new Attribute($key), array_pop($parameters) ?? null);
return new Standard(new Attribute($key), array_shift($parameters) ?? null);
}

if ($name === 'propertyNotPresent') {
return new Standard(new Not(new Attribute($key)), array_pop($parameters) ?? null);
return new Standard(new Not(new Attribute($key)), array_shift($parameters) ?? null);
}

$assertion = $this->assertionCreator->create(lcfirst(substr($name, 8)), $parameters);
Expand Down
4 changes: 2 additions & 2 deletions src/Creator/StandardCreator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
use Respect\Validation\Validatable;
use Throwable;

use function array_pop;
use function array_slice;
use function class_exists;
use function count;
use function current;
use function sprintf;
use function ucfirst;

Expand Down Expand Up @@ -84,6 +84,6 @@ private function description(array $parameters, array $constructorParameters): s
return null;
}

return array_pop($parameters);
return current(array_slice($parameters, count($constructorParameters), 1)) ?? null;
}
}
12 changes: 12 additions & 0 deletions tests/documentation/usage-chained-assertions.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ exceptionMessage(
->positive('I expected a positive number')
->lessThan(4)
);
exceptionMessage(
static fn() => Assert::that(0, new DomainException('The number must be valid'))
->positive()
->greaterThan(5)
);
exceptionMessage(
static fn() => Assert::that(3, 'The number must be valid')
->positive()
->greaterThan(5, 'But it is not greater than 5, though')
);
?>
--EXPECT--
I expected a positive number
The number must be valid
But it is not greater than 5, though
17 changes: 15 additions & 2 deletions tests/integration/ChainAssertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,26 @@ public function itShouldThrowRespectValidationExceptionWithCustomTemplate(): voi
/**
* @test
*/
public function itShouldThrowCustomException(): void
public function itShouldThrowCustomChainException(): void
{
$exception = new Exception('The number you are validating must be negative');

$this->expectExceptionObject($exception);

Assert::that(2)->negative($exception);
Assert::that(2, $exception)->negative();
}

/**
* @test
*/
public function itShouldThrowCustomRuleException(): void
{
$chainException = new Exception('You should not see this');
$assertionException = new Exception('The number you are validating must be negative');

$this->expectExceptionObject($assertionException);

Assert::that(2, $chainException)->negative($assertionException);
}

/**
Expand Down

0 comments on commit e3bcb48

Please sign in to comment.