##// END OF EJS Templates
ActivationContext removed 'clone' method, rewritten 'enter' method
cin -
r3:8a4027b4c19f default
parent child
Show More
@@ -1,144 +1,138
1 1 import { Descriptor, ILifetime, RegistrationMap, LifetimeContainer, ConfigurableKeys } from "./interfaces";
2 2 import { argumentNotNull } from "./traits";
3 3
4 4 export interface ActivationContextInfo {
5 5 name: string;
6 6
7 7 service: string;
8 8
9 9 }
10 10
11 11 let nextId = 1;
12 12
13 13 /** This class is created once per `Container.resolve` method call and used to
14 14 * cache dependencies and to track created instances. The activation context
15 15 * tracks services with `context` activation type.
16 16 */
17 17 export class ActivationContext<S extends object> {
18 private _cache: { [K: string]: unknown };
18 private readonly _cache: Record<string, unknown>;
19 19
20 private _services: Partial<RegistrationMap<S>>;
20 private readonly _services: Partial<RegistrationMap<S>>;
21 21
22 private _name: string;
22 private readonly _name: string;
23 23
24 private _service: Descriptor<S, unknown>;
24 private readonly _service: Descriptor<S, unknown>;
25 25
26 26 private readonly _containerLifetimeManager: LifetimeContainer;
27 27
28 private _parent: ActivationContext<S> | undefined;
28 private readonly _parent: ActivationContext<S> | undefined;
29 29
30 30 /** Creates a new activation context with the specified parameters.
31 31 * @param containerLifetimeManager the container which starts the activation process
32 32 * @param services the initial service registrations
33 33 * @param name the name of the service being activated, this parameter is
34 34 * used for the debug purpose.
35 35 * @param service the service to activate, this parameter is used for the
36 36 * debug purpose.
37 37 */
38 constructor(containerLifetimeManager: LifetimeContainer, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>) {
38 constructor(containerLifetimeManager: LifetimeContainer, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>, cache = {}) {
39 39 this._name = name;
40 40 this._service = service;
41 this._cache = {};
41 this._cache = cache;
42 42 this._services = services;
43 43 this._containerLifetimeManager = containerLifetimeManager;
44 44 }
45 45
46 46 /** the name of the current resolving dependency */
47 47 getName() {
48 48 return this._name;
49 49 }
50 50
51 51 createContainerLifetime<T>() {
52 52 return this._containerLifetimeManager.createLifetime<T>();
53 53 }
54 54
55 55 /** Resolves the specified dependency in the current context
56 56 * @param name The name of the dependency being resolved
57 57 */
58 58 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
59 59 /** Resolves the specified dependency with the specified default value if
60 60 * the dependency is missing.
61 61 *
62 62 * @param name The name of the dependency being resolved
63 63 * @param def A default value to return in case of the specified dependency
64 64 * is missing.
65 65 */
66 66 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
67 67 resolve<K extends keyof S, T>(name: K, def?: T): S[K] | T | undefined {
68 68 const d = this._services[name];
69 69
70 70 if (d !== undefined) {
71 71 return this.activate(d, name.toString());
72 72 } else {
73 73 if (arguments.length > 1)
74 74 return def;
75 75 else
76 76 throw new Error(`Service ${String(name)} not found`);
77 77 }
78 78 }
79 79
80 80 /**
81 81 * registers services local to the the activation context
82 82 *
83 83 * @name{string} the name of the service
84 84 * @service{string} the service descriptor to register
85 85 */
86 86 register<K extends ConfigurableKeys<S>>(name: K, service: RegistrationMap<S>[K]) {
87 87 argumentNotNull(name, "name");
88 88
89 89 this._services[name] = service;
90 90 }
91 91
92 92 createLifetime<T>(): ILifetime<T> {
93 93 const id = nextId++;
94 94 return {
95 95 initialize() {},
96 96 has: () => id in this._cache,
97 97 get: () => this._cache[id] as T,
98 98 store: item => {
99 99 this._cache[id] = item;
100 100 }
101 101 };
102 102 }
103 103
104 104 activate<T>(d: Descriptor<S, T>, name: string) {
105 105 // TODO: add logging
106 106 // if (trace.isLogEnabled())
107 107 // trace.log("enter {0} {1}", name, d);
108 108
109 109 const ctx = this.enter(d, name);
110 110 const v = d.activate(ctx);
111 111
112 112 // if (trace.isLogEnabled())
113 113 // trace.log(`leave ${name}`);
114 114
115 115 return v;
116 116 }
117 117
118 118 getStack(): ActivationContextInfo[] {
119 119 const stack = [{
120 120 name: this._name,
121 121 service: this._service.toString()
122 122 }];
123 123
124 124 return this._parent ?
125 125 stack.concat(this._parent.getStack()) :
126 126 stack;
127 127 }
128 128
129 private enter(service: Descriptor<S, unknown>, name: string): this {
130 const clone = Object.create(this) as this;
131 clone._name = name;
132 clone._services = Object.create(this._services) as typeof this._services;
133 clone._parent = this;
134 clone._service = service;
135 return clone;
136 }
137
138 /** Creates a clone for the current context, used to protect it from modifications */
139 clone(): this {
140 const clone = Object.create(this) as this;
141 clone._services = Object.create(this._services) as typeof this._services;
142 return clone;
129 private enter(service: Descriptor<S, unknown>, name: string) {
130 return new ActivationContext(
131 this._containerLifetimeManager,
132 Object.create(this._services) as typeof this._services,
133 name,
134 service,
135 this._cache
136 );
143 137 }
144 138 }
General Comments 0
You need to be logged in to leave comments. Login now