From 5be908f0e274abae0c3218697efbb9dfb93a5f8d Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 9 May 2019 07:10:29 -0700 Subject: [PATCH] chore(context): rename binding sorter to comparator --- docs/site/Context.md | 2 +- docs/site/Decorators_inject.md | 4 +-- .../inject-multiple-values.acceptance.ts | 18 +++++------ .../src/__tests__/unit/binding-sorter.unit.ts | 2 +- .../src/__tests__/unit/context-view.unit.ts | 4 +-- packages/context/src/binding-sorter.ts | 32 +++++++++++++------ packages/context/src/context-view.ts | 14 ++++---- packages/context/src/context.ts | 4 +-- packages/context/src/inject.ts | 12 +++---- packages/core/src/extension-point.ts | 2 +- 10 files changed, 53 insertions(+), 41 deletions(-) diff --git a/docs/site/Context.md b/docs/site/Context.md index a8bfe4621d06..94f9d88b51af 100644 --- a/docs/site/Context.md +++ b/docs/site/Context.md @@ -435,7 +435,7 @@ should be able to pick up these new routes without restarting. To support the dynamic tracking of such artifacts registered within a context chain, we introduce `ContextObserver` interface and `ContextView` class that can be used to watch a list of bindings matching certain criteria depicted by a -`BindingFilter` function and an optional `BindingSorter` function to sort +`BindingFilter` function and an optional `BindingComparator` function to sort matched bindings. ```ts diff --git a/docs/site/Decorators_inject.md b/docs/site/Decorators_inject.md index bbe7866c27b2..e918c69c58da 100644 --- a/docs/site/Decorators_inject.md +++ b/docs/site/Decorators_inject.md @@ -84,13 +84,13 @@ class MyControllerWithValues { ``` To sort matched bindings found by the binding filter function, `@inject` honors -`bindingSorter` in `metadata`: +`bindingComparator` in `metadata`: ```ts class MyControllerWithValues { constructor( @inject(binding => binding.tagNames.includes('foo'), { - bindingSorter: (a, b) => { + bindingComparator: (a, b) => { // Sort by value of `foo` tag return a.tagMap.foo.localCompare(b.tagMap.foo); }, diff --git a/packages/context/src/__tests__/acceptance/inject-multiple-values.acceptance.ts b/packages/context/src/__tests__/acceptance/inject-multiple-values.acceptance.ts index 795433c09052..05f5d5e0503d 100644 --- a/packages/context/src/__tests__/acceptance/inject-multiple-values.acceptance.ts +++ b/packages/context/src/__tests__/acceptance/inject-multiple-values.acceptance.ts @@ -7,10 +7,10 @@ import {expect} from '@loopback/testlab'; import { Context, ContextView, - createSorterByGroup, filterByTag, Getter, inject, + compareBindingsByGroup, } from '../..'; let app: Context; @@ -36,10 +36,10 @@ describe('@inject.* to receive multiple values matching a filter', async () => { expect(await getter()).to.eql([3, 7, 5]); }); - it('injects as getter with bindingSorter', async () => { + it('injects as getter with bindingComparator', async () => { class MyControllerWithGetter { @inject.getter(workloadMonitorFilter, { - bindingSorter: createSorterByGroup('name'), + bindingComparator: compareBindingsByGroup('name'), }) getter: Getter; } @@ -76,11 +76,11 @@ describe('@inject.* to receive multiple values matching a filter', async () => { expect(inst.values).to.eql([3, 5]); }); - it('injects as values with bindingSorter', async () => { + it('injects as values with bindingComparator', async () => { class MyControllerWithBindingSorter { constructor( @inject(workloadMonitorFilter, { - bindingSorter: createSorterByGroup('name'), + bindingComparator: compareBindingsByGroup('name'), }) public values: number[], ) {} @@ -93,13 +93,13 @@ describe('@inject.* to receive multiple values matching a filter', async () => { expect(inst.values).to.eql([5, 3]); }); - it('throws error if bindingSorter is provided without a filter', () => { + it('throws error if bindingComparator is provided without a filter', () => { expect(() => { // tslint:disable-next-line:no-unused class ControllerWithInvalidInject { constructor( @inject('my-key', { - bindingSorter: createSorterByGroup('name'), + bindingComparator: compareBindingsByGroup('name'), }) public values: number[], ) {} @@ -126,10 +126,10 @@ describe('@inject.* to receive multiple values matching a filter', async () => { expect(await view.values()).to.eql([3, 5]); }); - it('injects as a view with bindingSorter', async () => { + it('injects as a view with bindingComparator', async () => { class MyControllerWithView { @inject.view(workloadMonitorFilter, { - bindingSorter: createSorterByGroup('name'), + bindingComparator: compareBindingsByGroup('name'), }) view: ContextView; } diff --git a/packages/context/src/__tests__/unit/binding-sorter.unit.ts b/packages/context/src/__tests__/unit/binding-sorter.unit.ts index be888f3c60c3..c2cfeab77a73 100644 --- a/packages/context/src/__tests__/unit/binding-sorter.unit.ts +++ b/packages/context/src/__tests__/unit/binding-sorter.unit.ts @@ -6,7 +6,7 @@ import {expect} from '@loopback/testlab'; import {Binding, sortBindingsByGroup} from '../..'; -describe('BindingSorter', () => { +describe('BindingComparator', () => { const orderedGroups = ['log', 'auth']; const groupTagName = 'group'; let bindings: Binding[]; diff --git a/packages/context/src/__tests__/unit/context-view.unit.ts b/packages/context/src/__tests__/unit/context-view.unit.ts index a2a41579438c..6bf4da7595f4 100644 --- a/packages/context/src/__tests__/unit/context-view.unit.ts +++ b/packages/context/src/__tests__/unit/context-view.unit.ts @@ -9,7 +9,7 @@ import { BindingScope, Context, ContextView, - createSorterByGroup, + compareBindingsByGroup, createViewGetter, filterByTag, } from '../..'; @@ -31,7 +31,7 @@ describe('ContextView', () => { const view = new ContextView( server, filterByTag('foo'), - createSorterByGroup('foo', ['b', 'a']), + compareBindingsByGroup('foo', ['b', 'a']), ); expect(view.bindings).to.eql([bindings[1], bindings[0]]); }); diff --git a/packages/context/src/binding-sorter.ts b/packages/context/src/binding-sorter.ts index 8b82f66da683..5f978f40adc8 100644 --- a/packages/context/src/binding-sorter.ts +++ b/packages/context/src/binding-sorter.ts @@ -6,16 +6,28 @@ import {Binding} from './binding'; /** - * Compare function to sort bindings + * Compare function to sort an array of bindings. + * It is used by `Array.prototype.sort()`. */ -export type BindingSorter = ( - bindingA: Readonly>, - bindingB: Readonly>, -) => number; +export interface BindingComparator { + /** + * Compare two bindings + * @param bindingA First binding + * @param bindingB Second binding + * @returns A number to determine order of bindingA and bindingB + * - 0 leaves bindingA and bindingB unchanged + * - <0 bindingA comes before bindingB + * - >0 bindingA comes after bindingB + */ + ( + bindingA: Readonly>, + bindingB: Readonly>, + ): number; +} /** - * Creates a binding sorter to sort bindings by tagged group name. Two bindings - * are compared as follows: + * Creates a binding compare function to sort bindings by tagged group name. + * 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 @@ -28,10 +40,10 @@ export type BindingSorter = ( * @param groupTagName Name of the tag for group * @param orderedGroups An array of group names as predefined orders */ -export function createSorterByGroup( +export function compareBindingsByGroup( groupTagName: string = 'group', orderedGroups: string[] = [], -): BindingSorter { +): BindingComparator { return (a: Readonly>, b: Readonly>) => { const g1: string = a.tagMap[groupTagName] || ''; const g2: string = b.tagMap[groupTagName] || ''; @@ -60,5 +72,5 @@ export function sortBindingsByGroup( groupTagName?: string, orderedGroups?: string[], ) { - return bindings.sort(createSorterByGroup(groupTagName, orderedGroups)); + return bindings.sort(compareBindingsByGroup(groupTagName, orderedGroups)); } diff --git a/packages/context/src/context-view.ts b/packages/context/src/context-view.ts index 8df6222bc61f..93426f8e903d 100644 --- a/packages/context/src/context-view.ts +++ b/packages/context/src/context-view.ts @@ -8,7 +8,7 @@ import {EventEmitter} from 'events'; import {promisify} from 'util'; import {Binding} from './binding'; import {BindingFilter} from './binding-filter'; -import {BindingSorter} from './binding-sorter'; +import {BindingComparator} from './binding-sorter'; import {Context} from './context'; import { ContextEventType, @@ -44,7 +44,7 @@ export class ContextView extends EventEmitter constructor( protected readonly context: Context, public readonly filter: BindingFilter, - public readonly sorter?: BindingSorter, + public readonly sorter?: BindingComparator, ) { super(); } @@ -163,23 +163,23 @@ export class ContextView extends EventEmitter * Create a context view as a getter * @param ctx Context object * @param bindingFilter A function to match bindings - * @param bindingSorter A function to sort matched bindings + * @param bindingComparator A function to sort matched bindings * @param session Resolution session */ export function createViewGetter( ctx: Context, bindingFilter: BindingFilter, - bindingSorterOrSession?: BindingSorter | ResolutionSession, + bindingSorterOrSession?: BindingComparator | ResolutionSession, session?: ResolutionSession, ): Getter { - let bindingSorter: BindingSorter | undefined = undefined; + let bindingComparator: BindingComparator | undefined = undefined; if (typeof bindingSorterOrSession === 'function') { - bindingSorter = bindingSorterOrSession; + bindingComparator = bindingSorterOrSession; } else if (bindingSorterOrSession instanceof ResolutionSession) { session = bindingSorterOrSession; } - const view = new ContextView(ctx, bindingFilter, bindingSorter); + const view = new ContextView(ctx, bindingFilter, bindingComparator); view.open(); return view.asGetter(session); } diff --git a/packages/context/src/context.ts b/packages/context/src/context.ts index 54854871cab7..5d746acd5115 100644 --- a/packages/context/src/context.ts +++ b/packages/context/src/context.ts @@ -9,7 +9,7 @@ import {v1 as uuidv1} from 'uuid'; import {Binding, BindingTag} from './binding'; import {BindingFilter, filterByKey, filterByTag} from './binding-filter'; import {BindingAddress, BindingKey} from './binding-key'; -import {BindingSorter} from './binding-sorter'; +import {BindingComparator} from './binding-sorter'; import { ContextEventObserver, ContextEventType, @@ -444,7 +444,7 @@ export class Context extends EventEmitter { * @param filter A function to match bindings * @param sorter A function to sort matched bindings */ - createView(filter: BindingFilter, sorter?: BindingSorter) { + createView(filter: BindingFilter, sorter?: BindingComparator) { const view = new ContextView(this, filter, sorter); view.open(); return view; diff --git a/packages/context/src/inject.ts b/packages/context/src/inject.ts index 762c556649fa..37bc72e4f8fe 100644 --- a/packages/context/src/inject.ts +++ b/packages/context/src/inject.ts @@ -20,7 +20,7 @@ import { isBindingAddress, } from './binding-filter'; import {BindingAddress} from './binding-key'; -import {BindingSorter} from './binding-sorter'; +import {BindingComparator} from './binding-sorter'; import {BindingCreationPolicy, Context} from './context'; import {ContextView, createViewGetter} from './context-view'; import {ResolutionSession} from './resolution-session'; @@ -60,7 +60,7 @@ export interface InjectionMetadata { /** * Optional sorter for matched bindings */ - bindingSorter?: BindingSorter; + bindingComparator?: BindingComparator; /** * Other attributes */ @@ -118,7 +118,7 @@ export function inject( resolve = resolveValuesByFilter; } const injectionMetadata = Object.assign({decorator: '@inject'}, metadata); - if (injectionMetadata.bindingSorter && !resolve) { + if (injectionMetadata.bindingComparator && !resolve) { throw new Error('Binding sorter is only allowed with a binding filter'); } return function markParameterOrPropertyAsInjected( @@ -544,7 +544,7 @@ function resolveValuesByFilter( const view = new ContextView( ctx, bindingFilter, - injection.metadata.bindingSorter, + injection.metadata.bindingComparator, ); return view.resolve(session); } @@ -567,7 +567,7 @@ function resolveAsGetterByFilter( return createViewGetter( ctx, bindingFilter, - injection.metadata.bindingSorter, + injection.metadata.bindingComparator, session, ); } @@ -585,7 +585,7 @@ function resolveAsContextView(ctx: Context, injection: Readonly) { const view = new ContextView( ctx, bindingFilter, - injection.metadata.bindingSorter, + injection.metadata.bindingComparator, ); view.open(); return view; diff --git a/packages/core/src/extension-point.ts b/packages/core/src/extension-point.ts index 037eb4363c17..0796197bb2c1 100644 --- a/packages/core/src/extension-point.ts +++ b/packages/core/src/extension-point.ts @@ -74,7 +74,7 @@ export function extensions(extensionPointName?: string) { return createViewGetter( ctx, bindingFilter, - injection.metadata.bindingSorter, + injection.metadata.bindingComparator, session, ); });