Comparing version 0.14.4 to 0.15.0
@@ -14,114 +14,145 @@ import { konsole } from './Konsole'; | ||
export function toFilteredObservable <T> (t: Observableable<T>) { | ||
if (!t) | ||
return Observable.empty<T>(); | ||
if (t instanceof Observable) | ||
return t.filter(i => !!i); | ||
if (t instanceof Promise) | ||
return Observable.fromPromise<T> (t).filter(i => !!i); | ||
return Observable.of(t); | ||
} | ||
export interface Route { | ||
score?: number; | ||
thrown?: true; | ||
export interface ActionRoute { | ||
type: 'action'; | ||
action: () => Observableable<any>; | ||
score: number; | ||
} | ||
export interface Routable { | ||
score?: number; | ||
export interface NoRoute { | ||
type: 'no'; | ||
reason: string; | ||
} | ||
export interface Handler <Z extends Routable = {}> { | ||
(m: Z): Observableable<any>; | ||
} | ||
export type Route = ActionRoute | NoRoute; | ||
export type RouterOrHandler <M extends Routable = {}> = Router<M> | Handler<M>; | ||
export type Routable = object; | ||
export class Router <M extends Routable> { | ||
constructor(public getRoute: (m: M) => Observable<Route>) {} | ||
export type Handler <ROUTABLE> = | ||
(routable: ROUTABLE) => Observableable<any>; | ||
static fromHandler <M extends Routable> (handler: Handler<M>) { | ||
return new Router<M>(m => Observable.of({ | ||
action: () => handler(m) | ||
} as Route)); | ||
export class Router <ROUTABLE> { | ||
constructor(public getRoute: (routable: ROUTABLE) => Observable<Route>) {} | ||
static actionRoute ( | ||
action: () => Observableable<any>, | ||
score: number = 1 | ||
) { | ||
return { | ||
type: 'action', | ||
action, | ||
score | ||
} as ActionRoute; | ||
} | ||
static do <ROUTABLE> ( | ||
handler: Handler<ROUTABLE>, | ||
score?: number | ||
) { | ||
return new Router<ROUTABLE>(routable => Observable.of(Router.actionRoute(() => handler(routable), score))); | ||
} | ||
static null = new Router<any>(m => Observable.empty()); | ||
static noop <ROUTABLE> (handler: Handler<ROUTABLE>) { | ||
return new RunRouter(handler); | ||
} | ||
static noRoute (reason: string = "none") { | ||
return { | ||
type: 'no', | ||
reason | ||
} as NoRoute; | ||
} | ||
static from <M extends Routable> (routerOrHandler: RouterOrHandler<M>): Router<M> { | ||
return routerOrHandler | ||
? routerOrHandler instanceof Router | ||
? routerOrHandler | ||
: Router.fromHandler(routerOrHandler) | ||
: Router.null; | ||
static no (reason?: string) { | ||
return new Router<any>(routable => Observable.of(Router.noRoute(reason))); | ||
} | ||
static routersFrom <M extends Routable> (routersOrHandlers: RouterOrHandler<M>[]) { | ||
return routersOrHandlers | ||
.map(routerOrHandler => Router.from(routerOrHandler)); | ||
} | ||
route (m: M) { | ||
route (routable: ROUTABLE) { | ||
return this | ||
.getRoute(m) | ||
.getRoute(routable) | ||
.do(route => konsole.log("route: returned a route", route)) | ||
.flatMap(route => toObservable(route.action())) | ||
.filter(route => route.type === 'action') | ||
.flatMap((route: ActionRoute) => toObservable(route.action())) | ||
.do(_ => konsole.log("route: called action")); | ||
} | ||
doBefore (handler: Handler<M>) { | ||
return new BeforeRouter(handler, this); | ||
beforeDo (handler: Handler<ROUTABLE>) { | ||
return new BeforeRouter<ROUTABLE>(handler, this); | ||
} | ||
doAfter (handler: Handler<M>) { | ||
return new AfterRouter(handler, this); | ||
afterDo (handler: Handler<ROUTABLE>) { | ||
return new AfterRouter<ROUTABLE>(handler, this); | ||
} | ||
defaultDo (handler: (routable: ROUTABLE, reason: string) => Observableable<any>) { | ||
return this.defaultTry(reason => Router.do(routable => handler(routable, reason))); | ||
} | ||
defaultTry (getRouter: (reason: string) => Router<ROUTABLE>): Router<ROUTABLE>; | ||
defaultTry (router: Router<ROUTABLE>): Router<ROUTABLE>; | ||
defaultTry (arg) { | ||
return new DefaultRouter<ROUTABLE>(this, typeof(arg) === 'function' | ||
? arg | ||
: reason => arg | ||
); | ||
} | ||
} | ||
export class FirstRouter <M extends Routable> extends Router<M> { | ||
constructor (... routersOrHandlers: RouterOrHandler<M>[]) { | ||
const router$ = Observable.from(Router.routersFrom(routersOrHandlers)); | ||
super(m => router$ | ||
export class Helpers <ROUTABLE> { | ||
tryInOrder (... routers: Router<ROUTABLE>[]) { | ||
return new FirstRouter(... routers); | ||
} | ||
tryInScoreOrder (... routers: Router<ROUTABLE>[]) { | ||
return new BestRouter(... routers); | ||
} | ||
ifMatches <VALUE>(matcher: Matcher<ROUTABLE, VALUE>) { | ||
return new IfMatches(matcher); | ||
} | ||
ifTrue (predicate: Predicate<ROUTABLE>): IfTrue<ROUTABLE> { | ||
return new IfTrue(predicate); | ||
} | ||
} | ||
export class FirstRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
constructor (... routers: Router<ROUTABLE>[]) { | ||
super(routable => Observable.from(routers) | ||
.filter(router => !!router) | ||
.concatMap((router, i) => { | ||
konsole.log(`first: trying router #${i}`); | ||
return router | ||
.getRoute(m) | ||
.do(n => konsole.log(`first: router #${i} succeeded`, n)); | ||
.getRoute(routable) | ||
.do(route => konsole.log(`first: router #${i} returned route`, route)); | ||
}) | ||
.take(1) // so that we don't keep going through routers after we find one that matches | ||
); | ||
.filter(route => route.type === 'action') | ||
.take(1) // so that we don't keep going through routers after we find one that matches; | ||
.defaultIfEmpty(Router.noRoute('tryInOrder')) | ||
); | ||
} | ||
} | ||
export function first <M extends Routable> (... routersOrHandlers: RouterOrHandler<M>[]) { | ||
return new FirstRouter(... routersOrHandlers); | ||
} | ||
export function toScore (score: number) { | ||
return score == null ? 1 : score; | ||
} | ||
export class BestRouter <M extends Routable> extends Router<M> { | ||
private static minRoute: Route = { | ||
score: 0, | ||
action: () => { | ||
export class BestRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
private static minRoute = Router.actionRoute( | ||
() => { | ||
console.warn("BestRouter.minRoute.action should never be called"); | ||
} | ||
} | ||
}, | ||
0 | ||
); | ||
constructor(... routersOrHandlers: RouterOrHandler<M>[]) { | ||
const router$ = Observable.from(Router.routersFrom(routersOrHandlers)); | ||
super(m => new Observable<Route>(observer => { | ||
let bestRoute: Route = BestRouter.minRoute; | ||
constructor(... routers: Router<ROUTABLE>[]) { | ||
super(routable => new Observable<Route>(observer => { | ||
let bestRoute = BestRouter.minRoute; | ||
const subscription = router$ | ||
.takeWhile(_ => toScore(bestRoute.score) < 1) | ||
.concatMap(router => router.getRoute(m)) | ||
const subscription = Observable.from(routers) | ||
.filter(router => !!router) | ||
.takeWhile(_ => bestRoute.score < 1) | ||
.concatMap(router => router.getRoute(routable)) | ||
.filter(route => route.type === 'action') | ||
.defaultIfEmpty(Router.noRoute('tryInScoreOrder')) | ||
.subscribe( | ||
route => { | ||
if (toScore(route.score) > toScore(bestRoute.score)) { | ||
(route: ActionRoute) => { | ||
if (route.score > bestRoute.score) { | ||
bestRoute = route; | ||
if (toScore(bestRoute.score) === 1) { | ||
if (bestRoute.score === 1) { | ||
observer.next(bestRoute); | ||
@@ -135,3 +166,3 @@ observer.complete(); | ||
() => { | ||
if (toScore(bestRoute.score) > 0) | ||
if (bestRoute.score > 0) | ||
observer.next(bestRoute); | ||
@@ -147,10 +178,6 @@ observer.complete(); | ||
export function best <M extends Routable> (... routersOrHandlers: RouterOrHandler<M>[]) { | ||
return new BestRouter(... routersOrHandlers); | ||
} | ||
export class RunRouter <M extends Routable> extends Router<M> { | ||
constructor(handler: Handler<M>) { | ||
super(m => toObservable(handler(m)) | ||
.filter(_ => false) | ||
export class RunRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(handler: Handler<ROUTABLE>) { | ||
super(routable => toObservable(handler(routable)) | ||
.map(_ => Router.noRoute('noop')) | ||
); | ||
@@ -160,23 +187,48 @@ } | ||
export function run <M extends Routable> (handler: Handler<M>) { | ||
return new RunRouter(handler); | ||
export interface MatchSuccess <VALUE> { | ||
value: VALUE; | ||
score?: number; | ||
} | ||
export interface Predicate <M extends Routable = {}> { | ||
(m: M): Observableable<boolean>; | ||
export interface MatchFailure <VALUE> { | ||
value?: VALUE; | ||
reason: string; | ||
} | ||
export class IfTrueRouter <M extends Routable> extends Router<M> { | ||
constructor ( | ||
predicate: Predicate<M>, | ||
thenRouterOrHandler: RouterOrHandler<M>, | ||
elseRouterOrHandler?: RouterOrHandler<M>, | ||
export type Match <VALUE> = MatchSuccess<VALUE> | MatchFailure<VALUE>; | ||
export type Matcher <ROUTABLE, VALUE> = (routable: ROUTABLE) => Observableable<Match<VALUE> | VALUE>; | ||
function combineScore(score, otherScore) { | ||
return score * otherScore | ||
} | ||
export function routeWithCombinedScore(route: ActionRoute, newScore: number) { | ||
const score = combineScore(newScore, route.score); | ||
return route.score === score | ||
? route | ||
: { | ||
... route, | ||
score | ||
} as Route; | ||
} | ||
export class IfMatchesElse <ROUTABLE, VALUE> extends Router<ROUTABLE> { | ||
constructor( | ||
private matcher: Matcher<ROUTABLE, VALUE>, | ||
private getThenRouter: (value: VALUE) => Router<ROUTABLE>, | ||
private getElseRouter: (reason: string) => Router<ROUTABLE> | ||
) { | ||
const thenRouter = Router.from(thenRouterOrHandler); | ||
const elseRouter = Router.from(elseRouterOrHandler); | ||
super(m => toObservable(predicate(m)) | ||
.flatMap(n => n | ||
? thenRouter.getRoute(m) | ||
: elseRouter.getRoute(m) | ||
super(routable => toObservable(matcher(routable)) | ||
.map(response => IfMatches.normalizeMatcherResponse<VALUE>(response)) | ||
.flatMap(match => IfMatches.matchIsSuccess(match) | ||
? getThenRouter(match.value) | ||
.getRoute(routable) | ||
.map(route => route.type === 'action' | ||
? routeWithCombinedScore(route, match.score) | ||
: route | ||
) | ||
: getElseRouter(match.reason) | ||
.getRoute(routable) | ||
) | ||
@@ -187,82 +239,142 @@ ); | ||
export function ifTrue <M extends Routable> ( | ||
predicate: Predicate<M>, | ||
thenRouterOrHandler: RouterOrHandler<M>, | ||
elseRouterOrHandler?: RouterOrHandler<M> | ||
): IfTrueRouter<M> { | ||
return new IfTrueRouter(predicate, thenRouterOrHandler, elseRouterOrHandler); | ||
} | ||
export class IfMatchesThen <ROUTABLE, VALUE = any> extends Router<ROUTABLE> { | ||
constructor( | ||
private matcher: Matcher<ROUTABLE, VALUE>, | ||
private getThenRouter: (value: VALUE) => Router<ROUTABLE> | ||
) { | ||
super(routable => toObservable(matcher(routable)) | ||
.map(response => IfMatches.normalizeMatcherResponse<VALUE>(response)) | ||
.flatMap(match => IfMatches.matchIsSuccess(match) | ||
? getThenRouter(match.value) | ||
.getRoute(routable) | ||
.map(route => route.type === 'action' | ||
? routeWithCombinedScore(route, match.score) | ||
: route | ||
) | ||
: Observable.of(Router.noRoute(match.reason)) | ||
) | ||
); | ||
} | ||
export interface Matcher <M extends Routable = {}, Z extends Routable = {}> { | ||
(m: M): Observableable<Z>; | ||
elseDo(elseHandler: (routable: ROUTABLE, reason: string) => Observableable<any>) { | ||
return this.elseTry(reason => Router.do(routable => elseHandler(routable, reason))); | ||
} | ||
elseTry(elseRouter: Router<ROUTABLE>): IfMatchesElse<ROUTABLE, VALUE>; | ||
elseTry(getElseRouter: (reason: string) => Router<ROUTABLE>): IfMatchesElse<ROUTABLE, VALUE>; | ||
elseTry(arg) { | ||
return new IfMatchesElse(this.matcher, this.getThenRouter, typeof(arg) === 'function' | ||
? arg | ||
: reason => arg | ||
); | ||
} | ||
} | ||
export class IfMatchesRouter <M extends Routable, N extends Routable> extends Router<M> { | ||
private static routeWithCombinedScore(route: Route, newScore: number) { | ||
const score = toScore(newScore) * toScore(route.score); | ||
return toScore(route.score) === score | ||
? route | ||
: { | ||
... route, | ||
score | ||
} as Route; | ||
} | ||
export class IfMatches <ROUTABLE, VALUE> { | ||
constructor ( | ||
matcher: Matcher<M, N>, | ||
thenRouterOrHandler: RouterOrHandler<N>, | ||
elseRouterOrHandler?: RouterOrHandler<M> | ||
private matcher: Matcher<ROUTABLE, VALUE> | ||
) { | ||
const thenRouter = Router.from(thenRouterOrHandler); | ||
const elseRouter = Router.from(elseRouterOrHandler); | ||
} | ||
super(m => toObservable(matcher(m)) | ||
.flatMap(n => n | ||
? thenRouter | ||
.getRoute(n) | ||
.map(route => IfMatchesRouter.routeWithCombinedScore(route, n.score)) | ||
: elseRouter | ||
.getRoute(m) | ||
static matchIsSuccess <VALUE> (match: Match<any>): match is MatchSuccess<VALUE> { | ||
return ((match as any).reason === undefined); | ||
} | ||
and (predicate: (value: VALUE) => IfTrue<ROUTABLE>): IfMatches<ROUTABLE, VALUE>; | ||
and <TRANSFORMRESULT> (recognizer: (value: VALUE) => IfMatches<ROUTABLE, TRANSFORMRESULT>): IfMatches<ROUTABLE, TRANSFORMRESULT>; | ||
and <TRANSFORMRESULT> (recognizer: (value: VALUE) => IfMatches<ROUTABLE, TRANSFORMRESULT>) { | ||
return new IfMatches((routable: ROUTABLE) => toObservable(this.matcher(routable)) | ||
.map(response => IfMatches.normalizeMatcherResponse<VALUE>(response)) | ||
.flatMap(match => IfMatches.matchIsSuccess(match) | ||
? toObservable(recognizer(match.value)) | ||
.flatMap(_ifMatches => toObservable(_ifMatches.matcher(routable)) | ||
.map(_response => IfMatches.normalizeMatcherResponse(_response)) | ||
.map(_match => IfMatches.matchIsSuccess(_match) | ||
? _ifMatches instanceof IfTrue | ||
? match | ||
: { | ||
value: _match.value, | ||
score: combineScore(match.score, _match.score) | ||
} | ||
: _match | ||
) | ||
) | ||
: Observable.of(match) | ||
) | ||
); | ||
} | ||
} | ||
export function ifMatches <M extends Routable, N extends Routable> ( | ||
matcher: Matcher<M, N>, | ||
thenRouterOrHandler: RouterOrHandler<N>, | ||
elseRouterOrHandler?: RouterOrHandler<M> | ||
): IfMatchesRouter<M, N> { | ||
return new IfMatchesRouter(matcher, thenRouterOrHandler, elseRouterOrHandler); | ||
} | ||
thenDo(thenHandler: (routable: ROUTABLE, value: VALUE) => Observableable<any>) { | ||
return this.thenTry(value => Router.do(routable => thenHandler(routable, value))); | ||
} | ||
const thrownRoute: Route = { | ||
thrown: true, | ||
action: () => {} | ||
}; | ||
thenTry(router: Router<ROUTABLE>): IfMatchesThen<ROUTABLE, VALUE>; | ||
thenTry(getRouter: (value: VALUE) => Router<ROUTABLE>): IfMatchesThen<ROUTABLE, VALUE>; | ||
thenTry(arg) { | ||
return new IfMatchesThen(this.matcher, typeof arg === 'function' | ||
? arg | ||
: value => arg | ||
); | ||
} | ||
export function throwRoute <M extends Routable> () { | ||
return new Router<M>(m => Observable.of(thrownRoute)); | ||
} | ||
static defaultReason = "none"; | ||
export function catchRoute <M extends Routable> (routerOrHandler: RouterOrHandler<M>): Router<M> { | ||
return new Router<M>(m => Router | ||
.from(routerOrHandler) | ||
.getRoute(m) | ||
.filter(route => !route.thrown) | ||
); | ||
static normalizeMatcherResponse <VALUE> (response: any): Match<VALUE> { | ||
if (!response) | ||
return { | ||
reason: IfMatches.defaultReason | ||
} | ||
if (typeof(response) === 'object') { | ||
if (response.reason) { | ||
if (typeof(response.reason) !== 'string') | ||
throw new Error('The reason for NoMatch must be a string'); | ||
return { | ||
reason: response.reason | ||
} | ||
} | ||
if (response.value !== undefined) { | ||
if (response.score !== undefined && typeof(response.score) !== 'number') | ||
throw new Error('The score for Match must be a number'); | ||
return { | ||
value: response.value, | ||
score: response.score | ||
} | ||
} | ||
} | ||
return { | ||
value: response, | ||
score: 1 | ||
} | ||
} | ||
} | ||
export class BeforeRouter <M extends Routable> extends Router<M> { | ||
constructor (beforeHandler: Handler<M>, routerOrHandler: RouterOrHandler<M>) { | ||
const router = Router.from(routerOrHandler); | ||
export type Predicate <ROUTABLE> = Matcher<ROUTABLE, boolean>; | ||
super(m => router | ||
.getRoute(m) | ||
.map(route => ({ | ||
... route, | ||
action: () => toObservable(beforeHandler(m)) | ||
.flatMap(_ => toObservable(route.action())) | ||
})) | ||
export class IfTrue <ROUTABLE> extends IfMatches<ROUTABLE, boolean> { | ||
constructor( | ||
predicate: Predicate<ROUTABLE> | ||
) { | ||
super(routable => toObservable(predicate(routable)) | ||
.map((response: any) => { | ||
if (response === true || response === false) | ||
return response; | ||
if (typeof(response) === 'object') { | ||
if (response.reason) | ||
return response; | ||
if (response.value !== undefined) { | ||
if (response.value === false) | ||
return false; | ||
if (response.value === true) | ||
return response; | ||
throw new Error('When returning a Match from the predicate for IfTrue, the value must be true or false'); | ||
} | ||
} | ||
throw new Error('The predicate for ifTrue may only return true, false, a Match of true or false, or a NoMatch'); | ||
}) | ||
); | ||
@@ -272,15 +384,46 @@ } | ||
export class AfterRouter <M extends Routable> extends Router<M> { | ||
constructor (afterHandler: Handler<M>, routerOrHandler: RouterOrHandler<M>) { | ||
const router = Router.from(routerOrHandler); | ||
export class BeforeRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
constructor (beforeHandler: Handler<ROUTABLE>, router: Router<ROUTABLE>) { | ||
super(routable => router | ||
.getRoute(routable) | ||
.map(route => route.type === 'action' | ||
? { | ||
... route, | ||
action: () => toObservable(beforeHandler(routable)) | ||
.flatMap(_ => toObservable(route.action())) | ||
} | ||
: route | ||
) | ||
); | ||
} | ||
} | ||
super(m => router | ||
.getRoute(m) | ||
.map(route => ({ | ||
... route, | ||
action: () => toObservable(route.action()) | ||
.flatMap(_ => toObservable(afterHandler(m))) | ||
})) | ||
export class AfterRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
constructor (afterHandler: Handler<ROUTABLE>, router: Router<ROUTABLE>) { | ||
super(routable => router | ||
.getRoute(routable) | ||
.map(route => route.type === 'action' | ||
? { | ||
... route, | ||
action: () => toObservable(route.action()) | ||
.flatMap(_ => toObservable(afterHandler(routable))) | ||
} | ||
: route | ||
) | ||
); | ||
} | ||
} | ||
export class DefaultRouter <ROUTABLE> extends Router<ROUTABLE> { | ||
constructor ( | ||
mainRouter: Router<ROUTABLE>, | ||
getDefaultRouter: (reason: string) => Router<ROUTABLE> | ||
) { | ||
super(routable => mainRouter.getRoute(routable) | ||
.flatMap(route => route.type === 'action' | ||
? Observable.of(route) | ||
: getDefaultRouter(route.reason).getRoute(routable) | ||
) | ||
); | ||
} | ||
} |
import { Observable } from 'rxjs'; | ||
export declare type Observableable<T> = T | Observable<T> | Promise<T>; | ||
export declare function toObservable<T>(t: Observableable<T>): Observable<T>; | ||
export declare function toFilteredObservable<T>(t: Observableable<T>): Observable<T>; | ||
export interface BaseRoute { | ||
score?: number; | ||
} | ||
export interface ActionRoute extends BaseRoute { | ||
export interface ActionRoute { | ||
type: 'action'; | ||
action: () => Observableable<any>; | ||
score: number; | ||
} | ||
export interface AbstractRoute extends BaseRoute { | ||
type: 'abstract'; | ||
name: string; | ||
value?: object; | ||
export interface NoRoute { | ||
type: 'no'; | ||
reason: string; | ||
} | ||
export declare type Route = ActionRoute | AbstractRoute; | ||
export interface Routable { | ||
score?: number; | ||
export declare type Route = ActionRoute | NoRoute; | ||
export declare type Routable = object; | ||
export declare type Handler<ROUTABLE> = (routable: ROUTABLE) => Observableable<any>; | ||
export declare class Router<ROUTABLE> { | ||
getRoute: (routable: ROUTABLE) => Observable<Route>; | ||
constructor(getRoute: (routable: ROUTABLE) => Observable<Route>); | ||
static actionRoute(action: () => Observableable<any>, score?: number): ActionRoute; | ||
static do<ROUTABLE>(handler: Handler<ROUTABLE>, score?: number): Router<ROUTABLE>; | ||
static noop<ROUTABLE>(handler: Handler<ROUTABLE>): RunRouter<ROUTABLE>; | ||
static noRoute(reason?: string): NoRoute; | ||
static no(reason?: string): Router<any>; | ||
route(routable: ROUTABLE): Observable<any>; | ||
beforeDo(handler: Handler<ROUTABLE>): BeforeRouter<ROUTABLE>; | ||
afterDo(handler: Handler<ROUTABLE>): AfterRouter<ROUTABLE>; | ||
defaultDo(handler: (routable: ROUTABLE, reason: string) => Observableable<any>): Router<ROUTABLE>; | ||
defaultTry(getRouter: (reason: string) => Router<ROUTABLE>): Router<ROUTABLE>; | ||
defaultTry(router: Router<ROUTABLE>): Router<ROUTABLE>; | ||
} | ||
export interface Handler<Z extends Routable = {}> { | ||
(m: Z): Observableable<any>; | ||
export declare class Helpers<ROUTABLE> { | ||
tryInOrder(...routers: Router<ROUTABLE>[]): FirstRouter<ROUTABLE>; | ||
tryInScoreOrder(...routers: Router<ROUTABLE>[]): BestRouter<ROUTABLE>; | ||
ifMatches<VALUE>(matcher: Matcher<ROUTABLE, VALUE>): IfMatches<ROUTABLE, VALUE>; | ||
ifTrue(predicate: Predicate<ROUTABLE>): IfTrue<ROUTABLE>; | ||
} | ||
export declare type RouterOrHandler<M extends Routable = {}> = Router<M> | Handler<M>; | ||
export interface AbstractRouteNameValue { | ||
name: string; | ||
value?: object; | ||
export declare class FirstRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(...routers: Router<ROUTABLE>[]); | ||
} | ||
export interface GetRoute<M extends Routable> { | ||
(m: M): Observable<Route>; | ||
export declare class BestRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
private static minRoute; | ||
constructor(...routers: Router<ROUTABLE>[]); | ||
} | ||
export declare class Router<M extends Routable> { | ||
getRoute: GetRoute<M>; | ||
constructor(getRoute: GetRoute<M>); | ||
static fromHandler<M extends Routable>(handler: Handler<M>): Router<M>; | ||
static null: Router<any>; | ||
static from<M extends Routable>(routerOrHandler: RouterOrHandler<M>): Router<M>; | ||
static routersFrom<M extends Routable>(routersOrHandlers: RouterOrHandler<M>[]): Router<M>[]; | ||
static abstractRouteWarning: (route: AbstractRoute) => (m: any) => void; | ||
static abstractRoute<M extends Routable>(name: string, value?: object): Router<M>; | ||
static abstractRoute<M extends Routable>(getAbstractRouteNameValue: (m: M) => Observableable<AbstractRouteNameValue>): Router<M>; | ||
catchAbstractRoute(getRouter: (route: AbstractRoute) => RouterOrHandler<M>): Router<M>; | ||
route(m: M): Observable<any>; | ||
doBefore(handler: Handler<M>): any; | ||
doAfter(handler: Handler<M>): any; | ||
export declare class RunRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(handler: Handler<ROUTABLE>); | ||
} | ||
export declare class FirstRouter<M extends Routable> extends Router<M> { | ||
constructor(...routersOrHandlers: RouterOrHandler<M>[]); | ||
export interface MatchSuccess<VALUE> { | ||
value: VALUE; | ||
score?: number; | ||
} | ||
export declare function first<M extends Routable>(...routersOrHandlers: RouterOrHandler<M>[]): FirstRouter<M>; | ||
export declare function toScore(score: number): number; | ||
export declare class BestRouter<M extends Routable> extends Router<M> { | ||
private static minRoute; | ||
constructor(...routersOrHandlers: RouterOrHandler<M>[]); | ||
export interface MatchFailure<VALUE> { | ||
value?: VALUE; | ||
reason: string; | ||
} | ||
export declare function best<M extends Routable>(...routersOrHandlers: RouterOrHandler<M>[]): BestRouter<M>; | ||
export declare class RunRouter<M extends Routable> extends Router<M> { | ||
constructor(handler: Handler<M>); | ||
export declare type Match<VALUE> = MatchSuccess<VALUE> | MatchFailure<VALUE>; | ||
export declare type Matcher<ROUTABLE, VALUE> = (routable: ROUTABLE) => Observableable<Match<VALUE> | VALUE>; | ||
export declare function routeWithCombinedScore(route: ActionRoute, newScore: number): Route; | ||
export declare class IfMatchesElse<ROUTABLE, VALUE> extends Router<ROUTABLE> { | ||
private matcher; | ||
private getThenRouter; | ||
private getElseRouter; | ||
constructor(matcher: Matcher<ROUTABLE, VALUE>, getThenRouter: (value: VALUE) => Router<ROUTABLE>, getElseRouter: (reason: string) => Router<ROUTABLE>); | ||
} | ||
export declare function run<M extends Routable>(handler: Handler<M>): RunRouter<M>; | ||
export interface Predicate<M extends Routable = {}> { | ||
(m: M): Observableable<boolean>; | ||
export declare class IfMatchesThen<ROUTABLE, VALUE = any> extends Router<ROUTABLE> { | ||
private matcher; | ||
private getThenRouter; | ||
constructor(matcher: Matcher<ROUTABLE, VALUE>, getThenRouter: (value: VALUE) => Router<ROUTABLE>); | ||
elseDo(elseHandler: (routable: ROUTABLE, reason: string) => Observableable<any>): IfMatchesElse<ROUTABLE, VALUE>; | ||
elseTry(elseRouter: Router<ROUTABLE>): IfMatchesElse<ROUTABLE, VALUE>; | ||
elseTry(getElseRouter: (reason: string) => Router<ROUTABLE>): IfMatchesElse<ROUTABLE, VALUE>; | ||
} | ||
export declare class IfTrueRouter<M extends Routable> extends Router<M> { | ||
constructor(predicate: Predicate<M>, thenRouterOrHandler: RouterOrHandler<M>, elseRouterOrHandler?: RouterOrHandler<M>); | ||
export declare class IfMatches<ROUTABLE, VALUE> { | ||
private matcher; | ||
constructor(matcher: Matcher<ROUTABLE, VALUE>); | ||
static matchIsSuccess<VALUE>(match: Match<any>): match is MatchSuccess<VALUE>; | ||
and(predicate: (value: VALUE) => IfTrue<ROUTABLE>): IfMatches<ROUTABLE, VALUE>; | ||
and<TRANSFORMRESULT>(recognizer: (value: VALUE) => IfMatches<ROUTABLE, TRANSFORMRESULT>): IfMatches<ROUTABLE, TRANSFORMRESULT>; | ||
thenDo(thenHandler: (routable: ROUTABLE, value: VALUE) => Observableable<any>): IfMatchesThen<ROUTABLE, VALUE>; | ||
thenTry(router: Router<ROUTABLE>): IfMatchesThen<ROUTABLE, VALUE>; | ||
thenTry(getRouter: (value: VALUE) => Router<ROUTABLE>): IfMatchesThen<ROUTABLE, VALUE>; | ||
static defaultReason: string; | ||
static normalizeMatcherResponse<VALUE>(response: any): Match<VALUE>; | ||
} | ||
export declare function ifTrue<M extends Routable>(predicate: Predicate<M>, thenRouterOrHandler: RouterOrHandler<M>, elseRouterOrHandler?: RouterOrHandler<M>): IfTrueRouter<M>; | ||
export interface Matcher<M extends Routable = {}, Z extends Routable = {}> { | ||
(m: M): Observableable<Z>; | ||
export declare type Predicate<ROUTABLE> = Matcher<ROUTABLE, boolean>; | ||
export declare class IfTrue<ROUTABLE> extends IfMatches<ROUTABLE, boolean> { | ||
constructor(predicate: Predicate<ROUTABLE>); | ||
} | ||
export declare class IfMatchesRouter<M extends Routable, N extends Routable> extends Router<M> { | ||
private static routeWithCombinedScore(route, newScore); | ||
constructor(matcher: Matcher<M, N>, thenRouterOrHandler: RouterOrHandler<N>, elseRouterOrHandler?: RouterOrHandler<M>); | ||
export declare class BeforeRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(beforeHandler: Handler<ROUTABLE>, router: Router<ROUTABLE>); | ||
} | ||
export declare function ifMatches<M extends Routable, N extends Routable>(matcher: Matcher<M, N>, thenRouterOrHandler: RouterOrHandler<N>, elseRouterOrHandler?: RouterOrHandler<M>): IfMatchesRouter<M, N>; | ||
export declare class BeforeRouter<M extends Routable> extends Router<M> { | ||
constructor(beforeHandler: Handler<M>, routerOrHandler: RouterOrHandler<M>); | ||
export declare class AfterRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(afterHandler: Handler<ROUTABLE>, router: Router<ROUTABLE>); | ||
} | ||
export declare class AfterRouter<M extends Routable> extends Router<M> { | ||
constructor(afterHandler: Handler<M>, routerOrHandler: RouterOrHandler<M>); | ||
export declare class DefaultRouter<ROUTABLE> extends Router<ROUTABLE> { | ||
constructor(mainRouter: Router<ROUTABLE>, getDefaultRouter: (reason: string) => Router<ROUTABLE>); | ||
} |
@@ -31,95 +31,99 @@ "use strict"; | ||
exports.toObservable = toObservable; | ||
function toFilteredObservable(t) { | ||
if (!t) | ||
return rxjs_1.Observable.empty(); | ||
if (t instanceof rxjs_1.Observable) | ||
return t.filter(function (i) { return !!i; }); | ||
if (t instanceof Promise) | ||
return rxjs_1.Observable.fromPromise(t).filter(function (i) { return !!i; }); | ||
return rxjs_1.Observable.of(t); | ||
} | ||
exports.toFilteredObservable = toFilteredObservable; | ||
var Router = (function () { | ||
var Router = /** @class */ (function () { | ||
function Router(getRoute) { | ||
this.getRoute = getRoute; | ||
} | ||
Router.fromHandler = function (handler) { | ||
return new Router(function (m) { return rxjs_1.Observable.of({ | ||
Router.actionRoute = function (action, score) { | ||
if (score === void 0) { score = 1; } | ||
return { | ||
type: 'action', | ||
action: function () { return handler(m); } | ||
}); }); | ||
action: action, | ||
score: score | ||
}; | ||
}; | ||
Router.from = function (routerOrHandler) { | ||
return routerOrHandler | ||
? routerOrHandler instanceof Router | ||
? routerOrHandler | ||
: Router.fromHandler(routerOrHandler) | ||
: Router.null; | ||
Router.do = function (handler, score) { | ||
return new Router(function (routable) { return rxjs_1.Observable.of(Router.actionRoute(function () { return handler(routable); }, score)); }); | ||
}; | ||
Router.routersFrom = function (routersOrHandlers) { | ||
return routersOrHandlers | ||
.map(function (routerOrHandler) { return Router.from(routerOrHandler); }); | ||
Router.noop = function (handler) { | ||
return new RunRouter(handler); | ||
}; | ||
Router.abstractRoute = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
return new Router(typeof args[0] === 'string' | ||
? function (m) { return rxjs_1.Observable.of({ | ||
type: 'abstract', | ||
name: args[0], | ||
value: args[1] | ||
}); } | ||
: function (m) { return toObservable(args[0](m)) | ||
.map(function (nameValue) { return (__assign({ type: 'abstract' }, nameValue)); }); }); | ||
Router.noRoute = function (reason) { | ||
if (reason === void 0) { reason = "none"; } | ||
return { | ||
type: 'no', | ||
reason: reason | ||
}; | ||
}; | ||
Router.prototype.catchAbstractRoute = function (getRouter) { | ||
var _this = this; | ||
return new Router(function (m) { return _this | ||
.getRoute(m) | ||
.flatMap(function (route) { return route.type === "abstract" | ||
? Router.from(getRouter(route)).getRoute(m) | ||
: rxjs_1.Observable.of(route); }); }); | ||
Router.no = function (reason) { | ||
return new Router(function (routable) { return rxjs_1.Observable.of(Router.noRoute(reason)); }); | ||
}; | ||
Router.prototype.route = function (m) { | ||
Router.prototype.route = function (routable) { | ||
return this | ||
.catchAbstractRoute(Router.abstractRouteWarning) | ||
.getRoute(m) | ||
.getRoute(routable) | ||
.do(function (route) { return Konsole_1.konsole.log("route: returned a route", route); }) | ||
.filter(function (route) { return route.type === 'action'; }) | ||
.flatMap(function (route) { return toObservable(route.action()); }) | ||
.do(function (_) { return Konsole_1.konsole.log("route: called action"); }); | ||
}; | ||
Router.prototype.doBefore = function (handler) { | ||
Router.prototype.beforeDo = function (handler) { | ||
return new BeforeRouter(handler, this); | ||
}; | ||
Router.prototype.doAfter = function (handler) { | ||
Router.prototype.afterDo = function (handler) { | ||
return new AfterRouter(handler, this); | ||
}; | ||
Router.null = new Router(function (m) { return rxjs_1.Observable.empty(); }); | ||
Router.abstractRouteWarning = function (route) { return function (m) { | ||
console.warn("An attempt was made to execute an abstract route named " + route.name + " with value " + route.value); | ||
}; }; | ||
Router.prototype.defaultDo = function (handler) { | ||
return this.defaultTry(function (reason) { return Router.do(function (routable) { return handler(routable, reason); }); }); | ||
}; | ||
Router.prototype.defaultTry = function (arg) { | ||
return new DefaultRouter(this, typeof (arg) === 'function' | ||
? arg | ||
: function (reason) { return arg; }); | ||
}; | ||
return Router; | ||
}()); | ||
exports.Router = Router; | ||
var FirstRouter = (function (_super) { | ||
var Helpers = /** @class */ (function () { | ||
function Helpers() { | ||
} | ||
Helpers.prototype.tryInOrder = function () { | ||
var routers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routers[_i] = arguments[_i]; | ||
} | ||
return new (FirstRouter.bind.apply(FirstRouter, [void 0].concat(routers)))(); | ||
}; | ||
Helpers.prototype.tryInScoreOrder = function () { | ||
var routers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routers[_i] = arguments[_i]; | ||
} | ||
return new (BestRouter.bind.apply(BestRouter, [void 0].concat(routers)))(); | ||
}; | ||
Helpers.prototype.ifMatches = function (matcher) { | ||
return new IfMatches(matcher); | ||
}; | ||
Helpers.prototype.ifTrue = function (predicate) { | ||
return new IfTrue(predicate); | ||
}; | ||
return Helpers; | ||
}()); | ||
exports.Helpers = Helpers; | ||
var FirstRouter = /** @class */ (function (_super) { | ||
__extends(FirstRouter, _super); | ||
function FirstRouter() { | ||
var routersOrHandlers = []; | ||
var routers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routersOrHandlers[_i] = arguments[_i]; | ||
routers[_i] = arguments[_i]; | ||
} | ||
var _this = this; | ||
var router$ = rxjs_1.Observable.from(Router.routersFrom(routersOrHandlers)); | ||
_this = _super.call(this, function (m) { return router$ | ||
return _super.call(this, function (routable) { return rxjs_1.Observable.from(routers) | ||
.filter(function (router) { return !!router; }) | ||
.concatMap(function (router, i) { | ||
Konsole_1.konsole.log("first: trying router #" + i); | ||
return router | ||
.getRoute(m) | ||
.do(function (n) { return Konsole_1.konsole.log("first: router #" + i + " succeeded", n); }); | ||
.getRoute(routable) | ||
.do(function (route) { return Konsole_1.konsole.log("first: router #" + i + " returned route", route); }); | ||
}) | ||
.take(1); } // so that we don't keep going through routers after we find one that matches | ||
) || this; | ||
return _this; | ||
.filter(function (route) { return route.type === 'action'; }) | ||
.take(1) // so that we don't keep going through routers after we find one that matches; | ||
.defaultIfEmpty(Router.noRoute('tryInOrder')); }) || this; | ||
} | ||
@@ -129,32 +133,21 @@ return FirstRouter; | ||
exports.FirstRouter = FirstRouter; | ||
function first() { | ||
var routersOrHandlers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routersOrHandlers[_i] = arguments[_i]; | ||
} | ||
return new (FirstRouter.bind.apply(FirstRouter, [void 0].concat(routersOrHandlers)))(); | ||
} | ||
exports.first = first; | ||
function toScore(score) { | ||
return score == null ? 1 : score; | ||
} | ||
exports.toScore = toScore; | ||
var BestRouter = (function (_super) { | ||
var BestRouter = /** @class */ (function (_super) { | ||
__extends(BestRouter, _super); | ||
function BestRouter() { | ||
var routersOrHandlers = []; | ||
var routers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routersOrHandlers[_i] = arguments[_i]; | ||
routers[_i] = arguments[_i]; | ||
} | ||
var _this = this; | ||
var router$ = rxjs_1.Observable.from(Router.routersFrom(routersOrHandlers)); | ||
_this = _super.call(this, function (m) { return new rxjs_1.Observable(function (observer) { | ||
return _super.call(this, function (routable) { return new rxjs_1.Observable(function (observer) { | ||
var bestRoute = BestRouter.minRoute; | ||
var subscription = router$ | ||
.takeWhile(function (_) { return toScore(bestRoute.score) < 1; }) | ||
.concatMap(function (router) { return router.getRoute(m); }) | ||
var subscription = rxjs_1.Observable.from(routers) | ||
.filter(function (router) { return !!router; }) | ||
.takeWhile(function (_) { return bestRoute.score < 1; }) | ||
.concatMap(function (router) { return router.getRoute(routable); }) | ||
.filter(function (route) { return route.type === 'action'; }) | ||
.defaultIfEmpty(Router.noRoute('tryInScoreOrder')) | ||
.subscribe(function (route) { | ||
if (toScore(route.score) > toScore(bestRoute.score)) { | ||
if (route.score > bestRoute.score) { | ||
bestRoute = route; | ||
if (toScore(bestRoute.score) === 1) { | ||
if (bestRoute.score === 1) { | ||
observer.next(bestRoute); | ||
@@ -167,3 +160,3 @@ observer.complete(); | ||
}, function () { | ||
if (toScore(bestRoute.score) > 0) | ||
if (bestRoute.score > 0) | ||
observer.next(bestRoute); | ||
@@ -174,27 +167,14 @@ observer.complete(); | ||
}); }) || this; | ||
return _this; | ||
} | ||
BestRouter.minRoute = { | ||
type: 'action', | ||
score: 0, | ||
action: function () { | ||
console.warn("BestRouter.minRoute.action should never be called"); | ||
} | ||
}; | ||
BestRouter.minRoute = Router.actionRoute(function () { | ||
console.warn("BestRouter.minRoute.action should never be called"); | ||
}, 0); | ||
return BestRouter; | ||
}(Router)); | ||
exports.BestRouter = BestRouter; | ||
function best() { | ||
var routersOrHandlers = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
routersOrHandlers[_i] = arguments[_i]; | ||
} | ||
return new (BestRouter.bind.apply(BestRouter, [void 0].concat(routersOrHandlers)))(); | ||
} | ||
exports.best = best; | ||
var RunRouter = (function (_super) { | ||
var RunRouter = /** @class */ (function (_super) { | ||
__extends(RunRouter, _super); | ||
function RunRouter(handler) { | ||
return _super.call(this, function (m) { return toObservable(handler(m)) | ||
.filter(function (_) { return false; }); }) || this; | ||
return _super.call(this, function (routable) { return toObservable(handler(routable)) | ||
.map(function (_) { return Router.noRoute('noop'); }); }) || this; | ||
} | ||
@@ -204,81 +184,168 @@ return RunRouter; | ||
exports.RunRouter = RunRouter; | ||
function run(handler) { | ||
return new RunRouter(handler); | ||
function combineScore(score, otherScore) { | ||
return score * otherScore; | ||
} | ||
exports.run = run; | ||
var IfTrueRouter = (function (_super) { | ||
__extends(IfTrueRouter, _super); | ||
function IfTrueRouter(predicate, thenRouterOrHandler, elseRouterOrHandler) { | ||
var _this = this; | ||
var thenRouter = Router.from(thenRouterOrHandler); | ||
var elseRouter = Router.from(elseRouterOrHandler); | ||
_this = _super.call(this, function (m) { return toObservable(predicate(m)) | ||
.flatMap(function (n) { return n | ||
? thenRouter.getRoute(m) | ||
: elseRouter.getRoute(m); }); }) || this; | ||
function routeWithCombinedScore(route, newScore) { | ||
var score = combineScore(newScore, route.score); | ||
return route.score === score | ||
? route | ||
: __assign({}, route, { score: score }); | ||
} | ||
exports.routeWithCombinedScore = routeWithCombinedScore; | ||
var IfMatchesElse = /** @class */ (function (_super) { | ||
__extends(IfMatchesElse, _super); | ||
function IfMatchesElse(matcher, getThenRouter, getElseRouter) { | ||
var _this = _super.call(this, function (routable) { return toObservable(matcher(routable)) | ||
.map(function (response) { return IfMatches.normalizeMatcherResponse(response); }) | ||
.flatMap(function (match) { return IfMatches.matchIsSuccess(match) | ||
? getThenRouter(match.value) | ||
.getRoute(routable) | ||
.map(function (route) { return route.type === 'action' | ||
? routeWithCombinedScore(route, match.score) | ||
: route; }) | ||
: getElseRouter(match.reason) | ||
.getRoute(routable); }); }) || this; | ||
_this.matcher = matcher; | ||
_this.getThenRouter = getThenRouter; | ||
_this.getElseRouter = getElseRouter; | ||
return _this; | ||
} | ||
return IfTrueRouter; | ||
return IfMatchesElse; | ||
}(Router)); | ||
exports.IfTrueRouter = IfTrueRouter; | ||
function ifTrue(predicate, thenRouterOrHandler, elseRouterOrHandler) { | ||
return new IfTrueRouter(predicate, thenRouterOrHandler, elseRouterOrHandler); | ||
} | ||
exports.ifTrue = ifTrue; | ||
var IfMatchesRouter = (function (_super) { | ||
__extends(IfMatchesRouter, _super); | ||
function IfMatchesRouter(matcher, thenRouterOrHandler, elseRouterOrHandler) { | ||
var _this = this; | ||
var thenRouter = Router.from(thenRouterOrHandler); | ||
var elseRouter = Router.from(elseRouterOrHandler); | ||
_this = _super.call(this, function (m) { return toObservable(matcher(m)) | ||
.flatMap(function (n) { return n | ||
? thenRouter | ||
.getRoute(n) | ||
.map(function (route) { return IfMatchesRouter.routeWithCombinedScore(route, n.score); }) | ||
: elseRouter | ||
.getRoute(m); }); }) || this; | ||
exports.IfMatchesElse = IfMatchesElse; | ||
var IfMatchesThen = /** @class */ (function (_super) { | ||
__extends(IfMatchesThen, _super); | ||
function IfMatchesThen(matcher, getThenRouter) { | ||
var _this = _super.call(this, function (routable) { return toObservable(matcher(routable)) | ||
.map(function (response) { return IfMatches.normalizeMatcherResponse(response); }) | ||
.flatMap(function (match) { return IfMatches.matchIsSuccess(match) | ||
? getThenRouter(match.value) | ||
.getRoute(routable) | ||
.map(function (route) { return route.type === 'action' | ||
? routeWithCombinedScore(route, match.score) | ||
: route; }) | ||
: rxjs_1.Observable.of(Router.noRoute(match.reason)); }); }) || this; | ||
_this.matcher = matcher; | ||
_this.getThenRouter = getThenRouter; | ||
return _this; | ||
} | ||
IfMatchesRouter.routeWithCombinedScore = function (route, newScore) { | ||
var score = toScore(newScore) * toScore(route.score); | ||
return toScore(route.score) === score | ||
? route | ||
: __assign({}, route, { score: score }); | ||
IfMatchesThen.prototype.elseDo = function (elseHandler) { | ||
return this.elseTry(function (reason) { return Router.do(function (routable) { return elseHandler(routable, reason); }); }); | ||
}; | ||
return IfMatchesRouter; | ||
IfMatchesThen.prototype.elseTry = function (arg) { | ||
return new IfMatchesElse(this.matcher, this.getThenRouter, typeof (arg) === 'function' | ||
? arg | ||
: function (reason) { return arg; }); | ||
}; | ||
return IfMatchesThen; | ||
}(Router)); | ||
exports.IfMatchesRouter = IfMatchesRouter; | ||
function ifMatches(matcher, thenRouterOrHandler, elseRouterOrHandler) { | ||
return new IfMatchesRouter(matcher, thenRouterOrHandler, elseRouterOrHandler); | ||
} | ||
exports.ifMatches = ifMatches; | ||
var BeforeRouter = (function (_super) { | ||
__extends(BeforeRouter, _super); | ||
function BeforeRouter(beforeHandler, routerOrHandler) { | ||
exports.IfMatchesThen = IfMatchesThen; | ||
var IfMatches = /** @class */ (function () { | ||
function IfMatches(matcher) { | ||
this.matcher = matcher; | ||
} | ||
IfMatches.matchIsSuccess = function (match) { | ||
return (match.reason === undefined); | ||
}; | ||
IfMatches.prototype.and = function (recognizer) { | ||
var _this = this; | ||
var router = Router | ||
.from(routerOrHandler) | ||
.catchAbstractRoute(Router.abstractRouteWarning); | ||
_this = _super.call(this, function (m) { return router | ||
.getRoute(m) | ||
.map(function (route) { return (__assign({}, route, { action: function () { return toObservable(beforeHandler(m)) | ||
.flatMap(function (_) { return toObservable(route.action()); }); } })); }); }) || this; | ||
return _this; | ||
return new IfMatches(function (routable) { return toObservable(_this.matcher(routable)) | ||
.map(function (response) { return IfMatches.normalizeMatcherResponse(response); }) | ||
.flatMap(function (match) { return IfMatches.matchIsSuccess(match) | ||
? toObservable(recognizer(match.value)) | ||
.flatMap(function (_ifMatches) { return toObservable(_ifMatches.matcher(routable)) | ||
.map(function (_response) { return IfMatches.normalizeMatcherResponse(_response); }) | ||
.map(function (_match) { return IfMatches.matchIsSuccess(_match) | ||
? _ifMatches instanceof IfTrue | ||
? match | ||
: { | ||
value: _match.value, | ||
score: combineScore(match.score, _match.score) | ||
} | ||
: _match; }); }) | ||
: rxjs_1.Observable.of(match); }); }); | ||
}; | ||
IfMatches.prototype.thenDo = function (thenHandler) { | ||
return this.thenTry(function (value) { return Router.do(function (routable) { return thenHandler(routable, value); }); }); | ||
}; | ||
IfMatches.prototype.thenTry = function (arg) { | ||
return new IfMatchesThen(this.matcher, typeof arg === 'function' | ||
? arg | ||
: function (value) { return arg; }); | ||
}; | ||
IfMatches.normalizeMatcherResponse = function (response) { | ||
if (!response) | ||
return { | ||
reason: IfMatches.defaultReason | ||
}; | ||
if (typeof (response) === 'object') { | ||
if (response.reason) { | ||
if (typeof (response.reason) !== 'string') | ||
throw new Error('The reason for NoMatch must be a string'); | ||
return { | ||
reason: response.reason | ||
}; | ||
} | ||
if (response.value !== undefined) { | ||
if (response.score !== undefined && typeof (response.score) !== 'number') | ||
throw new Error('The score for Match must be a number'); | ||
return { | ||
value: response.value, | ||
score: response.score | ||
}; | ||
} | ||
} | ||
return { | ||
value: response, | ||
score: 1 | ||
}; | ||
}; | ||
IfMatches.defaultReason = "none"; | ||
return IfMatches; | ||
}()); | ||
exports.IfMatches = IfMatches; | ||
var IfTrue = /** @class */ (function (_super) { | ||
__extends(IfTrue, _super); | ||
function IfTrue(predicate) { | ||
return _super.call(this, function (routable) { return toObservable(predicate(routable)) | ||
.map(function (response) { | ||
if (response === true || response === false) | ||
return response; | ||
if (typeof (response) === 'object') { | ||
if (response.reason) | ||
return response; | ||
if (response.value !== undefined) { | ||
if (response.value === false) | ||
return false; | ||
if (response.value === true) | ||
return response; | ||
throw new Error('When returning a Match from the predicate for IfTrue, the value must be true or false'); | ||
} | ||
} | ||
throw new Error('The predicate for ifTrue may only return true, false, a Match of true or false, or a NoMatch'); | ||
}); }) || this; | ||
} | ||
return IfTrue; | ||
}(IfMatches)); | ||
exports.IfTrue = IfTrue; | ||
var BeforeRouter = /** @class */ (function (_super) { | ||
__extends(BeforeRouter, _super); | ||
function BeforeRouter(beforeHandler, router) { | ||
return _super.call(this, function (routable) { return router | ||
.getRoute(routable) | ||
.map(function (route) { return route.type === 'action' | ||
? __assign({}, route, { action: function () { return toObservable(beforeHandler(routable)) | ||
.flatMap(function (_) { return toObservable(route.action()); }); } }) : route; }); }) || this; | ||
} | ||
return BeforeRouter; | ||
}(Router)); | ||
exports.BeforeRouter = BeforeRouter; | ||
var AfterRouter = (function (_super) { | ||
var AfterRouter = /** @class */ (function (_super) { | ||
__extends(AfterRouter, _super); | ||
function AfterRouter(afterHandler, routerOrHandler) { | ||
var _this = this; | ||
var router = Router | ||
.from(routerOrHandler) | ||
.catchAbstractRoute(Router.abstractRouteWarning); | ||
_this = _super.call(this, function (m) { return router | ||
.getRoute(m) | ||
.map(function (route) { return (__assign({}, route, { action: function () { return toObservable(route.action()) | ||
.flatMap(function (_) { return toObservable(afterHandler(m)); }); } })); }); }) || this; | ||
return _this; | ||
function AfterRouter(afterHandler, router) { | ||
return _super.call(this, function (routable) { return router | ||
.getRoute(routable) | ||
.map(function (route) { return route.type === 'action' | ||
? __assign({}, route, { action: function () { return toObservable(route.action()) | ||
.flatMap(function (_) { return toObservable(afterHandler(routable)); }); } }) : route; }); }) || this; | ||
} | ||
@@ -288,2 +355,13 @@ return AfterRouter; | ||
exports.AfterRouter = AfterRouter; | ||
var DefaultRouter = /** @class */ (function (_super) { | ||
__extends(DefaultRouter, _super); | ||
function DefaultRouter(mainRouter, getDefaultRouter) { | ||
return _super.call(this, function (routable) { return mainRouter.getRoute(routable) | ||
.flatMap(function (route) { return route.type === 'action' | ||
? rxjs_1.Observable.of(route) | ||
: getDefaultRouter(route.reason).getRoute(routable); }); }) || this; | ||
} | ||
return DefaultRouter; | ||
}(Router)); | ||
exports.DefaultRouter = DefaultRouter; | ||
//# sourceMappingURL=Router.js.map |
@@ -7,3 +7,3 @@ { | ||
}, | ||
"version": "0.14.4", | ||
"version": "0.15.0", | ||
"description": "rules-based app engine", | ||
@@ -24,8 +24,8 @@ "main": "dist/prague.js", | ||
"mocha": "^3.5.0", | ||
"typescript": "2.4.2" | ||
"typescript": "2.6.1" | ||
}, | ||
"dependencies": { | ||
"isomorphic-fetch": "^2.2.1", | ||
"rxjs": "5.4.2" | ||
"rxjs": "5.5.2" | ||
} | ||
} |
@@ -6,3 +6,4 @@ "use strict"; | ||
const expect = chai.expect; | ||
const { toObservable, toFilteredObservable, Router, first, best, run, toScore, IfMatchesRouter, ifTrue, ifMatches, throwRoute, catchRoute, before, after } = require('../dist/prague.js'); | ||
const { toObservable, Router, toScore, routeWithCombinedScore, Helpers } = require('../dist/prague.js'); | ||
const { tryInOrder, tryInScoreOrder, ifMatches, ifTrue } = new Helpers(); | ||
const { Observable } = require('rxjs'); | ||
@@ -167,132 +168,148 @@ | ||
describe('Router.actionRoute', () => { | ||
it('should create an ActionRoute with supplied action and no score', () => { | ||
let action = () => {}; | ||
let route = Router.actionRoute(action); | ||
expect(route.type).to.eql('action'); | ||
expect(route.action).to.eql(action); | ||
expect(route.score).to.eql(1); | ||
}); | ||
describe('toFilteredObservable', () => { | ||
it("should convert a number to an observable", (done) => { | ||
toFilteredObservable(5) | ||
.subscribe(n => { | ||
expect(n).to.eql(5); | ||
done(); | ||
}); | ||
it('should create an ActionRoute with supplied action and score', () => { | ||
let action = () => {}; | ||
let route = Router.actionRoute(action, 0.5); | ||
expect(route.type).to.eql('action'); | ||
expect(route.action).to.eql(action); | ||
expect(route.score).to.eql(.5); | ||
expect(route.reason).to.be.undefined; | ||
}); | ||
}); | ||
it("should convert a string to an observable", (done) => { | ||
toFilteredObservable("Prague") | ||
.subscribe(n => { | ||
expect(n).to.eql("Prague"); | ||
describe('Router.do', () => { | ||
it('should create a router returning an ActionRoute using supplied handler and no score', (done) => { | ||
let handled; | ||
Router.do(m => { handled = m; }) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.type).to.eql('action'); | ||
expect(route.score).to.eql(1); | ||
route.action(); | ||
expect(handled).to.eql(foo); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("should convert an array to an observable", (done) => { | ||
toFilteredObservable([1, 2, 3]) | ||
.subscribe(n => { | ||
expect(n).to.eql([1, 2, 3]); | ||
done(); | ||
}); | ||
}); | ||
describe('(test code) testRouter', () => { | ||
it('should route', (done) => { | ||
let routed; | ||
it("should convert a Promise<number> to an observable", (done) => { | ||
toFilteredObservable(Promise.resolve(5)) | ||
.subscribe(n => { | ||
expect(n).to.eql(5); | ||
done(); | ||
}); | ||
}); | ||
const testRouter = new Router(m => Observable.of(Router.actionRoute( | ||
() => { routed = true; } | ||
))); | ||
it("should convert a Promise<string> to an observable", (done) => { | ||
toFilteredObservable(Promise.resolve("Prague")) | ||
testRouter | ||
.getRoute(foo) | ||
.flatMap(route => toObservable(route.action())) | ||
.subscribe(n => { | ||
expect(n).to.eql("Prague"); | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it("should convert a Promise<array> to an observable", (done) => { | ||
toFilteredObservable(Promise.resolve([1, 2, 3])) | ||
.subscribe(n => { | ||
expect(n).to.eql([1, 2, 3]); | ||
done(); | ||
}); | ||
describe('Router.noRoute', () => { | ||
it('should create an ActionRoute with default reason', () => { | ||
let route = Router.noRoute(); | ||
expect(route.type).to.eql('no'); | ||
expect(route.reason).to.eql('none'); | ||
expect(route.action).to.be.undefined; | ||
expect(route.score).to.be.undefined; | ||
}); | ||
it("should convert an Observable<number> to an observable", (done) => { | ||
toFilteredObservable(Observable.of(5)) | ||
.subscribe(n => { | ||
expect(n).to.eql(5); | ||
done(); | ||
}); | ||
it('should create an ActionRoute with supplied reason', () => { | ||
let route = Router.noRoute('reason'); | ||
expect(route.type).to.eql('no'); | ||
expect(route.reason).to.eql('reason'); | ||
expect(route.action).to.be.undefined; | ||
expect(route.score).to.be.undefined; | ||
}); | ||
}); | ||
it("should convert an Observable<string> to an observable", (done) => { | ||
toFilteredObservable(Observable.of("Prague")) | ||
.subscribe(n => { | ||
expect(n).to.eql("Prague"); | ||
describe('Router.no', () => { | ||
it('should create a router returning a NoRoute with default reason', (done) => { | ||
Router.no() | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.type).to.eql('no'); | ||
expect(route.reason).to.eql('none'); | ||
expect(route.action).to.be.undefined; | ||
expect(route.score).to.be.undefined; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("should convert an Observable<array> to an observable", (done) => { | ||
toFilteredObservable(Observable.of([1, 2, 3])) | ||
.subscribe(n => { | ||
expect(n).to.eql([1, 2, 3]); | ||
it('should create a router returning a NoRoute with supplied reason', (done) => { | ||
Router.no('reason') | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.type).to.eql('no'); | ||
expect(route.reason).to.eql('reason'); | ||
expect(route.action).to.be.undefined; | ||
expect(route.score).to.be.undefined; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it("should complete and never emit on null", (done) => { | ||
toFilteredObservable(null) | ||
.subscribe(throwErr, passErr, done); | ||
describe('Router.route', () => { | ||
it("should complete and never emit on Router.no", (done) => { | ||
Router.no() | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
it("should complete and never emit on undefined", (done) => { | ||
toFilteredObservable(undefined) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
it("should route to testRouter", (done) => { | ||
let routed; | ||
it("should complete and never emit on Promise<null>", (done) => { | ||
toFilteredObservable(Promise.resolve(null)) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
const testRouter = new Router(m => Observable.of(Router.actionRoute( | ||
() => { routed = true; } | ||
))); | ||
it("should complete and never emit on Promise<undefined>", (done) => { | ||
toFilteredObservable(Promise.resolve(undefined)) | ||
.subscribe(throwErr, passErr, done); | ||
testRouter | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("should complete and never emit on Observable<null>", (done) => { | ||
toFilteredObservable(Observable.of(null)) | ||
.subscribe(throwErr, passErr, done); | ||
describe("router.beforeDo", () => { | ||
it("should complete and never emit with Router.no", (done) => { | ||
Router | ||
.no() | ||
.beforeDo( | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done) | ||
}); | ||
it("should complete and never emit on Observable<undefined>", (done) => { | ||
toFilteredObservable(Observable.of(undefined)) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
it("should complete and never emit on Observable.empty()", (done) => { | ||
toFilteredObservable(Observable.empty()) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
}); | ||
describe('Router.null', () => { | ||
it('should not route', (done) => { | ||
Router.null | ||
.getRoute(foo) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
}) | ||
describe('(test code) testRouter', () => { | ||
it('should route', (done) => { | ||
it("should run 'before' handler and then router's action", (done) => { | ||
let handled; | ||
let routed; | ||
const testRouter = new Router(m => Observable.of({ | ||
action: () => { routed = true; } | ||
})); | ||
testRouter | ||
.getRoute(foo) | ||
.flatMap(route => toObservable(route.action())) | ||
Router | ||
.do(m => { | ||
expect(handled).to.be.true; | ||
routed = true; | ||
}) | ||
.beforeDo( | ||
m => { | ||
handled = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
@@ -305,32 +322,45 @@ expect(routed).to.be.true; | ||
describe('Router.route', () => { | ||
it("should complete and never commit on Router.null", (done) => { | ||
Router.null | ||
describe("router.afterDo", () => { | ||
it("should complete and never emit with Router.no", (done) => { | ||
Router | ||
.no() | ||
.afterDo( | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done); | ||
.subscribe(throwErr, passErr, done) | ||
}); | ||
it("should route to testRouter", (done) => { | ||
it("should run router's action and then 'after' router when router is supplied", (done) => { | ||
let handled; | ||
let routed; | ||
const testRouter = new Router(m => Observable.of({ | ||
action: () => { routed = true; } | ||
})); | ||
testRouter | ||
Router | ||
.do(m => { | ||
routed = true; | ||
}) | ||
.afterDo( | ||
m => { | ||
expect(routed).to.be.true; | ||
handled = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
expect(handled).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('Router.fromHandler', () => { | ||
it("should convert a handler to a router", (done) => { | ||
describe("router.defaultDo", () => { | ||
it("should not be run when router returns an action route", (done) => { | ||
let routed; | ||
Router.fromHandler(m => { | ||
routed = true; | ||
}) | ||
Router | ||
.do(m => { | ||
routed = true; | ||
}) | ||
.defaultDo(throwErr) | ||
.route(foo) | ||
@@ -342,11 +372,28 @@ .subscribe(n => { | ||
}); | ||
it("should be run when router returns no route", (done) => { | ||
let handled; | ||
Router | ||
.no() | ||
.defaultDo(m => { | ||
handled = true; | ||
}) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(handled).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('Router.from', () => { | ||
it('should convert a router to a router', (done) => { | ||
describe("router.defaultTry", () => { | ||
it("should not be run when router returns an action route", (done) => { | ||
let routed; | ||
Router.from(Router.fromHandler(m => { | ||
routed = true; | ||
})) | ||
Router | ||
.do(m => { | ||
routed = true; | ||
}) | ||
.defaultTry(Router.do(throwErr)) | ||
.route(foo) | ||
@@ -358,9 +405,13 @@ .subscribe(n => { | ||
}); | ||
}); | ||
it('should convert a handler to a router', (done) => { | ||
describe("router.defaultTry", () => { | ||
it("should not be run when router returns an action route", (done) => { | ||
let routed; | ||
Router.from(m => { | ||
routed = true; | ||
}) | ||
Router | ||
.do(m => { | ||
routed = true; | ||
}) | ||
.defaultTry(reason => Router.do(throwErr)) | ||
.route(foo) | ||
@@ -372,13 +423,28 @@ .subscribe(n => { | ||
}); | ||
}) | ||
it("should be run when router returns no route", (done) => { | ||
let handled; | ||
Router | ||
.no() | ||
.defaultTry(reason => Router.do(m => { | ||
handled = reason; | ||
})) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(handled).to.eql('none'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('first', () => { | ||
describe('tryInOrder', () => { | ||
it('should complete and never emit on no routers', (done) => | ||
first() | ||
tryInOrder() | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done) | ||
); | ||
) | ||
it('should complete and never emit on only null/undefined routers', (done) => | ||
first( | ||
tryInOrder( | ||
null, | ||
@@ -392,4 +458,4 @@ undefined | ||
it('should complete and never emit on only unsuccessful and null/undefined routers', (done) => | ||
first( | ||
Router.null, | ||
tryInOrder( | ||
Router.no(), | ||
null, | ||
@@ -403,4 +469,4 @@ undefined | ||
it('should complete and never emit on no successful routers', (done) => { | ||
first( | ||
Router.null | ||
tryInOrder( | ||
Router.no() | ||
) | ||
@@ -411,22 +477,7 @@ .route(foo) | ||
it('should convert a handler to a router, and route to it', (done) => { | ||
let routed; | ||
first( | ||
m => { | ||
routed = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
it('should route to a single successful router', (done) => { | ||
let routed; | ||
first( | ||
Router.fromHandler(m => { | ||
tryInOrder( | ||
Router.do(m => { | ||
routed = true; | ||
@@ -445,6 +496,6 @@ }) | ||
first( | ||
tryInOrder( | ||
null, | ||
undefined, | ||
Router.fromHandler(m => { | ||
Router.do(m => { | ||
routed = true; | ||
@@ -463,5 +514,5 @@ }) | ||
first( | ||
Router.null, | ||
Router.fromHandler(m => { | ||
tryInOrder( | ||
Router.no(), | ||
Router.do(m => { | ||
routed = true; | ||
@@ -479,10 +530,5 @@ }) | ||
const makeRouter = (score, action) => new Router(m => Observable.of({ | ||
score, | ||
action | ||
})); | ||
describe('best', () => { | ||
describe('tryInScoreOrder', () => { | ||
it('should complete and never emit on no routers', (done) => | ||
best() | ||
tryInScoreOrder() | ||
.route(foo) | ||
@@ -493,3 +539,3 @@ .subscribe(throwErr, passErr, done) | ||
it('should complete and never emit on only null/undefined routers', (done) => | ||
best( | ||
tryInScoreOrder( | ||
null, | ||
@@ -503,4 +549,4 @@ undefined | ||
it('should complete and never emit on only unsuccessful and null/undefined routers', (done) => | ||
best( | ||
Router.null, | ||
tryInScoreOrder( | ||
Router.no(), | ||
null, | ||
@@ -514,4 +560,4 @@ undefined | ||
it('should complete and never emit on no successful routers', (done) => { | ||
best( | ||
Router.null | ||
tryInScoreOrder( | ||
Router.no() | ||
) | ||
@@ -522,22 +568,7 @@ .route(foo) | ||
it('should convert a handler to a router, and route to it', (done) => { | ||
let routed; | ||
best( | ||
m => { | ||
routed = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
it('should route to a single successful scoreless router', (done) => { | ||
let routed; | ||
best( | ||
Router.fromHandler(m => { | ||
tryInScoreOrder( | ||
Router.do(m => { | ||
routed = true; | ||
@@ -556,6 +587,6 @@ }) | ||
best( | ||
tryInScoreOrder( | ||
null, | ||
undefined, | ||
Router.fromHandler(m => { | ||
Router.do(m => { | ||
routed = true; | ||
@@ -574,5 +605,5 @@ }) | ||
best( | ||
Router.null, | ||
Router.fromHandler(m => { | ||
tryInScoreOrder( | ||
Router.no(), | ||
Router.do(m => { | ||
routed = true; | ||
@@ -591,6 +622,6 @@ }) | ||
best( | ||
m => { | ||
tryInScoreOrder( | ||
Router.do(m => { | ||
routed = true; | ||
}, | ||
}), | ||
throwErr | ||
@@ -608,5 +639,5 @@ ) | ||
best( | ||
makeRouter(0.75, _ => { routed = 'first'; }), | ||
makeRouter(0.50, _ => { routed = 'second'; }) | ||
tryInScoreOrder( | ||
Router.do(_ => { routed = 'first'; }, 0.75), | ||
Router.do(_ => { routed = 'second'; }, 0.50) | ||
) | ||
@@ -623,5 +654,5 @@ .route(foo) | ||
best( | ||
makeRouter(0.50, _ => { routed = 'first'; }), | ||
makeRouter(0.75, _ => { routed = 'second'; }) | ||
tryInScoreOrder( | ||
Router.do(_ => { routed = 'first'; }, .5), | ||
Router.do(_ => { routed = 'second'; }, .75) | ||
) | ||
@@ -638,5 +669,5 @@ .route(foo) | ||
best( | ||
makeRouter(undefined, _ => { routed = 'first'; }), | ||
makeRouter(0.75, _ => { routed = 'second'; }) | ||
tryInScoreOrder( | ||
Router.do(_ => { routed = 'first'; }), | ||
Router.do(_ => { routed = 'second'; }, .75) | ||
) | ||
@@ -653,5 +684,5 @@ .route(foo) | ||
best( | ||
makeRouter(0.75, _ => { routed = 'first'; }), | ||
makeRouter(0.75, _ => { routed = 'second'; }) | ||
tryInScoreOrder( | ||
Router.do(_ => { routed = 'first'; }, 0.75), | ||
Router.do(_ => { routed = 'second'; }, 0.75) | ||
) | ||
@@ -666,8 +697,7 @@ .route(foo) | ||
describe('run', () => { | ||
describe('Router.noop', () => { | ||
it("should execute the handler, complete, and never emit", (done) => { | ||
let routed; | ||
run( | ||
Router.noop( | ||
m => { | ||
@@ -685,37 +715,8 @@ routed = true; | ||
describe('IfMatchesRouter.routeWithCombinedScore', () => { | ||
it("should return score=1 with both scores undefined", () => { | ||
expect(toScore(IfMatchesRouter.routeWithCombinedScore( | ||
{ | ||
action: () => {} | ||
} | ||
).score)).to.eql(1); | ||
}); | ||
it("should return supplied score when route score undefined", () => { | ||
expect(toScore(IfMatchesRouter.routeWithCombinedScore( | ||
{ | ||
action: () => {} | ||
}, | ||
.13 | ||
).score)).to.eql(.13); | ||
}); | ||
it("should return route score when supplied score undefined", () => { | ||
expect(toScore(IfMatchesRouter.routeWithCombinedScore( | ||
{ | ||
score: .13, | ||
action: () => {} | ||
} | ||
).score)).to.eql(.13); | ||
}); | ||
it("should return combined score when both scores supplied", () => { | ||
expect(toScore(IfMatchesRouter.routeWithCombinedScore( | ||
{ | ||
score: .4, | ||
action: () => {} | ||
}, | ||
describe('routeWithCombinedScore', () => { | ||
it("should return combined score", () => { | ||
expect(routeWithCombinedScore( | ||
Router.actionRoute(() => {}, .4), | ||
.25 | ||
).score)).to.eql(.1); | ||
).score).to.eql(.1); | ||
}); | ||
@@ -726,6 +727,4 @@ }) | ||
it("should complete and never emit on false when 'else' router doesn't exist", (done) => | ||
ifTrue( | ||
m => false, | ||
throwErr | ||
) | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.route(foo) | ||
@@ -735,8 +734,6 @@ .subscribe(throwErr, passErr, done) | ||
it("should complete and never emit on true when 'else' router doesn't route", (done) => | ||
ifTrue( | ||
m => false, | ||
throwErr, | ||
Router.null | ||
) | ||
it("should complete and never emit on false when 'else' router doesn't route", (done) => | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.elseTry(Router.no()) | ||
.route(foo) | ||
@@ -747,6 +744,4 @@ .subscribe(throwErr, passErr, done) | ||
it("should complete and never emit on true when 'if' router doesn't route and 'else' router doesn't exist", (done) => | ||
ifTrue( | ||
m => true, | ||
Router.null | ||
) | ||
ifTrue(m => true) | ||
.thenTry(Router.no()) | ||
.route(foo) | ||
@@ -757,7 +752,5 @@ .subscribe(throwErr, passErr, done) | ||
it("should complete and never emit on true when 'if' router doesn't route and 'else' router exists", (done) => | ||
ifTrue( | ||
m => true, | ||
Router.null, | ||
throwErr | ||
) | ||
ifTrue(m => true) | ||
.thenTry(Router.no()) | ||
.elseDo(throwErr) | ||
.route(foo) | ||
@@ -770,8 +763,6 @@ .subscribe(throwErr, passErr, done) | ||
ifTrue( | ||
m => true, | ||
m => { | ||
ifTrue(m => true) | ||
.thenDo(m => { | ||
routed = true; | ||
} | ||
) | ||
}) | ||
.route(foo) | ||
@@ -781,3 +772,3 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
@@ -788,9 +779,7 @@ | ||
ifTrue( | ||
m => true, | ||
m => { | ||
ifTrue(m => true) | ||
.thenDo(m => { | ||
routed = true; | ||
}, | ||
throwErr | ||
) | ||
}) | ||
.elseDo(throwErr) | ||
.route(foo) | ||
@@ -800,3 +789,3 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
@@ -807,8 +796,6 @@ | ||
ifTrue( | ||
m => true, | ||
Router.fromHandler(m => { | ||
ifTrue(m => true) | ||
.thenTry(Router.do(m => { | ||
routed = true; | ||
}) | ||
) | ||
})) | ||
.route(foo) | ||
@@ -818,3 +805,3 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
@@ -825,9 +812,7 @@ | ||
ifTrue( | ||
m => true, | ||
Router.fromHandler(m => { | ||
ifTrue(m => true) | ||
.thenTry(Router.do(m => { | ||
routed = true; | ||
}), | ||
throwErr | ||
) | ||
})) | ||
.elseDo(throwErr) | ||
.route(foo) | ||
@@ -837,3 +822,3 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
@@ -844,9 +829,7 @@ | ||
ifTrue( | ||
m => false, | ||
throwErr, | ||
m => { | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.elseDo(m => { | ||
routed = true; | ||
} | ||
) | ||
}) | ||
.route(foo) | ||
@@ -856,3 +839,3 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
@@ -863,9 +846,7 @@ | ||
ifTrue( | ||
m => false, | ||
throwErr, | ||
Router.fromHandler(m => { | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.elseTry(Router.do(m => { | ||
routed = true; | ||
}) | ||
) | ||
})) | ||
.route(foo) | ||
@@ -875,63 +856,116 @@ .subscribe(n => { | ||
done(); | ||
}) | ||
}); | ||
}); | ||
it("should return score=1 on true predicate when 'if' score undefined", (done) => { | ||
ifTrue( | ||
m => true, | ||
m => {} | ||
) | ||
ifTrue(m => true) | ||
.thenDo(m => {}) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(1); | ||
expect(route.score).to.eql(1); | ||
done(); | ||
}) | ||
}); | ||
}); | ||
it("should return route score on true predicate", (done) => { | ||
ifTrue( | ||
m => true, | ||
makeRouter(0.25, () => {}) | ||
) | ||
ifTrue(m => true) | ||
.thenTry(Router.do(() => {}, 0.25)) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.25); | ||
expect(route.score).to.eql(.25); | ||
done(); | ||
}) | ||
}); | ||
}); | ||
it("should return score=1 on false predicate when 'else' score undefined", (done) => { | ||
ifTrue( | ||
m => false, | ||
m => {}, | ||
m => {} | ||
) | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.elseDo(m => {}) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(1); | ||
expect(route.score).to.eql(1); | ||
done(); | ||
}) | ||
}); | ||
}); | ||
it("should return 'else' route score on false predicate", (done) => { | ||
ifTrue( | ||
m => false, | ||
throwErr, | ||
makeRouter(0.5, () => {}) | ||
) | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.elseTry(Router.do(_ => {}, .5)) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.5); | ||
expect(route.score).to.eql(.5); | ||
done(); | ||
}) | ||
}); | ||
}); | ||
it("should throw on string", (done) => { | ||
ifTrue(m => 'foo') | ||
.thenDo(throwErr) | ||
.getRoute(foo) | ||
.subscribe(throwErr, error => { | ||
done(); | ||
}, throwErr); | ||
}); | ||
it("should throw on object", (done) => { | ||
ifTrue(m => ({ foo: "foo" })) | ||
.thenDo(throwErr) | ||
.getRoute(foo) | ||
.subscribe(throwErr, error => { | ||
done(); | ||
}, throwErr); | ||
}); | ||
it("should return a default reason on false", (done) => { | ||
ifTrue(m => false) | ||
.thenDo(throwErr) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.reason).to.eql("none"); | ||
done(); | ||
}); | ||
}); | ||
it("should return supplied reason", (done) => { | ||
ifTrue(m => ({ reason: 'whatevs' })) | ||
.thenDo(throwErr) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.reason).to.eql("whatevs"); | ||
done(); | ||
}); | ||
}); | ||
it("should use formal true value", (done) => { | ||
let handled; | ||
ifTrue(m => ({ value: true, score: .5 })) | ||
.thenDo(m => { handled = true; }) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
route.action(); | ||
expect(handled).to.be.true; | ||
expect(route.score).to.eql(.5); | ||
done(); | ||
}); | ||
}); | ||
it("should use formal false value", (done) => { | ||
let handled; | ||
ifTrue(m => ({ value: false })) | ||
.thenDo(throwErr) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.type).to.eql('no') | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('ifMatches', () => { | ||
it("should complete and never emit on no match when 'else' router doesn't exist", (done) => | ||
ifMatches( | ||
addBar, | ||
throwErr | ||
) | ||
ifMatches(addBar) | ||
.thenDo(throwErr) | ||
.route(notFoo) | ||
@@ -941,8 +975,5 @@ .subscribe(throwErr, passErr, done) | ||
it("should complete and never emit on no match when 'else' router doesn't route", (done) => | ||
ifMatches( | ||
addBar, | ||
throwErr, | ||
Router.null | ||
) | ||
it("should complete and never emit on no match when 'else' router doesn't exist", (done) => | ||
ifMatches(addBar) | ||
.thenTry(Router.do(throwErr)) | ||
.route(notFoo) | ||
@@ -952,18 +983,14 @@ .subscribe(throwErr, passErr, done) | ||
it("should complete and never emit on match when 'if' router doesn't route and 'else' router doesn't exist", (done) => | ||
ifMatches( | ||
addBar, | ||
Router.null | ||
) | ||
.route(foo) | ||
it("should complete and never emit on no match when 'else' router doesn't route", (done) => | ||
ifMatches(addBar) | ||
.thenDo(throwErr) | ||
.elseTry(Router.no()) | ||
.route(notFoo) | ||
.subscribe(throwErr, passErr, done) | ||
); | ||
it("should complete and never emit on match when 'if' router doesn't route and 'else' router exists", (done) => | ||
ifMatches( | ||
addBar, | ||
Router.null, | ||
throwErr | ||
) | ||
ifMatches(addBar) | ||
.thenTry(Router.no()) | ||
.elseDo(throwErr) | ||
.route(foo) | ||
@@ -976,8 +1003,6 @@ .subscribe(throwErr, passErr, done) | ||
ifMatches( | ||
addBar, | ||
m => { | ||
ifMatches(addBar) | ||
.thenDo(m => { | ||
routed = true; | ||
} | ||
) | ||
}) | ||
.route(foo) | ||
@@ -993,25 +1018,7 @@ .subscribe(n => { | ||
ifMatches( | ||
addBar, | ||
m => { | ||
ifMatches(addBar) | ||
.thenDo(m => { | ||
routed = true; | ||
}, | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}) | ||
}); | ||
it("should route message to 'if' router on match when 'else' router doesn't exist", (done) => { | ||
let routed; | ||
ifMatches( | ||
addBar, | ||
Router.fromHandler(m => { | ||
routed = true; | ||
}) | ||
) | ||
.elseDo(throwErr) | ||
.route(foo) | ||
@@ -1024,29 +1031,10 @@ .subscribe(n => { | ||
it("should route message to 'if' router on match when 'else' router exists", (done) => { | ||
it("should route message to 'else' handler on no match", (done) => { | ||
let routed; | ||
ifMatches( | ||
addBar, | ||
Router.fromHandler(m => { | ||
ifMatches(addBar) | ||
.thenDo(throwErr) | ||
.elseDo(m => { | ||
routed = true; | ||
}), | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}) | ||
}); | ||
it("should route message to 'else' handler on no match", (done) => { | ||
let routed; | ||
ifMatches( | ||
addBar, | ||
throwErr, | ||
m => { | ||
routed = true; | ||
} | ||
) | ||
.route(notFoo) | ||
@@ -1062,9 +1050,7 @@ .subscribe(n => { | ||
ifMatches( | ||
addBar, | ||
throwErr, | ||
Router.fromHandler(m => { | ||
ifMatches(addBar) | ||
.thenDo(throwErr) | ||
.elseTry(Router.do(m => { | ||
routed = true; | ||
}) | ||
) | ||
})) | ||
.route(notFoo) | ||
@@ -1077,10 +1063,8 @@ .subscribe(n => { | ||
it("should return score=1 on scoreless match when 'if' score undefined", (done) => { | ||
ifMatches( | ||
m => ({}), | ||
m => {} | ||
) | ||
it("should return score=1 when score not supplied", (done) => { | ||
ifMatches(addBar) | ||
.thenDo(m => {}) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(1); | ||
expect(route.score).to.eql(1); | ||
done(); | ||
@@ -1090,12 +1074,8 @@ }) | ||
it("should return supplied score when 'if' score undefined", (done) => { | ||
ifMatches( | ||
m => ({ | ||
score: 0.4 | ||
}), | ||
() => {} | ||
) | ||
it("should return supplied score", (done) => { | ||
ifMatches(m => ({ value: 'dog', score: 0.4 })) | ||
.thenDo(m => {}) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.4); | ||
expect(route.score).to.eql(.4); | ||
done(); | ||
@@ -1105,37 +1085,31 @@ }) | ||
it("should return route score on scoreless match", (done) => { | ||
ifMatches( | ||
m => ({}), | ||
makeRouter(0.25, () => {}) | ||
) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.25); | ||
it("should pass supplied value to handler", (done) => { | ||
let handled; | ||
ifMatches(m => ({ value: 'dog' })) | ||
.thenDo((m, value) => { | ||
handled = value; | ||
}) | ||
.route(foo) | ||
.subscribe(_ => { | ||
expect(handled).to.eql('dog'); | ||
done(); | ||
}) | ||
}); | ||
it("should return combined score when both scores supplied", (done) => { | ||
ifMatches( | ||
m => ({ | ||
score: 0.4 | ||
}), | ||
makeRouter(0.25, () => {}) | ||
) | ||
it("should return combined score when route score supplied", (done) => { | ||
ifMatches(addBar) | ||
.thenTry(Router.do(() => {}, .25)) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.1); | ||
expect(route.score).to.eql(.25); | ||
done(); | ||
}) | ||
}); | ||
it("should return score=1 on scoreless match when 'else' score undefined", (done) => { | ||
ifMatches( | ||
m => ({}), | ||
m => {}, | ||
m => {} | ||
) | ||
it("should return combined score when both scores supplied", (done) => { | ||
ifMatches(m => ({ value: 'cat', score: 0.4 })) | ||
.thenTry(Router.do(() => {}, .25)) | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(1); | ||
expect(route.score).to.eql(.1); | ||
done(); | ||
@@ -1146,10 +1120,8 @@ }) | ||
it("should return 'else' route score on no match", (done) => { | ||
ifMatches( | ||
addBar, | ||
throwErr, | ||
makeRouter(0.5, () => {}) | ||
) | ||
ifMatches(addBar) | ||
.thenDo(throwErr) | ||
.elseTry(Router.do(() => {}, .5)) | ||
.getRoute(notFoo) | ||
.subscribe(route => { | ||
expect(toScore(route.score)).to.eql(.5); | ||
expect(route.score).to.eql(.5); | ||
done(); | ||
@@ -1159,118 +1131,1 @@ }) | ||
}); | ||
describe('throwRoute', () => { | ||
it("should throw a route with thrown === true", (done) => { | ||
throwRoute() | ||
.getRoute(foo) | ||
.subscribe(route => { | ||
expect(route.thrown).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('catchRoute', () => { | ||
it("should pass through the route from a handler", (done) => { | ||
let routed; | ||
catchRoute(m => { | ||
routed = true; | ||
}) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
it("should pass through the route from a non-throwing router", (done) => { | ||
let routed; | ||
catchRoute(Router.fromHandler(m => { | ||
routed = true; | ||
})) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
it("should complete and never emit with a thrown route", (done) => { | ||
let routed; | ||
catchRoute(throwRoute()) | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done); | ||
}); | ||
}); | ||
describe("Router.before", () => { | ||
it("should complete and never emit with null router", (done) => { | ||
Router | ||
.null | ||
.doBefore( | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done) | ||
}); | ||
it("should run 'before' handler and then router's action", (done) => { | ||
let handled; | ||
let routed; | ||
Router | ||
.fromHandler(m => { | ||
expect(handled).to.be.true; | ||
routed = true; | ||
}) | ||
.doBefore( | ||
m => { | ||
handled = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(routed).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("after", () => { | ||
it("should complete and never emit with null router", (done) => { | ||
Router | ||
.null | ||
.doAfter( | ||
throwErr | ||
) | ||
.route(foo) | ||
.subscribe(throwErr, passErr, done) | ||
}); | ||
it("should run router's action and then 'after' router when router is supplied", (done) => { | ||
let handled; | ||
let routed; | ||
Router | ||
.fromHandler(m => { | ||
routed = true; | ||
}) | ||
.doAfter( | ||
m => { | ||
expect(routed).to.be.true; | ||
handled = true; | ||
} | ||
) | ||
.route(foo) | ||
.subscribe(n => { | ||
expect(handled).to.be.true; | ||
done(); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
210116
48
3134
+ Addedrxjs@5.5.2(transitive)
- Removedrxjs@5.4.2(transitive)
Updatedrxjs@5.5.2