Skip to content

Commit

Permalink
feat(kernel): add last resolver (#1976)
Browse files Browse the repository at this point in the history
[skip ci]
  • Loading branch information
Vheissu committed May 18, 2024
1 parent 253e92a commit c47817c
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,63 @@ export class MyClass {
}
```

### `last` Resolver

The last resolver is used to inject the last instance registered under a particular key. This can be useful when you need the most recently registered instance among multiple registrations of the same key.

#### Using `@inject` Decorator

```typescript
import { last, inject } from 'aurelia';

@inject(last(MyService))
export class MyClass {
constructor(private service: MyService) {
// service is the last registered instance of MyService
}
}
```

#### Using Static `inject` Property

```typescript
import { last } from 'aurelia';

export class MyClass {
static inject = [last(MyService)];
constructor(private service: MyService) {
// service is the last registered instance of MyService
}
}
```

#### Example

If you have multiple instances of a service registered under the same key, last will ensure that you get the most recently registered instance:

```typescript
import { DI, IContainer, last, Registration } from 'aurelia';

const container = DI.createContainer();
container.register(Registration.instance(MyService, new MyService('instance1')));
container.register(Registration.instance(MyService, new MyService('instance2')));
container.register(Registration.instance(MyService, new MyService('instance3')));

const myClass = container.get(last(MyService));
console.log(myClass.service); // Outputs: instance3
```

In this example, `myClass.service` will be the instance of MyService registered last (i.e., `instance3`).

If no instances are registered under the specified key, the last resolver will return undefined:

```typescript
const container = DI.createContainer();

const myClass = container.get(last(MyClass));
console.log(myClass.service); // Outputs: undefined
```

## Custom Resolvers

You can create custom resolvers by implementing the `IResolver` interface. Custom resolvers give you the flexibility to implement complex resolution logic that may not be covered by the built-in resolvers.
Expand Down
49 changes: 49 additions & 0 deletions packages/__tests__/src/1-kernel/di.last.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { DI, IContainer, last, Registration } from '@aurelia/kernel';
import { assert } from '@aurelia/testing';

describe('1-kernel/di.last.spec.ts', function () {
let container: IContainer;

beforeEach(function () {
container = DI.createContainer();
});

it('should return the last registered instance of a key', function () {
container.register(Registration.instance('key', 'value1'));
container.register(Registration.instance('key', 'value2'));
container.register(Registration.instance('key', 'value3'));
const result = container.get(last('key'));
assert.equal(result, 'value3');
});

it('should return the last registered instance in a child container', function () {
const child = container.createChild();
child.register(Registration.instance('key', 'childValue1'));
child.register(Registration.instance('key', 'childValue2'));
const result = child.get(last('key'));
assert.equal(result, 'childValue2');
});

it('should return the last registered instance in both parent and child containers', function () {
container.register(Registration.instance('key', 'parentValue1'));
const child = container.createChild();
child.register(Registration.instance('key', 'childValue1'));
const result = child.get(last('key'));
assert.equal(result, 'childValue1');
});

it('should handle different types of registrations', function () {
const instance1 = { name: 'instance1' };
const instance2 = { name: 'instance2' };
container.register(Registration.instance('key', instance1));
container.register(Registration.singleton('key', class { name = 'instance3'; }));
container.register(Registration.instance('key', instance2));
const result = container.get(last('key'));
assert.deepEqual(result, instance2);
});

it('should return undefined if the key is not registered', function () {
const result = container.get(last('key'));
assert.strictEqual(result, undefined);
});
});
13 changes: 13 additions & 0 deletions packages/kernel/src/di.resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ export type IAllResolver<T> = IResolver<readonly Resolved<T>[]> &
// any for decorator
((decorated: unknown, context: DecoratorContext) => any);

/**
* Create a resolver that will resolve the last instance of a key from the resolving container
*
* - @param key [[`Key`]]
*/
export const last = <T extends Key>(key: T): IResolver<T | undefined> => ({
$isResolver: true,
resolve: handler => {
const allInstances = handler.getAll(key);
return allInstances.length > 0 ? allInstances[allInstances.length - 1] : undefined;
}
});

/**
* Lazily inject a dependency depending on whether the [[`Key`]] is present at the time of function call.
*
Expand Down
1 change: 1 addition & 0 deletions packages/kernel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export {
type IOptionalResolver,
type IResolvedFactory,
type INewInstanceResolver,
last,
lazy,
type ILazyResolver,
type IResolvedLazy,
Expand Down

0 comments on commit c47817c

Please sign in to comment.