Refactor "AbstractWrapper" class

This commit also creates `AbstractLocaleWrapper` to help creating
locale-based rules.
This commit is contained in:
Henrique Moody 2018-01-28 13:14:15 +01:00
parent 550795c1e2
commit aa7d84c3ea
No known key found for this signature in database
GPG key ID: 221E9281655813A6
10 changed files with 150 additions and 108 deletions

View file

@ -0,0 +1,59 @@
<?php
/*
* This file is part of Respect/Validation.
*
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
*
* For the full copyright and license information, please view the "LICENSE.md"
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use function class_exists;
use function mb_strtolower;
use function sprintf;
use function ucfirst;
use Respect\Validation\Exceptions\ComponentException;
/**
* Abstract class to help creating rules based on location.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractLocaleWrapper extends AbstractWrapper
{
/**
* @var string
*/
private $countryCode;
/**
* Initializes the rule.
*
* @param string $countryCode
*
* @throws ComponentException When country is not supported.
*/
public function __construct(string $countryCode)
{
$normalized = ucfirst(mb_strtolower($countryCode));
$className = sprintf('%s\\Locale\\%s%s', __NAMESPACE__, $normalized, $this->getSuffix());
if (!class_exists($className)) {
throw new ComponentException(sprintf('"%s" is not a supported country code', $countryCode));
}
$this->countryCode = $countryCode;
parent::__construct(new $className);
}
/**
* Returns the class name based on the identifier.
*
* @return string
*/
abstract protected function getSuffix(): string;
}

View file

@ -13,40 +13,60 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Validatable;
/**
* Abstract class to help on creating rules that wrap rules.
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
abstract class AbstractWrapper extends AbstractRule
{
protected $validatable;
/**
* @var Validatable
*/
private $validatable;
public function getValidatable()
/**
* Initializes the rule.
*
* @param Validatable $validatable
*/
public function __construct(Validatable $validatable)
{
if (!$this->validatable instanceof Validatable) {
throw new ComponentException('There is no defined validatable');
}
return $this->validatable;
$this->validatable = $validatable;
}
/**
* {@inheritdoc}
*/
public function assert($input)
{
return $this->getValidatable()->assert($input);
return $this->validatable->assert($input);
}
/**
* {@inheritdoc}
*/
public function check($input)
{
return $this->getValidatable()->check($input);
return $this->validatable->check($input);
}
/**
* {@inheritdoc}
*/
public function validate($input)
{
return $this->getValidatable()->validate($input);
return $this->validatable->validate($input);
}
/**
* {@inheritdoc}
*/
public function setName($name)
{
$this->getValidatable()->setName($name);
$this->validatable->setName($name);
return parent::setName($name);
}

View file

@ -13,21 +13,13 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
class IdentityCard extends AbstractWrapper
final class IdentityCard extends AbstractLocaleWrapper
{
public $countryCode;
public function __construct($countryCode)
/**
* {@inheritdoc}
*/
protected function getSuffix(): string
{
$shortName = ucfirst(mb_strtolower($countryCode)).'IdentityCard';
$className = __NAMESPACE__.'\\Locale\\'.$shortName;
if (!class_exists($className)) {
throw new ComponentException(sprintf('There is no support for identity cards from "%s"', $countryCode));
}
$this->countryCode = $countryCode;
$this->validatable = new $className();
return 'IdentityCard';
}
}

View file

@ -20,11 +20,6 @@ class Optional extends AbstractWrapper
{
use UndefinedHelper;
public function __construct(Validatable $rule)
{
$this->validatable = $rule;
}
public function assert($input)
{
if ($this->isUndefined($input)) {

View file

@ -13,27 +13,21 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
/**
* Validates country subdivision codes according to ISO 3166-2.
*
* @see http://en.wikipedia.org/wiki/ISO_3166-2
* @see http://www.geonames.org/countries/
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
class SubdivisionCode extends AbstractWrapper
final class SubdivisionCode extends AbstractLocaleWrapper
{
public $countryCode;
public function __construct($countryCode)
/**
* {@inheritdoc}
*/
protected function getSuffix(): string
{
$shortName = ucfirst(mb_strtolower($countryCode)).'SubdivisionCode';
$className = __NAMESPACE__.'\\Locale\\'.$shortName;
if (!class_exists($className)) {
throw new ComponentException(sprintf('"%s" is not a valid country code in ISO 3166-2', $countryCode));
}
$this->countryCode = $countryCode;
$this->validatable = new $className();
return 'SubdivisionCode';
}
}

View file

@ -13,22 +13,13 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\ComponentException;
final class Vatin extends AbstractWrapper
final class Vatin extends AbstractLocaleWrapper
{
public $countryCode;
public function __construct($countryCode)
/**
* {@inheritdoc}
*/
protected function getSuffix(): string
{
$shortName = ucfirst(mb_strtolower($countryCode)).'Vatin';
$className = __NAMESPACE__.'\\Locale\\'.$shortName;
if (!class_exists($className)) {
$message = sprintf('There is no support for VAT identification number from "%s"', $countryCode);
throw new ComponentException($message);
}
$this->countryCode = $countryCode;
$this->validatable = new $className();
return 'Vatin';
}
}

View file

@ -14,40 +14,33 @@ declare(strict_types=1);
namespace Respect\Validation\Rules;
use PHPUnit\Framework\TestCase;
use ReflectionObject;
use Respect\Validation\Validatable;
class AbstractWrapperTest extends TestCase
/**
* @test core
*
* @covers \Respect\Validation\Rules\AbstractWrapper
*
* @author Henrique Moody <henriquemoody@gmail.com>
*/
final class AbstractWrapperTest extends TestCase
{
/**
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage There is no defined validatable
* @test
*/
public function testShouldThrowsAnExceptionWhenWrappedValidatableIsNotDefined(): void
{
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$wrapper->getValidatable();
}
private function bindValidatable($wrapper, $validatable): void
{
$reflectionObject = new ReflectionObject($wrapper);
$reflectionProperty = $reflectionObject->getProperty('validatable');
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($wrapper, $validatable);
}
public function testShouldReturnDefinedValidatable(): void
public function shouldReturnDefinedValidatable(): void
{
$validatable = $this->createMock(Validatable::class);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$this->bindValidatable($wrapper, $validatable);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class, [$validatable]);
self::assertSame($validatable, $wrapper->getValidatable());
self::assertAttributeSame($validatable, 'validatable', $wrapper);
}
public function testShouldUseWrappedToValidate(): void
/**
* @test
*/
public function shouldUseWrappedToValidate(): void
{
$input = 'Whatever';
@ -58,13 +51,15 @@ class AbstractWrapperTest extends TestCase
->with($input)
->will($this->returnValue(true));
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$this->bindValidatable($wrapper, $validatable);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class, [$validatable]);
self::assertTrue($wrapper->validate($input));
}
public function testShouldUseWrappedToAssert(): void
/**
* @test
*/
public function shouldUseWrappedToAssert(): void
{
$input = 'Whatever';
@ -75,13 +70,15 @@ class AbstractWrapperTest extends TestCase
->with($input)
->will($this->returnValue(true));
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$this->bindValidatable($wrapper, $validatable);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class, [$validatable]);
self::assertTrue($wrapper->assert($input));
}
public function testShouldUseWrappedToCheck(): void
/**
* @test
*/
public function shouldUseWrappedToCheck(): void
{
$input = 'Whatever';
@ -92,13 +89,15 @@ class AbstractWrapperTest extends TestCase
->with($input)
->will($this->returnValue(true));
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$this->bindValidatable($wrapper, $validatable);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class, [$validatable]);
self::assertTrue($wrapper->check($input));
}
public function testShouldPassNameOnToWrapped(): void
/**
* @test
*/
public function shouldPassNameOnToWrapped(): void
{
$name = 'Whatever';
@ -109,8 +108,7 @@ class AbstractWrapperTest extends TestCase
->with($name)
->will($this->returnValue($validatable));
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class);
$this->bindValidatable($wrapper, $validatable);
$wrapper = $this->getMockForAbstractClass(AbstractWrapper::class, [$validatable]);
self::assertSame($wrapper, $wrapper->setName($name));
}

View file

@ -52,14 +52,6 @@ class OptionalTest extends TestCase
];
}
public function testShouldAcceptInstanceOfValidatobleOnConstructor(): void
{
$validatable = $this->createMock(Validatable::class);
$rule = new Optional($validatable);
self::assertSame($validatable, $rule->getValidatable());
}
/**
* @dataProvider providerForOptional
*/

View file

@ -23,7 +23,7 @@ class SubdivisionCodeTest extends TestCase
{
/**
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage "whatever" is not a valid country code in ISO 3166-2
* @expectedExceptionMessage "whatever" is not a supported country code
*/
public function testShouldThrowsExceptionWhenInvalidFormat(): void
{
@ -32,7 +32,7 @@ class SubdivisionCodeTest extends TestCase
/**
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage "JK" is not a valid country code in ISO 3166-2
* @expectedExceptionMessage "JK" is not a supported country code
*/
public function testShouldNotAcceptWrongNamesOnConstructor(): void
{
@ -41,9 +41,10 @@ class SubdivisionCodeTest extends TestCase
public function testShouldDefineASubdivisionCodeFormatOnConstructor(): void
{
$countrySubdivision = new SubdivisionCode('US');
$countryCode = 'US';
$countrySubdivision = new SubdivisionCode($countryCode);
self::assertEquals('US', $countrySubdivision->countryCode);
self::assertAttributeEquals($countryCode, 'countryCode', $countrySubdivision);
}
public function providerForValidSubdivisionCodeInformation()

View file

@ -27,12 +27,12 @@ final class VatinTest extends TestCase
$countryCode = 'PL';
$rule = new Vatin($countryCode);
self::assertInstanceOf(Validatable::class, $rule->getValidatable());
self::assertAttributeInstanceOf(Validatable::class, 'validatable', $rule);
}
/**
* @expectedException \Respect\Validation\Exceptions\ComponentException
* @expectedExceptionMessage There is no support for VAT identification number from "BR"
* @expectedExceptionMessage "BR" is not a supported country code
*/
public function testShouldThrowAnExceptionWhenCountryCodeIsNotSupported(): void
{