add pipeline
This commit is contained in:
parent
17c96b2834
commit
5c640b578c
48
src/Utils/FunctionPipeline.php
Normal file
48
src/Utils/FunctionPipeline.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2018 Kacper Donat
|
||||
*
|
||||
* @author Kacper "Kadet" Donat <kacper@kadet.net>
|
||||
*
|
||||
* Full license available in separate LICENSE file
|
||||
*/
|
||||
|
||||
namespace Kadet\Functional\Utils;
|
||||
|
||||
|
||||
class FunctionPipeline
|
||||
{
|
||||
/**
|
||||
* @var callable[]
|
||||
*/
|
||||
private $functions = [];
|
||||
|
||||
/**
|
||||
* FunctionPipeline constructor.
|
||||
*
|
||||
* @param callable[] $functions
|
||||
*/
|
||||
public function __construct(callable ...$functions)
|
||||
{
|
||||
$this->functions = $functions;
|
||||
}
|
||||
|
||||
public function append(callable $callable)
|
||||
{
|
||||
return new static(...array_merge($this->functions, [ $callable ]));
|
||||
}
|
||||
|
||||
public function prepend(callable $callable)
|
||||
{
|
||||
return new static($callable, ...$this->functions);
|
||||
}
|
||||
|
||||
public function __invoke(...$args)
|
||||
{
|
||||
return array_reduce($this->functions, function ($arg, $callable) {
|
||||
return $arg instanceof UnpackedArgs
|
||||
? $callable(...$arg->getArrayCopy())
|
||||
: $callable($arg);
|
||||
}, new UnpackedArgs($args));
|
||||
}
|
||||
}
|
13
src/Utils/UnpackedArgs.php
Normal file
13
src/Utils/UnpackedArgs.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2018 Kacper Donat
|
||||
*
|
||||
* @author Kacper "Kadet" Donat <kacper@kadet.net>
|
||||
*
|
||||
* Full license available in separate LICENSE file
|
||||
*/
|
||||
|
||||
namespace Kadet\Functional\Utils;
|
||||
|
||||
|
||||
class UnpackedArgs extends \ArrayObject { }
|
@ -10,6 +10,7 @@
|
||||
namespace Kadet\Functional;
|
||||
|
||||
use Kadet\Functional\Utils\CurriedFunction;
|
||||
use Kadet\Functional\Utils\FunctionPipeline;
|
||||
use Kadet\Functional\Utils\PartiallyAppliedFunction;
|
||||
|
||||
function partial(callable $callable, ...$args): PartiallyAppliedFunction
|
||||
@ -25,4 +26,9 @@ function curry(callable $callable, int $threshold = null, ...$args): CurriedFunc
|
||||
function uncurry(CurriedFunction $curried)
|
||||
{
|
||||
return $curried->uncurry();
|
||||
}
|
||||
}
|
||||
|
||||
function pipe(...$functions)
|
||||
{
|
||||
return new FunctionPipeline(...$functions);
|
||||
}
|
||||
|
@ -78,4 +78,4 @@ function reflect(callable $callable): \ReflectionFunctionAbstract
|
||||
}
|
||||
}
|
||||
|
||||
define(__NAMESPACE__.'\\_', symbol());
|
||||
define(__NAMESPACE__.'\\_', symbol('placeholder'));
|
95
tests/FunctionPipelineTest.php
Normal file
95
tests/FunctionPipelineTest.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2018 Kacper Donat
|
||||
*
|
||||
* @author Kacper "Kadet" Donat <kacper@kadet.net>
|
||||
*
|
||||
* Full license available in separate LICENSE file
|
||||
*/
|
||||
|
||||
use \Kadet\Functional as f;
|
||||
|
||||
class FunctionPipelineTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/** @var \PHPUnit\Framework\MockObject\MockObject */
|
||||
private $mock;
|
||||
|
||||
protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */
|
||||
{
|
||||
$this->mock = $this
|
||||
->getMockBuilder('stdClass')
|
||||
->setMethods(['a', 'b', 'c'])
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testPipelineCalling()
|
||||
{
|
||||
$pipeline = f\pipe(
|
||||
[$this->mock, 'a'],
|
||||
[$this->mock, 'b']
|
||||
);
|
||||
|
||||
$this->mock->method('a')->willReturn('b');
|
||||
$this->mock->method('b')->willReturn('c');
|
||||
|
||||
$this->mock->expects($this->once())->method('a')->with('a');
|
||||
$this->mock->expects($this->once())->method('b')->with('b');
|
||||
|
||||
$this->assertSame('c', $pipeline('a'));
|
||||
}
|
||||
|
||||
public function testPipelineForward()
|
||||
{
|
||||
$pipeline = f\pipe(
|
||||
[$this->mock, 'a']
|
||||
);
|
||||
|
||||
$this->mock->method('a')->willReturn('b');
|
||||
|
||||
$this->mock->expects($this->once())->method('a')->with('a');
|
||||
|
||||
$this->assertSame('b', $pipeline('a'));
|
||||
}
|
||||
|
||||
public function testPipelineAppend()
|
||||
{
|
||||
$pipeline = f\pipe(
|
||||
[$this->mock, 'a'],
|
||||
[$this->mock, 'b']
|
||||
);
|
||||
|
||||
$second = $pipeline->append([$this->mock, 'c']);
|
||||
|
||||
$this->mock->method('a')->willReturn('b');
|
||||
$this->mock->method('b')->willReturn('c');
|
||||
$this->mock->method('c')->willReturn('d');
|
||||
|
||||
$this->mock->expects($this->exactly(2))->method('a')->with('a');
|
||||
$this->mock->expects($this->exactly(2))->method('b')->with('b');
|
||||
$this->mock->expects($this->once())->method('c')->with('c');
|
||||
|
||||
$this->assertSame('c', $pipeline('a'));
|
||||
$this->assertSame('d', $second('a'));
|
||||
}
|
||||
|
||||
public function testPipelinePrepend()
|
||||
{
|
||||
$pipeline = f\pipe(
|
||||
[$this->mock, 'b'],
|
||||
[$this->mock, 'c']
|
||||
);
|
||||
|
||||
$second = $pipeline->prepend([$this->mock, 'a']);
|
||||
|
||||
$this->mock->method('a')->willReturn('b');
|
||||
$this->mock->method('b')->willReturn('c');
|
||||
$this->mock->method('c')->willReturn('d');
|
||||
|
||||
$this->mock->expects($this->exactly(1))->method('a')->with('a');
|
||||
$this->mock->expects($this->exactly(2))->method('b')->with('b');
|
||||
$this->mock->expects($this->exactly(2))->method('c')->with('c');
|
||||
|
||||
$this->assertSame('d', $pipeline('b'));
|
||||
$this->assertSame('d', $second('a'));
|
||||
}
|
||||
}
|
@ -8,4 +8,4 @@
|
||||
*/
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
require_once __DIR__.'/RandomDataTrait.php';
|
||||
require_once __DIR__.'/RandomDataTrait.php';
|
||||
|
Loading…
Reference in New Issue
Block a user