Skip to content

Commit 095236e

Browse files
committed
WIP on supporting batches, chains
1 parent 90753b2 commit 095236e

20 files changed

+506
-92
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
/vendor
21
composer.lock
32
/.idea
3+
.phpunit.result.cache
4+
/vendor

src/Contracts/CacheKeyProvider.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55

66

77
use Closure;
8-
use Illuminate\Foundation\Bus\Dispatchable;
98

109
interface CacheKeyProvider
1110
{
1211
/**
13-
* @param Dispatchable|Closure $job
12+
* @param \Illuminate\Foundation\Bus\Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
1413
* @return string
1514
*/
1615
public function getKey($job): string;

src/Debouncer.php

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@
55

66

77
use Closure;
8-
use DateInterval;
9-
use DateTimeInterface;
108
use Illuminate\Bus\Queueable;
119
use Illuminate\Foundation\Bus\Dispatchable;
12-
use Illuminate\Foundation\Bus\PendingDispatch;
1310
use Illuminate\Support\Facades\Cache;
1411
use Mpbarlow\LaravelQueueDebouncer\Contracts\CacheKeyProvider;
1512
use Mpbarlow\LaravelQueueDebouncer\Contracts\UniqueIdentifierProvider;
@@ -34,10 +31,24 @@ public function __construct(
3431
$this->factory = $factory;
3532
}
3633

34+
public function usingCacheKeyProvider(CacheKeyProvider $provider): self
35+
{
36+
$this->keyProvider = $provider;
37+
38+
return $this;
39+
}
40+
41+
public function usingUniqueIdentifierProvider(UniqueIdentifierProvider $provider): self
42+
{
43+
$this->idProvider = $provider;
44+
45+
return $this;
46+
}
47+
3748
/**
38-
* @param Dispatchable|Closure $job
39-
* @param DateTimeInterface|DateInterval|int|null $wait
40-
* @return PendingDispatch
49+
* @param Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
50+
* @param \DateTimeInterface|\DateInterval|int|null $wait
51+
* @return \Illuminate\Foundation\Bus\PendingDispatch
4152
*/
4253
public function __invoke($job, $wait)
4354
{
@@ -52,9 +63,9 @@ public function __invoke($job, $wait)
5263
}
5364

5465
/**
55-
* @param Dispatchable|Closure $job
56-
* @param DateTimeInterface|DateInterval|int|null $wait
57-
* @return PendingDispatch
66+
* @param Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
67+
* @param \DateTimeInterface|\DateInterval|int|null $wait
68+
* @return \Illuminate\Foundation\Bus\PendingDispatch
5869
*/
5970
public function debounce($job, $wait)
6071
{

src/DispatcherFactory.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66

77
use Closure;
8-
use Illuminate\Foundation\Bus\Dispatchable;
98
use Illuminate\Support\Facades\Cache;
109

1110
use function dispatch;
11+
use function get_class;
12+
use function in_array;
1213

1314
class DispatcherFactory
1415
{
@@ -18,7 +19,7 @@ class DispatcherFactory
1819
* If we used a class as the dispatcher, we would have to check whether the job is a closure ourselves, and
1920
* serialise it if it was.
2021
*
21-
* @param Dispatchable|Closure $job
22+
* @param \Illuminate\Foundation\Bus\Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
2223
* @param string $key
2324
* @param string $identifier
2425
* @return Closure
@@ -29,8 +30,25 @@ public function makeDispatcher($job, $key, $identifier): Closure
2930
if (Cache::get($key) == $identifier) {
3031
Cache::forget($key);
3132

32-
dispatch($job);
33+
if ($this->hasDispatchMethod($job)) {
34+
$job->dispatch();
35+
} else {
36+
dispatch($job);
37+
}
3338
}
3439
};
3540
}
41+
42+
/**
43+
* @param \Illuminate\Foundation\Bus\Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
44+
* @return bool
45+
*/
46+
protected function hasDispatchMethod($job)
47+
{
48+
// PendingBatch is not available in Laravel < 8.0, hence the string comparison.
49+
return in_array(
50+
get_class($job),
51+
['\Illuminate\Foundation\Bus\PendingChain', '\Illuminate\Bus\PendingBatch']
52+
);
53+
}
3654
}

src/Facade/Debouncer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
/**
1010
* @see \Mpbarlow\LaravelQueueDebouncer\Debouncer
11-
* @method static \Illuminate\Foundation\Bus\PendingDispatch debounce(\Illuminate\Foundation\Bus\Dispatchable|\Closure $job, \DateTimeInterface|\DateInterval|int|null $wait)
11+
* @method static \Illuminate\Foundation\Bus\PendingDispatch debounce(\Illuminate\Foundation\Bus\Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|\Closure $job, \DateTimeInterface|\DateInterval|int|null $wait)
1212
*/
1313
class Debouncer extends Facade
1414
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
4+
namespace Mpbarlow\LaravelQueueDebouncer\Support;
5+
6+
7+
use Mpbarlow\LaravelQueueDebouncer\Contracts\CacheKeyProvider as CacheKeyProviderContract;
8+
9+
use function config;
10+
use function sha1;
11+
12+
class ParameterAwareCacheKeyProvider implements CacheKeyProviderContract
13+
{
14+
public function getKey($job): string
15+
{
16+
return config('queue_debouncer.cache_prefix') . ':' . sha1(\Opis\Closure\serialize($job));
17+
}
18+
}

src/Support/helpers.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
<?php
22

3-
use Illuminate\Foundation\Bus\Dispatchable;
4-
use Illuminate\Foundation\Bus\PendingDispatch;
53
use Mpbarlow\LaravelQueueDebouncer\Debouncer;
64

75
if (! function_exists('debounce')) {
86
/**
9-
* @param Dispatchable|Closure $job
7+
* @param \Illuminate\Foundation\Bus\Dispatchable|\Illuminate\Foundation\Bus\PendingChain|\Illuminate\Bus\PendingBatch|Closure $job
108
* @param DateTimeInterface|DateInterval|int|null $wait
11-
* @return PendingDispatch
9+
* @return \Illuminate\Foundation\Bus\PendingDispatch
1210
*/
1311
function debounce($job, $wait)
1412
{

tests/CacheKeyProviderTest.php

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,14 @@
44
namespace Mpbarlow\LaravelQueueDebouncer\Tests;
55

66

7-
use Illuminate\Foundation\Bus\Dispatchable;
87
use Mpbarlow\LaravelQueueDebouncer\Support\CacheKeyProvider;
9-
use ReflectionFunction;
8+
use Mpbarlow\LaravelQueueDebouncer\Tests\Support\DummyJob;
109

11-
use function sha1;
1210
use function strlen;
1311
use function substr;
1412

1513
class CacheKeyProviderTest extends TestCase
1614
{
17-
/** @test */
18-
public function it_applies_the_cache_prefix()
19-
{
20-
$this->app['config']->set('queue_debouncer.cache_prefix', $prefix = '__PREFIX__');
21-
22-
$provider = new CacheKeyProvider();
23-
24-
$closureJob = function () {};
25-
26-
$this->assertEquals($prefix . ':' . DummyJob::class, $provider->getKey(new DummyJob()));
27-
28-
$this->assertEquals(
29-
$prefix . ':' . sha1((string)(new ReflectionFunction($closureJob))),
30-
$provider->getKey($closureJob)
31-
);
32-
}
33-
3415
/** @test */
3516
public function it_uses_the_class_name_for_job_classes()
3617
{
@@ -44,37 +25,4 @@ public function it_uses_the_class_name_for_job_classes()
4425
substr($provider->getKey(new DummyJob()), strlen($prefix) + 1)
4526
);
4627
}
47-
48-
/** @test */
49-
public function it_generates_a_unique_key_for_closures()
50-
{
51-
$provider = new CacheKeyProvider();
52-
53-
$job1 = function () {};
54-
$job2 = function () {};
55-
56-
$key1 = $provider->getKey($job1);
57-
$key2 = $provider->getKey($job2);
58-
59-
// Assert different closures result in different keys.
60-
$this->assertNotEquals($key1, $key2);
61-
}
62-
63-
/** @test */
64-
public function it_generates_a_consistent_key_for_closures()
65-
{
66-
$provider = new CacheKeyProvider();
67-
68-
$job = function () {};
69-
70-
$key1 = $provider->getKey($job);
71-
$key2 = $provider->getKey($job);
72-
73-
// Assert the same closure results in the same key each time.
74-
$this->assertEquals($key1, $key2);
75-
}
76-
}
77-
78-
class DummyJob {
79-
use Dispatchable;
8028
}

tests/DebounceTest.php

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@
44
namespace Mpbarlow\LaravelQueueDebouncer\Tests;
55

66

7-
use Illuminate\Foundation\Bus\Dispatchable;
7+
use Illuminate\Bus\PendingBatch;
8+
use Illuminate\Foundation\Bus\PendingChain;
89
use Illuminate\Queue\CallQueuedClosure;
910
use Illuminate\Support\Facades\Bus;
1011
use Illuminate\Support\Facades\Cache;
1112
use Mpbarlow\LaravelQueueDebouncer\Debouncer;
1213
use Mpbarlow\LaravelQueueDebouncer\DispatcherFactory;
1314
use Mpbarlow\LaravelQueueDebouncer\Support\CacheKeyProvider;
15+
use Mpbarlow\LaravelQueueDebouncer\Support\ParameterAwareCacheKeyProvider;
1416
use Mpbarlow\LaravelQueueDebouncer\Support\UniqueIdentifierProvider;
17+
use Mpbarlow\LaravelQueueDebouncer\Tests\Support\CustomCacheKeyProvider;
18+
use Mpbarlow\LaravelQueueDebouncer\Tests\Support\CustomUniqueIdentifierProvider;
19+
use Mpbarlow\LaravelQueueDebouncer\Tests\Support\DummyJob;
1520
use ReflectionFunction;
1621

22+
use function class_exists;
1723

1824
class DebounceTest extends TestCase
1925
{
@@ -44,8 +50,10 @@ public function it_injects_the_configured_cache_key_provider_and_unique_identifi
4450
* @test
4551
* @dataProvider jobProvider
4652
*/
47-
public function it_debounces_jobs($job, $expectedDispatch)
53+
public function it_debounces_jobs($provider)
4854
{
55+
[$job, $expectedDispatch, $keyProvider] = $provider();
56+
4957
// So this is pretty tricky to test. It's a job that dispatches another job, so when we're faking the bus we
5058
// have to get a bit creative.
5159
Bus::fake();
@@ -57,13 +65,14 @@ public function it_debounces_jobs($job, $expectedDispatch)
5765
->andReturn(1, 2, 3);
5866
});
5967

60-
$key = $this->app->make(CacheKeyProvider::class)->getKey($job);
68+
$key = $this->app->make($keyProvider)->getKey($job);
6169
$debouncer = $this->app->make(Debouncer::class);
6270

6371
// Then we call the debouncer three times, asserting that each time, it's setting the cache value correctly,
6472
// and dispatching a runner closure with the values we expect.
6573
foreach ([1, 2, 3] as $i) {
6674
$debouncer($job, PHP_INT_MAX);
75+
6776
$this->assertEquals($i, Cache::get($key));
6877

6978
Bus::assertDispatched(CallQueuedClosure::class, function ($dispatch) use ($job, $key, $i) {
@@ -101,19 +110,63 @@ public function it_debounces_jobs($job, $expectedDispatch)
101110

102111
public function jobProvider(): array
103112
{
104-
return [
105-
[new Job(), Job::class],
106-
[function () {}, CallQueuedClosure::class]
113+
// Annoyingly we have to wrap each data set in a function as data providers run before the container is
114+
// initialised. Each provides the job to debounce, the class we ultimately expect to be dispatched, and the
115+
// cache key provider to use for the test.
116+
117+
// Closures and standard classes should work with both included cache key providers.
118+
// Chains and batches only work with the serialisation-based key provider.
119+
$jobTypes = [
120+
[
121+
function () {
122+
return [
123+
function () {
124+
},
125+
CallQueuedClosure::class,
126+
CacheKeyProvider::class
127+
];
128+
}
129+
],
130+
[
131+
function () {
132+
return [
133+
function () {
134+
},
135+
CallQueuedClosure::class,
136+
ParameterAwareCacheKeyProvider::class
137+
];
138+
}
139+
],
140+
[
141+
function () {
142+
return [new DummyJob(), DummyJob::class, CacheKeyProvider::class];
143+
}
144+
],
145+
[
146+
function () {
147+
return [new DummyJob(), DummyJob::class, ParameterAwareCacheKeyProvider::class];
148+
}
149+
],
150+
[
151+
function () {
152+
return [
153+
DummyJob::withChain([new DummyJob()]),
154+
PendingChain::class,
155+
ParameterAwareCacheKeyProvider::class
156+
];
157+
}
158+
]
107159
];
108-
}
109-
}
110160

111-
class Job
112-
{
113-
use Dispatchable;
161+
// PendingBatch is only available in Laravel >= 8.0.
162+
if (class_exists('\Illuminate\Bus\PendingBatch')) {
163+
$jobTypes[] = [
164+
function () {
165+
return [Bus::batch([new DummyJob()]), PendingBatch::class, ParameterAwareCacheKeyProvider::class];
166+
}
167+
];
168+
}
114169

115-
public function handle()
116-
{
117-
//
170+
return $jobTypes;
118171
}
119172
}

0 commit comments

Comments
 (0)