Skip to content

Commit b5f72e9

Browse files
committed
Add tests and reorganize code based on further refined requirements and feedback
1 parent 4d936c0 commit b5f72e9

File tree

7 files changed

+130
-210
lines changed

7 files changed

+130
-210
lines changed

src/ActionManager.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public function getDesignPatterns(): array
7373
public function registerDesignPattern(DesignPattern $designPattern): ActionManager
7474
{
7575
$this->designPatterns[] = $designPattern;
76-
76+
7777
return $this;
7878
}
7979

@@ -136,7 +136,7 @@ public function identifyFromBacktrace($usedTraits, ?BacktraceFrame &$frame = nul
136136
$designPatterns = $this->getDesignPatternsMatching($usedTraits);
137137
$backtraceOptions = DEBUG_BACKTRACE_PROVIDE_OBJECT
138138
| DEBUG_BACKTRACE_IGNORE_ARGS;
139-
139+
140140
$ownNumberOfFrames = 2;
141141
$frames = array_slice(
142142
debug_backtrace($backtraceOptions, $ownNumberOfFrames + $this->backtraceLimit),

src/Decorators/PipelineDecorator.php

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,45 +16,12 @@ public function __construct($action)
1616
}
1717

1818
public function __invoke(mixed ...$arguments): mixed
19-
{
20-
return $this->handleFromAnyMethod(...$arguments);
21-
}
22-
23-
public function handle(mixed ...$arguments): mixed
24-
{
25-
return $this->handleFromAnyMethod(...$arguments);
26-
}
27-
28-
/**
29-
* Typical pipeline behavior expects two things:
30-
*
31-
* 1) The pipe class to expect a single incoming parameter (along with
32-
* a closure) and single return value.
33-
* 2) The pipe class to be aware of the next closure and determine what
34-
* should be passed into the next pipe.
35-
*
36-
* Because of these expectations, this behavior is asserting two opinions:
37-
*
38-
* 1) Regardless of the number of parameters provided to the asPipeline
39-
* method implemented here, only the first will be supplied to the
40-
* invoked Action.
41-
* 2) If the invoked Action does not return anything, then the next
42-
* closure will be supplied the same parameter. However, if the
43-
* invoked action does return a non-null value, that value will
44-
* be supplied to the next closure.
45-
*/
46-
protected function handleFromAnyMethod(mixed ...$arguments): mixed
4719
{
4820
$passable = array_shift($arguments);
4921
$closure = array_pop($arguments);
50-
$returned = null;
5122

52-
if ($this->hasMethod('asPipeline')) {
53-
$returned = $this->callMethod('asPipeline', [$passable]);
54-
} elseif ($this->hasMethod('handle')) {
55-
$returned = $this->callMethod('handle', [$passable]);
56-
}
23+
$method = $this->hasMethod('asPipeline') ? 'asPipeline' : 'handle';
5724

58-
return $closure($returned ?? $passable);
25+
return $closure($this->callMethod($method, [$passable]) ?? $passable);
5926
}
6027
}

tests/AsPipelinePassable.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Lorisleiva\Actions\Tests;
4+
5+
/**
6+
* Test fixture used in all of the AsPipeline{*} tests.
7+
*/
8+
class AsPipelinePassable
9+
{
10+
public function __construct(public int $count = 0)
11+
{
12+
//
13+
}
14+
15+
public function increment()
16+
{
17+
$this->count++;
18+
}
19+
}

tests/AsPipelineTest.php

Lines changed: 15 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,10 @@
22

33
namespace Lorisleiva\Actions\Tests;
44

5-
use ArgumentCountError;
6-
use Closure;
75
use Illuminate\Support\Facades\Pipeline;
86
use Lorisleiva\Actions\Concerns\AsAction;
9-
use Lorisleiva\Actions\Concerns\AsPipeline;
107

11-
class AsPipelinePassable
12-
{
13-
public function __construct(public int $count = 0)
14-
{
15-
//
16-
}
17-
18-
public function increment()
19-
{
20-
$this->count++;
21-
}
22-
}
23-
24-
class AsPipelineExplicitTest
25-
{
26-
use AsPipeline;
27-
28-
public function handle(AsPipelinePassable $passable): void
29-
{
30-
$passable->increment();
31-
}
32-
33-
public function asPipeline(AsPipelinePassable $passable): AsPipelinePassable
34-
{
35-
$this->handle($passable);
36-
37-
return $passable;
38-
}
39-
}
40-
41-
class AsPipelineImplicitTest
8+
class AsPipelineTest
429
{
4310
use AsAction;
4411

@@ -55,156 +22,31 @@ public function asPipeline(AsPipelinePassable $passable): AsPipelinePassable
5522
}
5623
}
5724

58-
class AsPipelineMultipleParamTest
59-
{
60-
use AsAction;
61-
62-
public function handle(AsPipelinePassable $passable): void
63-
{
64-
$passable->increment();
65-
}
66-
67-
public function asPipeline(AsPipelinePassable $passable, int $foo): AsPipelinePassable
68-
{
69-
$this->handle($passable);
70-
71-
return $passable;
72-
}
73-
}
74-
75-
class AsPipelineSingleParamHandleOnlyTest
76-
{
77-
use AsAction;
78-
79-
public function handle(AsPipelinePassable $passable): void
80-
{
81-
$passable->increment();
82-
}
83-
}
84-
85-
class AsPipelineMultipleParamHandleOnlyTest
86-
{
87-
use AsAction;
88-
89-
public function handle(AsPipelinePassable $passable, int $foo): void
90-
{
91-
$passable->increment();
92-
}
93-
}
94-
95-
class AsPipelineWithoutHandleOrAsPipeline
96-
{
97-
use AsAction;
98-
}
99-
100-
function getAnonymous() {
101-
return function (AsPipelinePassable $p, $next) {
102-
$p->increment();
103-
104-
return $next($p);
105-
};
106-
}
107-
108-
function getPassable() {
109-
return new AsPipelinePassable;
110-
}
111-
112-
it('can run as a pipe in a pipeline, with explicit trait', function () {
113-
$anonymous = getAnonymous();
114-
$passable = Pipeline::send(getPassable())
115-
->through([
116-
AsPipelineExplicitTest::class,
117-
$anonymous,
118-
AsPipelineExplicitTest::class,
119-
$anonymous,
120-
])
121-
->thenReturn();
122-
123-
expect(is_object($passable))->toBe(true);
124-
expect($passable->count)->toBe(4);
125-
});
126-
127-
it('can run as a pipe in a pipeline, with implicit trait', function () {
128-
$anonymous = getAnonymous();
129-
$passable = Pipeline::send(getPassable())
130-
->through([
131-
AsPipelineImplicitTest::class,
132-
$anonymous,
133-
AsPipelineImplicitTest::class,
134-
$anonymous,
135-
])
136-
->thenReturn();
137-
138-
expect(is_object($passable))->toBe(true);
139-
expect($passable->count)->toBe(4);
140-
});
141-
142-
it('can run as a pipe in a pipeline, without an explicit asPipeline method', function () {
143-
$anonymous = getAnonymous();
144-
$passable = Pipeline::send(getPassable())
25+
it('can run as a pipe in a pipeline, with an explicit asPipeline method', function () {
26+
$passable = Pipeline::send(new AsPipelinePassable)
14527
->through([
146-
AsPipelineSingleParamHandleOnlyTest::class,
147-
$anonymous,
148-
AsPipelineSingleParamHandleOnlyTest::class,
149-
$anonymous,
28+
AsPipelineTest::class,
29+
AsPipelineTest::class,
30+
AsPipelineTest::class,
31+
AsPipelineTest::class,
15032
])
15133
->thenReturn();
15234

153-
expect(is_object($passable))->toBe(true);
35+
expect(is_a($passable, AsPipelinePassable::class))->toBe(true);
15436
expect($passable->count)->toBe(4);
15537
});
15638

157-
it('can run as a noop/passthrough pipe in a pipeline, without a handle or asPipeline method', function () {
158-
$anonymous = getAnonymous();
159-
$passable = Pipeline::send(getPassable())
160-
->through([
161-
AsPipelineWithoutHandleOrAsPipeline::class,
162-
$anonymous,
163-
AsPipelineWithoutHandleOrAsPipeline::class,
164-
$anonymous,
165-
])
166-
->thenReturn();
167-
168-
expect(is_object($passable))->toBe(true);
169-
expect($passable->count)->toBe(2);
170-
});
171-
17239
it('can run with an arbitrary via method configured on Pipeline', function () {
173-
$anonymous = getAnonymous();
174-
$passable = Pipeline::send(getPassable())
175-
->via('foobar')
40+
$passable = Pipeline::send(new AsPipelinePassable)
41+
->via('arbitraryMethodThatDoesNotExistOnTheAction')
17642
->through([
177-
AsPipelineImplicitTest::class,
178-
$anonymous,
179-
AsPipelineImplicitTest::class,
180-
$anonymous,
43+
AsPipelineTest::class,
44+
AsPipelineTest::class,
45+
AsPipelineTest::class,
46+
AsPipelineTest::class,
18147
])
18248
->thenReturn();
18349

184-
expect(is_object($passable))->toBe(true);
50+
expect(is_a($passable, AsPipelinePassable::class))->toBe(true);
18551
expect($passable->count)->toBe(4);
18652
});
187-
188-
it('cannot run as a pipe in a pipeline, with an explicit asPipeline method expecting multiple non-optional params', function () {
189-
$anonymous = getAnonymous();
190-
$passable = Pipeline::send(getPassable())
191-
->through([
192-
AsPipelineMultipleParamTest::class,
193-
$anonymous,
194-
AsPipelineMultipleParamTest::class,
195-
$anonymous,
196-
])
197-
->thenReturn();
198-
})->throws(ArgumentCountError::class, 'Too few arguments to function Lorisleiva\Actions\Tests\AsPipelineMultipleParamTest::asPipeline(), 1 passed and exactly 2 expected');
199-
200-
it('cannot run as a pipe in a pipeline, without an explicit asPipeline method and multiple non-optional handle params', function () {
201-
$anonymous = getAnonymous();
202-
$passable = Pipeline::send(getPassable())
203-
->through([
204-
AsPipelineMultipleParamHandleOnlyTest::class,
205-
$anonymous,
206-
AsPipelineMultipleParamHandleOnlyTest::class,
207-
$anonymous,
208-
])
209-
->thenReturn();
210-
})->throws(ArgumentCountError::class, 'Too few arguments to function Lorisleiva\Actions\Tests\AsPipelineMultipleParamHandleOnlyTest::handle(), 1 passed and exactly 2 expected');
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Lorisleiva\Actions\Tests;
4+
5+
use Illuminate\Support\Facades\Pipeline;
6+
use Lorisleiva\Actions\Concerns\AsPipeline;
7+
8+
class AsPipelineWithExplicitTraitTest
9+
{
10+
use AsPipeline;
11+
12+
public function handle(AsPipelinePassable $passable): void
13+
{
14+
$passable->increment();
15+
}
16+
}
17+
18+
it('can run as a pipe in a pipeline, with explicit trait, without asPipeline method', function () {
19+
$passable = Pipeline::send(new AsPipelinePassable)
20+
->through([
21+
AsPipelineWithExplicitTraitTest::class,
22+
AsPipelineWithExplicitTraitTest::class,
23+
AsPipelineWithExplicitTraitTest::class,
24+
AsPipelineWithExplicitTraitTest::class,
25+
])
26+
->thenReturn();
27+
28+
expect(is_a($passable, AsPipelinePassable::class))->toBe(true);
29+
expect($passable->count)->toBe(4);
30+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Lorisleiva\Actions\Tests;
4+
5+
use Illuminate\Support\Facades\Pipeline;
6+
use Lorisleiva\Actions\Concerns\AsAction;
7+
8+
class AsPipelineWithImplicitTraitTest
9+
{
10+
use AsAction;
11+
12+
public function handle(AsPipelinePassable $passable): void
13+
{
14+
$passable->increment();
15+
}
16+
}
17+
18+
it('can run as a pipe in a pipeline, with implicit trait, without asPipeline method', function () {
19+
$passable = Pipeline::send(new AsPipelinePassable)
20+
->through([
21+
AsPipelineWithImplicitTraitTest::class,
22+
AsPipelineWithImplicitTraitTest::class,
23+
AsPipelineWithImplicitTraitTest::class,
24+
AsPipelineWithImplicitTraitTest::class,
25+
])
26+
->thenReturn();
27+
28+
expect(is_a($passable, AsPipelinePassable::class))->toBe(true);
29+
expect($passable->count)->toBe(4);
30+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Lorisleiva\Actions\Tests;
4+
5+
use ArgumentCountError;
6+
use Illuminate\Support\Facades\Pipeline;
7+
use Lorisleiva\Actions\Concerns\AsAction;
8+
9+
class AsPipelineWithMultipleNonOptionalParametersTest
10+
{
11+
use AsAction;
12+
13+
public function handle(AsPipelinePassable $passable, int $nonOptionalAdditionalParameter): void
14+
{
15+
$passable->increment();
16+
}
17+
18+
public function asPipeline(AsPipelinePassable $passable): AsPipelinePassable
19+
{
20+
$this->handle($passable);
21+
22+
return $passable;
23+
}
24+
}
25+
26+
it('cannot run as a pipe in a pipeline expecting multiple non-optional parameters', function () {
27+
$passable = Pipeline::send(new AsPipelinePassable)
28+
->through([
29+
AsPipelineWithMultipleNonOptionalParametersTest::class,
30+
])
31+
->thenReturn();
32+
})->throws(ArgumentCountError::class);

0 commit comments

Comments
 (0)