mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-08-11 22:00:53 +02:00
v2.0.5: Add typed service lookup entrypoint and tests for Arcane.service
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
## 2.0.5
|
||||
|
||||
### Arcane Framework
|
||||
|
||||
- [NEW] Added `Arcane.service` typed lookup entrypoint for provider-aware
|
||||
service access:
|
||||
- `Arcane.service.ofType<T>(context)`
|
||||
- `Arcane.service.requiredOfType<T>(context)`
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Logging Service
|
||||
|
||||
@@ -169,6 +169,8 @@ services:
|
||||
`ArcaneService` instances. **Note**: This widget is already part of the
|
||||
_`ArcaneApp`_ widget, however if you are not using the `ArcaneApp` widget you
|
||||
can instead use this widget directly.
|
||||
- `Arcane.service`: Typed service lookup entrypoint with
|
||||
`ofType<T>(context)` and `requiredOfType<T>(context)`.
|
||||
- The `service<T>` and `requiredService<T>` extensions on `BuildContext`:
|
||||
nullable and non-nullable getters used to locate a given `ArcaneService` via
|
||||
`BuildContext`. **Note**: For app-defined services, these lookups require an
|
||||
@@ -255,11 +257,13 @@ whatever method you prefer:
|
||||
```dart
|
||||
// If a service of the given type is not registered, `null` is returned.
|
||||
final FavoriteColorService? nullableService = ArcaneService.ofType<FavoriteColorService>(context);
|
||||
final FavoriteColorService? nullableViaArcane = Arcane.service.ofType<FavoriteColorService>(context);
|
||||
final FavoriteColorService? nullableViaContext = context.service<FavoriteColorService>();
|
||||
final FavoriteColorService? nullableViaProvider = ArcaneServiceProvider.serviceOfType<FavoriteColorService>(context);
|
||||
|
||||
// If a service of the given type is not registered, an exception is thrown.
|
||||
final FavoriteColorService nonNullableService = ArcaneService.requiredOfType<FavoriteColorService>(context);
|
||||
final FavoriteColorService nonNullableViaArcane = Arcane.service.requiredOfType<FavoriteColorService>(context);
|
||||
final FavoriteColorService nonNullableViaContext = context.requiredService<FavoriteColorService>();
|
||||
final FavoriteColorService nonNullableViaProvider = ArcaneServiceProvider.requiredServiceOfType<FavoriteColorService>(context);
|
||||
```
|
||||
|
||||
@@ -24,7 +24,9 @@ Future<void> main() async {
|
||||
final DebugPrint debugPrintInterface = DebugPrint();
|
||||
final DebugAuthInterface debugAuthInterface = DebugAuthInterface();
|
||||
LogEvent? skipDebugPrintWhenDisabled(
|
||||
LogEvent event, LogInterceptorContext context) {
|
||||
LogEvent event,
|
||||
LogInterceptorContext context,
|
||||
) {
|
||||
if (context.interface is DebugPrint && Feature.logging.disabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ abstract class Arcane {
|
||||
/// Logger is not a service and is always the singleton.
|
||||
static ArcaneLogger get logger => ArcaneLogger.I;
|
||||
|
||||
/// Provides typed service lookups through `Arcane.service.ofType<T>(context)`.
|
||||
static const ArcaneServiceLookup service = ArcaneServiceLookup();
|
||||
|
||||
/// Provides access to the feature flags service instance registered in ArcaneApp, or the singleton if not present.
|
||||
static ArcaneFeatureFlagService get features =>
|
||||
services.whereType<ArcaneFeatureFlagService>().firstOrNull ??
|
||||
|
||||
@@ -144,3 +144,20 @@ class ArcaneServiceProvider
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed service lookup entrypoint for `Arcane.service`.
|
||||
class ArcaneServiceLookup {
|
||||
const ArcaneServiceLookup();
|
||||
|
||||
/// Returns a service of type `T` from the closest provider in [context].
|
||||
///
|
||||
/// Falls back to Arcane's built-in services when no provider instance exists.
|
||||
T? ofType<T extends ArcaneService>(BuildContext context) =>
|
||||
ArcaneService.ofType<T>(context);
|
||||
|
||||
/// Returns a service of type `T` from the closest provider in [context].
|
||||
///
|
||||
/// Throws an assertion error if no matching service is available.
|
||||
T requiredOfType<T extends ArcaneService>(BuildContext context) =>
|
||||
ArcaneService.requiredOfType<T>(context);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: arcane_framework
|
||||
description: "Agnostic Reusable Component Architecture for New Ecosystems: a
|
||||
modern framework for bootstrapping new applications"
|
||||
version: 2.0.4
|
||||
version: 2.0.5
|
||||
repository: https://github.com/hanskokx/arcane_framework
|
||||
issue_tracker: https://github.com/hanskokx/arcane_framework/issues
|
||||
|
||||
|
||||
@@ -325,6 +325,52 @@ void main() {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("Arcane.service.ofType<T> helper works", (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ArcaneApp(
|
||||
services: testServices,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final service = Arcane.service.ofType<MockArcaneService>(context);
|
||||
expect(service, isNotNull);
|
||||
expect(service, isA<MockArcaneService>());
|
||||
|
||||
final missing = Arcane.service.ofType<UnregisteredService>(
|
||||
context,
|
||||
);
|
||||
expect(missing, isNull);
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("Arcane.service.requiredOfType<T> helper works",
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
ArcaneApp(
|
||||
services: testServices,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final service = Arcane.service.requiredOfType<MockArcaneService>(
|
||||
context,
|
||||
);
|
||||
expect(service, isA<MockArcaneService>());
|
||||
|
||||
expect(
|
||||
() =>
|
||||
Arcane.service.requiredOfType<UnregisteredService>(context),
|
||||
throwsA(isA<AssertionError>()),
|
||||
);
|
||||
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user