Skip to content

Commit a7d5fcb

Browse files
refactor(UrlMatcherFactory): merge MatcherConfig into UrlMatcherFactory
- Previously, matcherConfig was a global object, exported from its own es6 module. - Now, matcherConfig is stored per-instance on the UrlMatcherFactory - Previously, Param.from* methods were static on Param class. - Now, urlMatcherFactory has `paramFactory` for creating Param objects
1 parent fddd1e2 commit a7d5fcb

14 files changed

+242
-249
lines changed

src/params/param.ts

+13-28
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
* @internalapi
33
* @module params
44
*/ /** for typedoc */
5-
import {extend, filter, map, applyPairs, allTrueR} from "../common/common";
6-
import {prop, propEq} from "../common/hof";
7-
import {isInjectable, isDefined, isString, isArray} from "../common/predicates";
8-
import {RawParams, ParamDeclaration} from "../params/interface";
9-
import {services} from "../common/coreservices";
10-
import {matcherConfig} from "../url/urlMatcherConfig";
11-
import {ParamType} from "./type";
12-
import {ParamTypes} from "./paramTypes";
5+
import { extend, filter, map, applyPairs, allTrueR } from "../common/common";
6+
import { prop, propEq } from "../common/hof";
7+
import { isInjectable, isDefined, isString, isArray } from "../common/predicates";
8+
import { RawParams, ParamDeclaration } from "../params/interface";
9+
import { services } from "../common/coreservices";
10+
import { ParamType } from "./type";
11+
import { ParamTypes } from "./paramTypes";
12+
import { UrlMatcherFactory } from "../url/urlMatcherFactory";
1313

1414
let hasOwn = Object.prototype.hasOwnProperty;
1515
let isShorthand = (cfg: ParamDeclaration) =>
@@ -43,10 +43,10 @@ function getType(cfg: ParamDeclaration, urlType: ParamType, location: DefType, i
4343
/**
4444
* returns false, true, or the squash value to indicate the "default parameter url squash policy".
4545
*/
46-
function getSquashPolicy(config: ParamDeclaration, isOptional: boolean) {
46+
function getSquashPolicy(config: ParamDeclaration, isOptional: boolean, defaultPolicy: (boolean|string)) {
4747
let squash = config.squash;
4848
if (!isOptional || squash === false) return false;
49-
if (!isDefined(squash) || squash == null) return matcherConfig.defaultSquashPolicy();
49+
if (!isDefined(squash) || squash == null) return defaultPolicy;
5050
if (squash === true || isString(squash)) return squash;
5151
throw new Error(`Invalid squash policy: '${squash}'. Valid policies: false, true, or arbitrary string`);
5252
}
@@ -75,15 +75,15 @@ export class Param {
7575
raw: boolean;
7676
config: any;
7777

78-
constructor(id: string, type: ParamType, config: ParamDeclaration, location: DefType, paramTypes: ParamTypes) {
78+
constructor(id: string, type: ParamType, config: ParamDeclaration, location: DefType, urlMatcherFactory: UrlMatcherFactory) {
7979
config = unwrapShorthand(config);
80-
type = getType(config, type, location, id, paramTypes);
80+
type = getType(config, type, location, id, urlMatcherFactory.paramTypes);
8181
let arrayMode = getArrayMode();
8282
type = arrayMode ? type.$asArray(arrayMode, location === DefType.SEARCH) : type;
8383
let isOptional = config.value !== undefined;
8484
let dynamic = isDefined(config.dynamic) ? !!config.dynamic : !!type.dynamic;
8585
let raw = isDefined(config.raw) ? !!config.raw : !!type.raw;
86-
let squash = getSquashPolicy(config, isOptional);
86+
let squash = getSquashPolicy(config, isOptional, urlMatcherFactory.defaultSquashPolicy());
8787
let replace = getReplace(config, arrayMode, isOptional, squash);
8888

8989
// array config: param name (param[]) overrides default settings. explicit config overrides param name.
@@ -146,21 +146,6 @@ export class Param {
146146
return `{Param:${this.id} ${this.type} squash: '${this.squash}' optional: ${this.isOptional}}`;
147147
}
148148

149-
/** Creates a new [[Param]] from a CONFIG block */
150-
static fromConfig(id: string, type: ParamType, config: any, paramTypes: ParamTypes): Param {
151-
return new Param(id, type, config, DefType.CONFIG, paramTypes);
152-
}
153-
154-
/** Creates a new [[Param]] from a url PATH */
155-
static fromPath(id: string, type: ParamType, config: any, paramTypes: ParamTypes): Param {
156-
return new Param(id, type, config, DefType.PATH, paramTypes);
157-
}
158-
159-
/** Creates a new [[Param]] from a url SEARCH */
160-
static fromSearch(id: string, type: ParamType, config: any, paramTypes: ParamTypes): Param {
161-
return new Param(id, type, config, DefType.SEARCH, paramTypes);
162-
}
163-
164149
static values(params: Param[], values: RawParams = {}): RawParams {
165150
return <RawParams> params.map(param => [param.id, param.value(values[param.id])]).reduce(applyPairs, {});
166151
}

src/state/stateBuilder.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {UrlMatcher} from "../url/urlMatcher";
1313
import {Resolvable} from "../resolve/resolvable";
1414
import {services} from "../common/coreservices";
1515
import {ResolvePolicy} from "../resolve/interface";
16-
import {ParamTypes} from "../params/paramTypes";
16+
import { ParamFactory } from "../url/interface";
1717

1818
const parseUrl = (url: string): any => {
1919
if (!isString(url)) return false;
@@ -84,9 +84,9 @@ function navigableBuilder(state: State) {
8484
return !isRoot(state) && state.url ? state : (state.parent ? state.parent.navigable : null);
8585
};
8686

87-
const getParamsBuilder = (paramTypes: ParamTypes) =>
87+
const getParamsBuilder = (paramFactory: ParamFactory) =>
8888
function paramsBuilder(state: State): { [key: string]: Param } {
89-
const makeConfigParam = (config: any, id: string) => Param.fromConfig(id, null, config, paramTypes);
89+
const makeConfigParam = (config: any, id: string) => paramFactory.fromConfig(id, null, config);
9090
let urlParams: Param[] = (state.url && state.url.parameters({inherit: false})) || [];
9191
let nonUrlParams: Param[] = values(mapObj(omit(state.params || {}, urlParams.map(prop('id'))), makeConfigParam));
9292
return urlParams.concat(nonUrlParams).map(p => [p.id, p]).reduce(applyPairs, {});
@@ -212,7 +212,7 @@ export class StateBuilder {
212212
/** An object that contains all the BuilderFunctions registered, key'd by the name of the State property they build */
213213
private builders: Builders;
214214

215-
constructor(private matcher: StateMatcher, $urlMatcherFactoryProvider: UrlMatcherFactory) {
215+
constructor(private matcher: StateMatcher, urlMatcherFactory: UrlMatcherFactory) {
216216
let self = this;
217217

218218
const root = () => matcher.find("");
@@ -229,10 +229,10 @@ export class StateBuilder {
229229
parent: [ parentBuilder ],
230230
data: [ dataBuilder ],
231231
// Build a URLMatcher if necessary, either via a relative or absolute URL
232-
url: [ getUrlBuilder($urlMatcherFactoryProvider, root) ],
232+
url: [ getUrlBuilder(urlMatcherFactory, root) ],
233233
// Keep track of the closest ancestor state that has a URL (i.e. is navigable)
234234
navigable: [ getNavigableBuilder(isRoot) ],
235-
params: [ getParamsBuilder($urlMatcherFactoryProvider.paramTypes) ],
235+
params: [ getParamsBuilder(urlMatcherFactory.paramFactory) ],
236236
// Each framework-specific ui-router implementation should define its own `views` builder
237237
// e.g., src/ng1/statebuilders/views.ts
238238
views: [],

src/url/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* @module url
44
*/ /** for typedoc */
55
export * from "./urlMatcher";
6-
export * from "./urlMatcherConfig";
76
export * from "./urlMatcherFactory";
87
export * from "./urlRouter";
98
export * from "./urlRule";

src/url/interface.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { LocationServices, $InjectorLike, LocationConfig } from "../common/coreservices";
2+
import { UrlRule } from "./urlRule";
3+
import { UrlMatcher } from "./urlMatcher";
4+
import { IInjectable } from "../common/common";
5+
import { ParamType } from "../params/type";
6+
import { Param } from "../params/param";
7+
8+
export interface ParamFactory {
9+
/** Creates a new [[Param]] from a CONFIG block */
10+
fromConfig(id: string, type: ParamType, config: any): Param;
11+
/** Creates a new [[Param]] from a url PATH */
12+
fromPath(id: string, type: ParamType, config: any): Param;
13+
/** Creates a new [[Param]] from a url SEARCH */
14+
fromSearch(id: string, type: ParamType, config: any): Param;
15+
}
16+
17+
export interface UrlConfig extends LocationConfig, UrlMatcherConfig {};
18+
19+
export interface UrlMatcherConfig {
20+
caseInsensitive(value?: boolean): boolean;
21+
strictMode(value?: boolean): boolean;
22+
defaultSquashPolicy(value?: (boolean|string)): (boolean|string);
23+
paramType(name, type?)
24+
}
25+

src/url/urlMatcher.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {DefType} from "../params/param";
1515
import {unnestR} from "../common/common";
1616
import {arrayTuples} from "../common/common";
1717
import {RawParams} from "../params/interface";
18+
import { ParamFactory } from "./interface";
1819

1920
/** @hidden */
2021
function quoteRegExp(string: any, param?: any) {
@@ -114,7 +115,7 @@ export class UrlMatcher {
114115
* - `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
115116
* - `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`.
116117
*/
117-
constructor(pattern: string, paramTypes: ParamTypes, public config?: any) {
118+
constructor(pattern: string, paramTypes: ParamTypes, paramFactory: ParamFactory, public config?: any) {
118119
this.pattern = pattern;
119120
this.config = defaults(this.config, {
120121
params: {},
@@ -172,7 +173,7 @@ export class UrlMatcher {
172173
if (p.segment.indexOf('?') >= 0) break; // we're into the search part
173174

174175
checkParamErrors(p.id);
175-
this._params.push(Param.fromPath(p.id, p.type, this.config.paramMap(p.cfg, false), paramTypes));
176+
this._params.push(paramFactory.fromPath(p.id, p.type, this.config.paramMap(p.cfg, false)));
176177
this._segments.push(p.segment);
177178
patterns.push([p.segment, tail(this._params)]);
178179
last = placeholder.lastIndex;
@@ -192,7 +193,7 @@ export class UrlMatcher {
192193
while ((m = searchPlaceholder.exec(search))) {
193194
p = matchDetails(m, true);
194195
checkParamErrors(p.id);
195-
this._params.push(Param.fromSearch(p.id, p.type, this.config.paramMap(p.cfg, true), paramTypes));
196+
this._params.push(paramFactory.fromSearch(p.id, p.type, this.config.paramMap(p.cfg, true)));
196197
last = placeholder.lastIndex;
197198
// check if ?&
198199
}

src/url/urlMatcherConfig.ts

-25
This file was deleted.

src/url/urlMatcherFactory.ts

+41-25
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,15 @@
22
* @coreapi
33
* @module url
44
*/ /** for typedoc */
5-
import {forEach, extend} from "../common/common";
6-
import {isObject, isDefined, isFunction} from "../common/predicates";
7-
8-
import {UrlMatcher} from "./urlMatcher";
9-
import {matcherConfig} from "./urlMatcherConfig";
10-
import {Param} from "../params/param";
11-
import {ParamTypes} from "../params/paramTypes";
12-
import {ParamTypeDefinition} from "../params/interface";
5+
import { forEach, extend } from "../common/common";
6+
import { isObject, isDefined, isFunction, isString } from "../common/predicates";
7+
import { UrlMatcher } from "./urlMatcher";
8+
import { Param, DefType } from "../params/param";
9+
import { ParamTypes } from "../params/paramTypes";
10+
import { ParamTypeDefinition } from "../params/interface";
1311
import { Disposable } from "../interface";
14-
15-
/** @hidden */
16-
function getDefaultConfig() {
17-
return {
18-
strict: matcherConfig.strictMode(),
19-
caseInsensitive: matcherConfig.caseInsensitive()
20-
};
21-
}
12+
import { ParamType } from "../params/type";
13+
import { ParamFactory } from "./interface";
2214

2315
/**
2416
* Factory for [[UrlMatcher]] instances.
@@ -27,7 +19,10 @@ function getDefaultConfig() {
2719
* `$urlMatcherFactor` or ng1 providers as `$urlMatcherFactoryProvider`.
2820
*/
2921
export class UrlMatcherFactory implements Disposable {
30-
paramTypes = new ParamTypes();
22+
/** @hidden */ paramTypes = new ParamTypes();
23+
/** @hidden */ _isCaseInsensitive: boolean = false;
24+
/** @hidden */ _isStrictMode: boolean = true;
25+
/** @hidden */ _defaultSquashPolicy: (boolean|string) = false;
3126

3227
constructor() {
3328
extend(this, { UrlMatcher, Param });
@@ -39,8 +34,8 @@ export class UrlMatcherFactory implements Disposable {
3934
* @param value `false` to match URL in a case sensitive manner; otherwise `true`;
4035
* @returns the current value of caseInsensitive
4136
*/
42-
caseInsensitive(value: boolean) {
43-
return matcherConfig.caseInsensitive(value);
37+
caseInsensitive(value?: boolean): boolean {
38+
return this._isCaseInsensitive = isDefined(value) ? value : this._isCaseInsensitive;
4439
}
4540

4641
/**
@@ -49,8 +44,8 @@ export class UrlMatcherFactory implements Disposable {
4944
* @param value `false` to match trailing slashes in URLs, otherwise `true`.
5045
* @returns the current value of strictMode
5146
*/
52-
strictMode(value: boolean) {
53-
return matcherConfig.strictMode(value);
47+
strictMode(value?: boolean): boolean {
48+
return this._isStrictMode = isDefined(value) ? value : this._isStrictMode;
5449
}
5550

5651
/**
@@ -64,10 +59,16 @@ export class UrlMatcherFactory implements Disposable {
6459
* the parameter value from the URL and replace it with this string.
6560
* @returns the current value of defaultSquashPolicy
6661
*/
67-
defaultSquashPolicy(value: string) {
68-
return matcherConfig.defaultSquashPolicy(value);
62+
defaultSquashPolicy(value?: (boolean|string)) {
63+
if (isDefined(value) && value !== true && value !== false && !isString(value))
64+
throw new Error(`Invalid squash policy: ${value}. Valid policies: false, true, arbitrary-string`);
65+
return this._defaultSquashPolicy = isDefined(value) ? value : this._defaultSquashPolicy;
6966
}
7067

68+
/** @hidden */
69+
private _getConfig = (config) =>
70+
extend({ strict: this._isStrictMode, caseInsensitive: this._isCaseInsensitive }, config);
71+
7172
/**
7273
* Creates a [[UrlMatcher]] for the specified pattern.
7374
*
@@ -76,7 +77,7 @@ export class UrlMatcherFactory implements Disposable {
7677
* @returns The UrlMatcher.
7778
*/
7879
compile(pattern: string, config?: { [key: string]: any }) {
79-
return new UrlMatcher(pattern, this.paramTypes, extend(getDefaultConfig(), config));
80+
return new UrlMatcher(pattern, this.paramTypes, this.paramFactory, this._getConfig(config));
8081
}
8182

8283
/**
@@ -86,7 +87,7 @@ export class UrlMatcherFactory implements Disposable {
8687
* @returns `true` if the object matches the `UrlMatcher` interface, by
8788
* implementing all the same methods.
8889
*/
89-
isMatcher(object: any) {
90+
isMatcher(object: any): boolean {
9091
// TODO: typeof?
9192
if (!isObject(object)) return false;
9293
let result = true;
@@ -128,6 +129,21 @@ export class UrlMatcherFactory implements Disposable {
128129
return this;
129130
};
130131

132+
/** @internalapi Creates a new [[Param]] for a given location (DefType) */
133+
paramFactory: ParamFactory = {
134+
/** Creates a new [[Param]] from a CONFIG block */
135+
fromConfig: (id: string, type: ParamType, config: any) =>
136+
new Param(id, type, config, DefType.CONFIG, this),
137+
138+
/** Creates a new [[Param]] from a url PATH */
139+
fromPath: (id: string, type: ParamType, config: any) =>
140+
new Param(id, type, config, DefType.PATH, this),
141+
142+
/** Creates a new [[Param]] from a url SEARCH */
143+
fromSearch: (id: string, type: ParamType, config: any) =>
144+
new Param(id, type, config, DefType.SEARCH, this),
145+
};
146+
131147
/** @internalapi */
132148
dispose() {
133149
this.paramTypes.dispose();

src/url/urlRouter.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ export class UrlRouter implements Disposable {
141141
*
142142
* #### Example:
143143
* ```js
144-
* $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
145-
* person: "bob"
146-
* });
144+
* matcher = $umf.compile("/about/:person");
145+
* params = { person: "bob" };
146+
* $bob = $urlRouter.href(matcher, params);
147147
* // $bob == "/about/bob";
148148
* ```
149149
*

src/url/urlService.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { UIRouter } from "../router";
44
import { LocationServices, notImplemented, LocationConfig } from "../common/coreservices";
55
import { noop, createProxyFunctions } from "../common/common";
6+
import { UrlConfig } from "./interface";
67

78
/** @hidden */
89
const makeStub = (keys: string[]): any =>
@@ -49,7 +50,7 @@ export class UrlService implements LocationServices {
4950
* This information can be used to build absolute URLs, such as
5051
* `https://example.com:443/basepath/state/substate?param1=a#hashvalue`;
5152
*/
52-
config: LocationConfig;
53+
config: UrlConfig;
5354

5455
constructor(private router: UIRouter) {
5556
this.config = {} as any;

test/stateHelperSpec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ describe('state helpers', function() {
130130
});
131131

132132
it('should compile a UrlMatcher for ^ URLs', function() {
133-
var url = new UrlMatcher('/', paramTypes);
133+
var url = new UrlMatcher('/', paramTypes, null);
134134
spyOn(urlMatcherFactoryProvider, 'compile').and.returnValue(url);
135135
spyOn(urlMatcherFactoryProvider, 'isMatcher').and.returnValue(true);
136136

@@ -158,7 +158,7 @@ describe('state helpers', function() {
158158

159159
it('should pass through custom UrlMatchers', function() {
160160
var root = states[''] = { url: { append: function() {} } };
161-
var url = new UrlMatcher("/", paramTypes);
161+
var url = new UrlMatcher("/", paramTypes, null);
162162
spyOn(urlMatcherFactoryProvider, 'isMatcher').and.returnValue(true);
163163
spyOn(root.url, 'append').and.returnValue(url);
164164
expect(builder.builder('url')({ url: url })).toBe(url);

0 commit comments

Comments
 (0)