1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
odoo.define("web.Registry", function (require) {
"use strict";
const { sortBy } = require("web.utils");
/**
* The registry is really pretty much only a mapping from some keys to some
* values. The Registry class only add a few simple methods around that to make
* it nicer and slightly safer.
*
* Note that registries have a fundamental problem: the value that you try to
* get in a registry might not have been added yet, so of course, you need to
* make sure that your dependencies are solid. For this reason, it is a good
* practice to avoid using the registry if you can simply import what you need
* with the 'require' statement.
*
* However, on the flip side, sometimes you cannot just simply import something
* because we would have a dependency cycle. In that case, registries might
* help.
*/
class Registry {
/**
* @function predicate
* @param {any} value
* @returns {boolean}
*/
/**
* @param {Object} [mapping] the initial data in the registry
* @param {predicate} [predicate=(() => true)] predicate that each
* added value must pass to be registered.
*/
constructor(mapping, predicate = () => true) {
this.map = Object.create(mapping || null);
this._scoreMapping = Object.create(null);
this._sortedKeys = null;
this.listeners = []; // listening callbacks on newly added items.
this.predicate = predicate;
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Add a key (and a value) to the registry.
* Notify the listeners on newly added item in the registry.
* @param {string} key
* @param {any} value
* @param {number} [score] if given, this value will be used to order keys
* @returns {Registry} can be used to chain add calls.
*/
add(key, value, score) {
if (!this.predicate(value)) {
throw new Error(`Value of key "${key}" does not pass the addition predicate.`);
}
this._scoreMapping[key] = score === undefined ? key : score;
this._sortedKeys = null;
this.map[key] = value;
for (const callback of this.listeners) {
callback(key, value);
}
return this;
}
/**
* Check if the registry contains the key
* @param {string} key
* @returns {boolean}
*/
contains(key) {
return key in this.map;
}
/**
* Returns the content of the registry (an object mapping keys to values)
* @returns {Object}
*/
entries() {
const entries = {};
const keys = this.keys();
for (const key of keys) {
entries[key] = this.map[key];
}
return entries;
}
/**
* Returns the value associated to the given key.
* @param {string} key
* @returns {any}
*/
get(key) {
return this.map[key];
}
/**
* Tries a number of keys, and returns the first object matching one of
* the keys.
* @param {string[]} keys a sequence of keys to fetch the object for
* @returns {any} the first result found matching an object
*/
getAny(keys) {
for (const key of keys) {
if (key in this.map) {
return this.map[key];
}
}
return null;
}
/**
* Return the list of keys in map object.
*
* The registry guarantees that the keys have a consistent order, defined by
* the 'score' value when the item has been added.
* @returns {string[]}
*/
keys() {
if (!this._sortedKeys) {
const keys = [];
for (const key in this.map) {
keys.push(key);
}
this._sortedKeys = sortBy(keys,
key => this._scoreMapping[key] || 0
);
}
return this._sortedKeys;
}
/**
* @function onAddCallback
* @param {string} key
* @param {any} value
*/
/**
* Register a callback to execute when items are added to the registry.
* @param {onAddCallback} callback function with parameters (key, value).
*/
onAdd(callback) {
this.listeners.push(callback);
}
/**
* Return the list of values in map object
* @returns {string[]}
*/
values() {
return this.keys().map((key) => this.map[key]);
}
}
return Registry;
});
|