melhorando sua api com dsls

80
MELHORANDO SUA API COM DSLS @augustohp

Upload: augusto-pascutti

Post on 12-Apr-2017

801 views

Category:

Software


0 download

TRANSCRIPT

MELHORANDO SUA API COM

DSLS@augustohp

DOMAIN SPECIFIC LANGUAGE

A SEGUIR, UMA

DSL

VALIDAÇÃO

1 <?php 2 3 use Respect\Validation\Validator as v; 4 5 v::stringType() 6 ->exactLength(8) 7 ->contains("-") 8 ->contains("/^[A-Z]{3}/") 9 ->contains("/[0-9]{4}$/") 10 ->assert($something);

1 <?php 2 3 namespace EasyTaxi\Validation\Rules; 4 5 use Respect\Validation\Rules as BaseRules; 6 7 class CarPlate extends BaseRules\AllOf 8 { 9 public function __construct() 10 { 11 $this->name = 'Brazilian car plate'; 12 13 parent::__construct( 14 new BaseRules\StringType(), 15 new ExactLength(8), 16 new BaseRule\Contains("-")->setName('Separator'), 17 new BaseRule\Contains("/^[A-Z]{3}/")->setName("Prefix") 18 new BaseRule\Contains("/^[0-9]{4}/")->setName("Sufix") 19 ); 20 } 21 }

1 <?php 2 3 namespace EasyTaxi\Validation\Rules; 4 5 use Respect\Validation\Rules as BaseRules; 6 7 class CarPlate extends BaseRules\AllOf 8 { 9 public function __construct() 10 { 11 $this->name = 'Brazilian car plate'; 12 13 parent::__construct( 14 new BaseRules\StringType(), 15 new ExactLength(8), 16 new BaseRule\Contains("-")->setName('Separator'), 17 new BaseRule\Contains("/^[A-Z]{3}/")->setName("Prefix") 18 new BaseRule\Contains("/^[0-9]{4}/")->setName("Sufix") 19 ); 20 } 21 }

1 <?php 2 3 // ... dentro de algum método de um Controller ... 4 5 Validation\Validator::arrayType() 6 ->key('driver_name', v::driverName()) 7 ->key('driver_birthdate', v::minimumAge(18)) 8 ->key( 9 'driver_car', v::arrayType( 10 v::key("model", v::carModel($this->get('model.vehicle'))), 11 v::key("assembler", v::carAssembler()), 12 v::key("year", v::maximumAge(5)), 13 v::key("plate", v::carPlate()) 14 ) 15 ) 16 ->key('license_number', v::driverLicense()) 17 ->key('taxi_permission', v::taxiPermissionNumber()) 18 ->key('address', v::address()) 19 ->assert($_POST);

COMO CRIAR ISSO?

1 <?php 2 3 namespace Respect\Validation; 4 5 class Validator 6 { 7 public function stringType() {} 8 public function contains($search) {} 9 public function assert($mixed) {} 10 }

1 <?php 2 3 namespace Respect\Validation; 4 5 interface Rule 6 { 7 public function isValid($mixed): bool; 8 }

IMPLEMENTANDO UMA

REGRA

1 <?php 2 3 namespace Respect\Validation\Rules; 4 5 use Respect\Validation; 6 7 class StringType implements Validation\Rule 8 { 9 public function isValid($mixed): bool 10 { 11 return is_string($mixed); 12 } 13 }

3 namespace Respect\Validation\Rules; 4 5 use Respect\Validation; 6 7 class AllOf implements Validation\Rule 8 { 9 protected $rules = []; 10 17 public function __construct(Validation\Rule ...$rules) 18 { 19 $this->rules = $rules; 20 } 21 22 public function isValid($mixed): bool 23 { 24 foreach ($this->rules as $rule) { 25 if (false === $rule->isValid($mixed)) { 26 return false; 27 } 28 } 29 30 return true; 31 } 32 }

INSTANCIANDO REGRAS

1 <?php 2 3 $factory = new Respect\Validation\RuleFactory; 4 $rule = $factory->createInstance('StringType');

1 <?php 2 3 namespace Respect\Validation; 4 5 class RuleFactory 6 { 7 public function createInstance($ruleName, array $args = []): Rule 8 { 9 $ruleNamespace = 'Respect\\Validation\\Rules\\'; 10 $className = $ruleNamespace . $rule; 11 $reflection = new ReflectionClass($className); 12 13 return $reflection->newInstanceArgs($args); 14 } 15 }

PODEMOS TER

MENOS CÓDIGO ?

1 <?php 2 3 namespace Respect\Validation; 4 5 class RuleFactory 6 { 7 public function __call($methodName, $methodArguments) 8 { 9 return $this->createInstance($methodName, $methodArguments); 10 } 11 12 public function createInstance($ruleName, array $args = []): Rule 13 { 14 /* ... */ 15 } 16 }

1 <?php 2 3 $checkFor = new Respect\Validation\RuleFactory; 4 5 $isString = $checkFor->StringType(); 6 7 $isTwitterUserName = $checkFor->AllOf( 8 $checkFor->AlNum(), 9 $checkFor->NoWhitespace(), 10 $checkFor->Length(1, 15) 11 );

PODEMOS TER

MENOS CÓDIGO ?

3 namespace Respect\Validation; 4 5 class RuleFactory 6 { 7 private static $factory = null; 8 9 public static function getInstance() 10 { 11 if (is_null(self::$factory)) { 12 self::$factory = new static; 13 } 14 15 return self::$factory; 16 } 17 18 public static function __callStatic($methodName, $methodArguments) 19 { 20 $factory = self::getFactory(); 21 22 return $factory->createInstance($methodName, $methodArguments); 23 } 24 25 /* ... */ 26 }

1 <?php 2 3 use Respect\Validation\RuleFactory as v; 4 5 $isTwitterUserName = v::AllOf( 6 v::AlNum(), 7 v::NoWhitespace(), 8 v::Length(1, 15) 9 );

@ANNOTATION

O QUE TEMOS

1 <?php 2 3 namespace EasyTaxi\Validation; 4 5 interface Rule 6 { 7 public function isValid($mixed): bool; 8 }

1 <?php 2 3 namespace EasyTaxi\Validation\Rules; 4 5 use EasyTaxi\Validation; 6 use Respect\Validation\Validator as v; 7 8 class PlateValidator implements Rule 9 { 10 public function isValid($mixed): bool 11 { 12 return v::stringType() 13 ->exactLength(8) 14 ->contains("-") 15 ->contains("/^[A-Z]{3}/") 16 ->contains("/[0-9]{4}$/") 17 ->setName('Car plate') 18 ->validate($mixed); 19 } 20 }

O QUE QUEREMOS

1 <?php 2 3 namespace EasyTaxi\Driver; 4 5 class Car 6 { 7 /** 8 * @PlateValidator 9 */ 10 private $plate = ''; 11 12 public function __construct($plate) 13 { 14 $this->plate = $plate; 15 } 16 }

1 <?php 2 3 use EasyTaxi\Annotation; 4 use EasyTaxi\Validation; 5 6 $filter = new Annotation\Filter(); 7 $factory = new Annotation\Factory($filter); 8 $validator = new Validation\Validator($factory); 9 $fusca = new Car('AAA-1111'); 10 11 $validator->annotations($fusca);

FILTRANDO A PARTE

INTERESSANTE DE UM

COMENTÁRIO

1 <?php 2 3 namespace EasyTaxi\Annotation; 4 5 class Filter 6 { 7 public function firstAnnotation($doc): string { 8 foreach ($this->breakLines($doc) as $line) { 9 if (false === $this->hasAnnotation($line)) { 10 continue; 11 } 12 13 return $this->filterName($line); 14 } 15 } 16 17 private function breakLines($doc): array { 18 return explode(PHP_EOL, $doc); 19 } 20 21 private function hasAnnotation($line): bool { 22 return false !== strpos($line, '@'); 23 } 24 25 private function filterName($line): string { 26 return trim(str_replace(['*', '/', '@'], '', $line)); 27 } 28 }

CRIANDO REGRAS A PARTIR

DE UM COMENTÁRIO

3 namespace EasyTaxi\Annotation; 4 5 class Factory 6 { 7 private $filter = null; 8 9 public function __construct(Filter $annotationFilter) 10 { 11 $this->filter = $annotationFilter; 12 } 13 14 public function createFromProperty($instance, $propertyName) 15 { 16 $object = new \ReflectionObject($instance); 17 $property = $object->getProperty($propertyName); 18 19 return $this->createInstanceFromComment($property->getDocComment()); 20 } 21 22 private function createInstanceFromComment($doc) 23 { 24 $annotationClass = $this->filter->firstAnnotation($doc); 25 $class = new \ReflectionClass($annotationClass); 26 27 return $class->newInstance(); 28 } 29 }

JUNTANDO TUDO NUM

MONTINHO SÓ

3 namespace EasyTaxi\Validation; 4 5 use EasyTaxi\Annotation; 6 7 class Validator 8 { 9 private $annotationFactory = null; 10 11 public function __construct(Annotation\Factory $factory) { 12 $this->annotationFactory = $factory; 13 } 14 15 public function annotations($object) { 16 $annotation = $this->annotationFactory; 17 $class = new \ReflectionObject($object); 18 $properties = $class->getProperties(); 19 foreach ($properties as $property) { 20 $propertyName = $property->getName(); 21 $rule = $annotation->createFromProperty($object, $propertyName); 22 if ($rule->isValid($object)) { 23 continue; 24 } 25 26 throw new \Exception("$propertyName is not valid."); 27 } 28 } 29 }

1 <?php 2 3 use EasyTaxi\Annotation; 4 use EasyTaxi\Validation; 5 6 $filter = new Annotation\Filter(); 7 $factory = new Annotation\Factory($filter); 8 $validator = new Validation\Validator($factory); 9 $fusca = new Car('AAA-1111'); 10 11 $validator->annotation($fusca);

OUTROS EXEMPLOS

BEHAT

COMPOSER

PHING

DQL

PHPUNIT MOCK OBJECTS

A VIDA DE UMA

DSL

DOMÍNIO

DSL INTERNADOMÍNIO

DSL INTERNA

DSL EXTERNADOMÍNIO

DSL INTERNA

DSL EXTERNA

LIMITES DE UMA

DSL

AUTOMATIZAR TAREFAS

REPETITIVAS

<TARGET NAME=“TEST”>

<TARGET NAME=“DEPLOY”>

<TARGET NAME=“BUILD”>

<CONDITION>

<CONDITION>

FAIL

A SEGUIR, UMA

MENSAGEM

1 <?php 2 3 use Respect\Validation\Validator as v; 4 5 v::stringType() 6 ->exactLength(8) 7 ->contains("-") 8 ->contains("/^[A-Z]{3}/") 9 ->contains("/[0-9]{4}$/") 10 ->assert($something);

A VIDA DE UMA

MENSAGEM

99% JAPA MAS

AQUELE 1% É ITALIANO

PARA QUEM

VOCÊ ESTÁ FALANDO

PARA QUEM

VOCÊ ESTÁ CODANDO

UMA BOA

MENSAGEM TEM LIMITES

FAZ USO DE

CONHECIMENTO PRÉVIO

FAZ USO DE UM

VOCABULÁRIO COMUM

DOMÍNIOS

QUE #%$!*&

SÃO

DSLS ?

DSLS SÃO

BOAS MENSAGENS

DEPENDEM DE BONS

DOMÍNIOS

SÃO MAIS ESPECÍFICAS

DO QUE LINGUAGENS GENÉRICAS

POR ISSO COMUNICAM

MAIS COISAS

DESENVOLVIMENTO É SOBRE

COMUNICAÇÃO

PERGUNTAS?

AGRADECIMENTOS

@NELSONSAR@IVONASCIMENTO

@ALGANET @ITEASYTAXI

HTTP://BIT.LY/PHPX-DSLS