Skip to content

Commit

Permalink
Events for Feature Updates and More (#59)
Browse files Browse the repository at this point in the history
* + Added more events

* - No scope or value, so drop serialization

* ~ Fixed after review

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
tylernathanreed and taylorotwell authored May 4, 2023
1 parent 9c0d9ef commit 50e3a73
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 5 deletions.
26 changes: 21 additions & 5 deletions src/Drivers/Decorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
use Closure;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Lottery;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use Laravel\Pennant\Contracts\Driver as DriverContract;
use Laravel\Pennant\Contracts\FeatureScopeable;
use Laravel\Pennant\Events\AllFeaturesPurged;
use Laravel\Pennant\Events\DynamicallyRegisteringFeatureClass;
use Laravel\Pennant\Events\FeatureDeleted;
use Laravel\Pennant\Events\FeatureResolved;
use Laravel\Pennant\Events\FeatureRetrieved;
use Laravel\Pennant\Events\FeaturesPurged;
use Laravel\Pennant\Events\FeatureUpdated;
use Laravel\Pennant\Events\FeatureUpdatedForAllScopes;
use Laravel\Pennant\Events\UnexpectedNullScopeEncountered;
use Laravel\Pennant\LazilyResolvedFeature;
use Laravel\Pennant\PendingScopedFeatureInteraction;
Expand Down Expand Up @@ -134,7 +140,7 @@ public function define($feature, $resolver = null): void
return $this->resolve($feature, $resolver, $scope);
}

$this->container['events']->dispatch(new UnexpectedNullScopeEncountered($feature));
Event::dispatch(new UnexpectedNullScopeEncountered($feature));

return $this->resolve($feature, fn () => false, $scope);
});
Expand All @@ -154,7 +160,7 @@ protected function resolve($feature, $resolver, $scope)

$value = $value instanceof Lottery ? $value() : $value;

$this->container['events']->dispatch(new FeatureResolved($feature, $scope, $value));
Event::dispatch(new FeatureResolved($feature, $scope, $value));

return $value;
}
Expand Down Expand Up @@ -266,15 +272,15 @@ public function get($feature, $scope): mixed
->first();

if ($item !== null) {
$this->container['events']->dispatch(new FeatureRetrieved($feature, $scope, $item['value']));
Event::dispatch(new FeatureRetrieved($feature, $scope, $item['value']));

return $item['value'];
}

return tap($this->driver->get($feature, $scope), function ($value) use ($feature, $scope) {
$this->putInCache($feature, $scope, $value);

$this->container['events']->dispatch(new FeatureRetrieved($feature, $scope, $value));
Event::dispatch(new FeatureRetrieved($feature, $scope, $value));
});
}

Expand All @@ -296,6 +302,8 @@ public function set($feature, $scope, $value): void
$this->driver->set($feature, $scope, $value);

$this->putInCache($feature, $scope, $value);

Event::dispatch(new FeatureUpdated($feature, $scope, $value));
}

/**
Expand Down Expand Up @@ -340,6 +348,8 @@ public function setForAllScopes($feature, $value): void
$this->cache = $this->cache->reject(
fn ($item) => $item['feature'] === $feature
);

Event::dispatch(new FeatureUpdatedForAllScopes($feature, $value));
}

/**
Expand All @@ -359,6 +369,8 @@ public function delete($feature, $scope): void
$this->driver->delete($feature, $scope);

$this->removeFromCache($feature, $scope);

Event::dispatch(new FeatureDeleted($feature, $scope));
}

/**
Expand All @@ -372,6 +384,8 @@ public function purge($features = null): void
$this->driver->purge(null);

$this->cache = new Collection;

Event::dispatch(new AllFeaturesPurged);
} else {
Collection::wrap($features)
->map($this->resolveFeature(...))
Expand All @@ -381,6 +395,8 @@ public function purge($features = null): void
$this->cache->forget(
$this->cache->whereInStrict('feature', $features)->keys()->all()
);

Event::dispatch(new FeaturesPurged($features->all()));
});
}
}
Expand Down Expand Up @@ -421,7 +437,7 @@ protected function ensureDynamicFeatureIsDefined($feature)
{
return tap($this->container->make($feature)->name ?? $feature, function ($name) use ($feature) {
if (! in_array($name, $this->defined())) {
$this->container['events']->dispatch(new DynamicallyRegisteringFeatureClass($feature));
Event::dispatch(new DynamicallyRegisteringFeatureClass($feature));

$this->define($feature);
}
Expand Down
8 changes: 8 additions & 0 deletions src/Events/AllFeaturesPurged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Laravel\Pennant\Events;

class AllFeaturesPurged
{
//
}
36 changes: 36 additions & 0 deletions src/Events/FeatureDeleted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Laravel\Pennant\Events;

use Illuminate\Queue\SerializesModels;

class FeatureDeleted
{
use SerializesModels;

/**
* The feature name.
*
* @var string
*/
public $feature;

/**
* The scope of the feature deletion.
*
* @var mixed
*/
public $scope;

/**
* Create a new event instance.
*
* @param string $feature
* @param mixed $scope
*/
public function __construct($feature, $scope)
{
$this->feature = $feature;
$this->scope = $scope;
}
}
45 changes: 45 additions & 0 deletions src/Events/FeatureUpdated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Laravel\Pennant\Events;

use Illuminate\Queue\SerializesModels;

class FeatureUpdated
{
use SerializesModels;

/**
* The feature name.
*
* @var string
*/
public $feature;

/**
* The scope of the feature update.
*
* @var mixed
*/
public $scope;

/**
* The new feature value.
*
* @var mixed
*/
public $value;

/**
* Create a new event instance.
*
* @param string $feature
* @param mixed $scope
* @param mixed $value
*/
public function __construct($feature, $scope, $value)
{
$this->feature = $feature;
$this->scope = $scope;
$this->value = $value;
}
}
37 changes: 37 additions & 0 deletions src/Events/FeatureUpdatedForAllScopes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Laravel\Pennant\Events;

use Illuminate\Queue\SerializesModels;

class FeatureUpdatedForAllScopes
{
use SerializesModels;

/**
* The feature name.
*
* @var string
*/
public $feature;

/**
* The new feature value.
*
* @var mixed
*/
public $value;

/**
* Create a new event instance.
*
* @param string $feature
* @param mixed $scope
* @param mixed $value
*/
public function __construct($feature, $value)
{
$this->feature = $feature;
$this->value = $value;
}
}
23 changes: 23 additions & 0 deletions src/Events/FeaturesPurged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Laravel\Pennant\Events;

class FeaturesPurged
{
/**
* The feature names.
*
* @var array
*/
public $features;

/**
* Create a new event instance.
*
* @param array $features
*/
public function __construct($features)
{
$this->features = $features;
}
}
78 changes: 78 additions & 0 deletions tests/Feature/ArrayDriverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Lottery;
use Laravel\Pennant\Contracts\FeatureScopeable;
use Laravel\Pennant\Events\AllFeaturesPurged;
use Laravel\Pennant\Events\FeatureDeleted;
use Laravel\Pennant\Events\FeatureResolved;
use Laravel\Pennant\Events\FeaturesPurged;
use Laravel\Pennant\Events\FeatureUpdated;
use Laravel\Pennant\Events\FeatureUpdatedForAllScopes;
use Laravel\Pennant\Events\UnexpectedNullScopeEncountered;
use Laravel\Pennant\Events\UnknownFeatureResolved;
use Laravel\Pennant\Feature;
Expand Down Expand Up @@ -1019,6 +1024,79 @@ public function dispatch()
$this->assertSame($first[FeatureDependency::class], $firstDependency);
$this->assertSame($second[FeatureDependency::class], $secondDependency);
}

public function test_it_dispatches_events_when_purging_features()
{
Event::fake([FeaturesPurged::class]);

Feature::define('foo', fn () => true);
Feature::define('bar', fn () => true);

Feature::purge(['foo', 'bar', 'baz']);

Event::assertDispatchedTimes(FeaturesPurged::class, 1);
Event::assertDispatched(function (FeaturesPurged $event) {
return $event->features === ['foo', 'bar', 'baz'];
});
}

public function test_it_dispatches_events_when_purging_all_features()
{
Event::fake([AllFeaturesPurged::class]);

Feature::define('foo', fn () => true);
Feature::define('bar', fn () => true);

Feature::purge();

Event::assertDispatchedTimes(AllFeaturesPurged::class, 1);
}

public function test_it_dispatches_events_when_updating_a_scoped_feature()
{
Event::fake([FeatureUpdated::class]);

Feature::define('foo', fn () => false);

Feature::for('tim')->activate('foo');

Event::assertDispatchedTimes(FeatureUpdated::class, 1);
Event::assertDispatched(function (FeatureUpdated $event) {
return $event->feature === 'foo'
&& $event->scope === 'tim'
&& $event->value === true;
});
}

public function test_it_dispatches_events_when_updating_a_feature_for_all_scopes()
{
Event::fake([FeatureUpdatedForAllScopes::class]);

Feature::define('foo', fn () => false);

Feature::activateForEveryone('foo', true);

Event::assertDispatchedTimes(FeatureUpdatedForAllScopes::class, 1);
Event::assertDispatched(function (FeatureUpdatedForAllScopes $event) {
return $event->feature === 'foo'
&& $event->value === true;
});
}

public function test_it_dispatches_events_when_deleting_a_feature_value()
{
Event::fake([FeatureDeleted::class]);

Feature::define('foo', fn () => false);

Feature::for('tim')->forget('foo');

Event::assertDispatchedTimes(FeatureDeleted::class, 1);
Event::assertDispatched(function (FeatureDeleted $event) {
return $event->feature === 'foo'
&& $event->scope === 'tim';
});
}
}

class MyFeature
Expand Down
Loading

0 comments on commit 50e3a73

Please sign in to comment.