Skip to content

Commit

Permalink
chore(context): address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed May 9, 2019
1 parent 5be908f commit 89ab031
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/site/Decorators_inject.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class MyControllerWithValues {
@inject(binding => binding.tagNames.includes('foo'), {
bindingComparator: (a, b) => {
// Sort by value of `foo` tag
return a.tagMap.foo.localCompare(b.tagMap.foo);
return a.tagMap.foo.localeCompare(b.tagMap.foo);
},
})
public values: string[],
Expand Down
8 changes: 4 additions & 4 deletions packages/context/src/__tests__/unit/context-view.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {expect} from '@loopback/testlab';
import {
Binding,
BindingScope,
compareBindingsByGroup,
Context,
ContextView,
compareBindingsByGroup,
createViewGetter,
filterByTag,
} from '../..';
Expand All @@ -31,7 +31,7 @@ describe('ContextView', () => {
const view = new ContextView(
server,
filterByTag('foo'),
compareBindingsByGroup('foo', ['b', 'a']),
compareBindingsByGroup('group', ['b', 'a']),
);
expect(view.bindings).to.eql([bindings[1], bindings[0]]);
});
Expand Down Expand Up @@ -195,14 +195,14 @@ describe('ContextView', () => {
server
.bind('bar')
.toDynamicValue(() => Promise.resolve('BAR'))
.tag({foo: 'a'})
.tag('foo', 'bar', {group: 'a'})
.inScope(BindingScope.SINGLETON),
);
bindings.push(
app
.bind('foo')
.to('FOO')
.tag({foo: 'b'}),
.tag('foo', 'bar', {group: 'b'}),
);
}
});
31 changes: 21 additions & 10 deletions packages/context/src/binding-sorter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import {Binding} from './binding';
/**
* Compare function to sort an array of bindings.
* It is used by `Array.prototype.sort()`.
*
* @example
* ```ts
* const compareByKey: BindingComparator = (a, b) => a.key.localeCompare(b.key);
* ```
*/
export interface BindingComparator {
/**
Expand All @@ -27,18 +32,21 @@ export interface BindingComparator {

/**
* Creates a binding compare function to sort bindings by tagged group name.
*
* @remarks
* Two bindings are compared as follows:
*
* 1. Get the `group` value from binding tags, if not present, default to `''`
* 2. If both bindings have `group` values in `orderedGroups`, honor the order
* denoted by `orderedGroups`.
* 1. Get values for the given tag as `group` for bindings, if the tag is not
* present, default `group` to `''`.
* 2. If both bindings have `group` value in `orderedGroups`, honor the order
* specified by `orderedGroups`.
* 3. If a binding's `group` does not exist in `orderedGroups`, it comes before
* the one exists in `orderedGroups`.
* 4. If both bindings have `group` values outside of `orderedGroups`, they are
* the one with `group` exists in `orderedGroups`.
* 4. If both bindings have `group` value outside of `orderedGroups`, they are
* ordered by group names alphabetically.
*
* @param groupTagName Name of the tag for group
* @param orderedGroups An array of group names as predefined orders
* @param groupTagName Name of the binding tag for group
* @param orderedGroups An array of group names as the predefined order
*/
export function compareBindingsByGroup(
groupTagName: string = 'group',
Expand All @@ -62,10 +70,13 @@ export function compareBindingsByGroup(
}

/**
* Sort bindings by tagged group name
* Sort bindings by group names denoted by a tag and the predefined order
*
* @param bindings An array of bindings
* @param groupTagName Tag name for group
* @param orderedGroups An array of group names as predefined orders
* @param groupTagName Tag name for group, for example, we can use the value
* `'a'` of tag `order` as the group name for `binding.tag({order: 'a'})`.
*
* @param orderedGroups An array of group names as the predefined order
*/
export function sortBindingsByGroup(
bindings: Readonly<Binding<unknown>>[],
Expand Down
42 changes: 35 additions & 7 deletions packages/context/src/context-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,23 +160,51 @@ export class ContextView<T = unknown> extends EventEmitter
}

/**
* Create a context view as a getter
* Create a context view as a getter with the given filter
* @param ctx Context object
* @param bindingFilter A function to match bindings
* @param session Resolution session
*/
export function createViewGetter<T = unknown>(
ctx: Context,
bindingFilter: BindingFilter,
session?: ResolutionSession,
): Getter<T[]>;

/**
* Create a context view as a getter with the given filter and sort matched
* bindings by the comparator.
* @param ctx Context object
* @param bindingFilter A function to match bindings
* @param bindingComparator A function to sort matched bindings
* @param bindingComparator A function to compare two bindings
* @param session Resolution session
*/
export function createViewGetter<T = unknown>(
ctx: Context,
bindingFilter: BindingFilter,
bindingSorterOrSession?: BindingComparator | ResolutionSession,
bindingComparator: BindingComparator,
session?: ResolutionSession,
): Getter<T[]>;

/**
* Create a context view as a getter
* @param ctx Context object
* @param bindingFilter A function to match bindings
* @param bindingComparatorOrSession A function to sort matched bindings or
* resolution session if the comparator is not needed
* @param session Resolution session if the comparator is provided
*/
export function createViewGetter<T = unknown>(
ctx: Context,
bindingFilter: BindingFilter,
bindingComparatorOrSession?: BindingComparator | ResolutionSession,
session?: ResolutionSession,
): Getter<T[]> {
let bindingComparator: BindingComparator | undefined = undefined;
if (typeof bindingSorterOrSession === 'function') {
bindingComparator = bindingSorterOrSession;
} else if (bindingSorterOrSession instanceof ResolutionSession) {
session = bindingSorterOrSession;
if (typeof bindingComparatorOrSession === 'function') {
bindingComparator = bindingComparatorOrSession;
} else if (bindingComparatorOrSession instanceof ResolutionSession) {
session = bindingComparatorOrSession;
}

const view = new ContextView<T>(ctx, bindingFilter, bindingComparator);
Expand Down
14 changes: 8 additions & 6 deletions packages/context/src/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,12 +564,14 @@ function resolveAsGetterByFilter(
) {
assertTargetType(injection, Function, 'Getter function');
const bindingFilter = injection.bindingSelector as BindingFilter;
return createViewGetter(
ctx,
bindingFilter,
injection.metadata.bindingComparator,
session,
);
return injection.metadata.bindingComparator
? createViewGetter(
ctx,
bindingFilter,
injection.metadata.bindingComparator,
session,
)
: createViewGetter(ctx, bindingFilter, session);
}

/**
Expand Down

0 comments on commit 89ab031

Please sign in to comment.