mm_dict 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -2
- package/index.js +25 -0
- package/lib/dict.js +22 -0
- package/package.json +25 -4
- package/test/dict.test.js +181 -0
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 字典管理器(mm_dict)
|
|
2
|
+
## 说明
|
|
3
|
+
mm_dict 是字典存储模块,用于存储和查询键值映射,键和值都只能是字符串。
|
|
4
|
+
例如:val → value,生命值 → hp,HP → hp。
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
## 功能
|
|
7
|
+
- 键名称为非空字符串,值只能为字符串
|
|
8
|
+
- 支持覆盖已有值、删除、判断存在、获取副本、清空
|
|
9
|
+
- 支持保存到 JSON 文件与从 JSON 文件加载(加载时校验值类型)
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
```bash
|
|
13
|
+
npm install mm_dict
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 快速开始
|
|
17
|
+
```javascript
|
|
18
|
+
const { Dict, dictAdmin } = require('mm_dict');
|
|
19
|
+
|
|
20
|
+
// 直接使用存储类
|
|
21
|
+
const dict = new Dict('./data/dict.json');
|
|
22
|
+
dict.set('val', 'value').set('生命值', 'hp');
|
|
23
|
+
dict.save();
|
|
24
|
+
|
|
25
|
+
// 通过管理器按作用域获取实例(相同作用域返回同一实例)
|
|
26
|
+
const scoped = dictAdmin('app', './data/app_dict.json');
|
|
27
|
+
scoped.set('HP', 'hp');
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API
|
|
31
|
+
| 方法 | 说明 | 参数 | 返回值 |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| set | 设置字典项(字符串) | key, value | Dict |
|
|
34
|
+
| get | 获取字典项 | key | string\|undefined |
|
|
35
|
+
| del | 删除字典项 | key | boolean |
|
|
36
|
+
| has | 判断字典项是否存在 | key | boolean |
|
|
37
|
+
| list | 获取字典表副本 | - | object |
|
|
38
|
+
| clear | 清空所有字典项 | - | Dict |
|
|
39
|
+
| save | 保存到持久化文件 | - | boolean |
|
|
40
|
+
| load | 从持久化文件加载 | - | boolean |
|
|
41
|
+
|
|
42
|
+
## 依赖
|
|
43
|
+
- mm_corejs:提供 Admin 管理器
|
|
44
|
+
|
|
45
|
+
## 许可证
|
|
46
|
+
ISC
|
package/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const Admin = require('mm_corejs').Admin;
|
|
2
|
+
const Dict = require('./lib/dict.js');
|
|
3
|
+
|
|
4
|
+
let admin = new Admin();
|
|
5
|
+
admin.setClass(Dict);
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
Dict,
|
|
9
|
+
admin,
|
|
10
|
+
/**
|
|
11
|
+
* 获取指定作用域的字典存储实例,不存在时创建
|
|
12
|
+
* @param {string} [scope] 作用域名称,缺省时使用 os
|
|
13
|
+
* @param {string} [config] 持久化文件路径
|
|
14
|
+
* @returns {Dict} 字典存储实例
|
|
15
|
+
*/
|
|
16
|
+
dictAdmin(scope, config) {
|
|
17
|
+
if (!scope) {
|
|
18
|
+
scope = 'os';
|
|
19
|
+
}
|
|
20
|
+
return admin.get(scope, config);
|
|
21
|
+
},
|
|
22
|
+
getDict() {
|
|
23
|
+
return admin.get('os');
|
|
24
|
+
}
|
|
25
|
+
};
|
package/lib/dict.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const KvStore = require('mm_corejs').KvStore;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 字典存储类,值只能是字符串
|
|
5
|
+
* @class
|
|
6
|
+
* @augments {KvStore}
|
|
7
|
+
*/
|
|
8
|
+
class Dict extends KvStore {
|
|
9
|
+
/**
|
|
10
|
+
* 校验值是否为字符串
|
|
11
|
+
* @param {*} value 值
|
|
12
|
+
* @returns {void}
|
|
13
|
+
* @throws {TypeError} 值不是字符串时抛出
|
|
14
|
+
*/
|
|
15
|
+
_checkValue(value) {
|
|
16
|
+
if (typeof value !== 'string') {
|
|
17
|
+
throw new TypeError('dict 的值必须是字符串');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = Dict;
|
package/package.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_dict",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "字典存储模块:键和值都只能是字符串,用于键值映射",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mm_mod",
|
|
8
|
+
"mm_infra",
|
|
9
|
+
"mm_data",
|
|
10
|
+
"dict",
|
|
11
|
+
"字典",
|
|
12
|
+
"存储"
|
|
13
|
+
],
|
|
14
|
+
"layer": "infra",
|
|
15
|
+
"group": "data",
|
|
16
|
+
"author": "邱文武",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "jest --coverage"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"mm_corejs": "^1.1.4"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"jest": "^30.5.2"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const mod = require('../index');
|
|
7
|
+
const Dict = require('../lib/dict');
|
|
8
|
+
|
|
9
|
+
describe('mm_dict 模块入口', () => {
|
|
10
|
+
it('导出 Dict 类与管理器', () => {
|
|
11
|
+
expect(mod.Dict).toBe(Dict);
|
|
12
|
+
expect(mod.admin).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('dictAdmin 按作用域缓存并返回实例', () => {
|
|
16
|
+
const first = mod.dictAdmin('dict_scope');
|
|
17
|
+
expect(first).toBeInstanceOf(Dict);
|
|
18
|
+
expect(mod.dictAdmin('dict_scope')).toBe(first);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('dictAdmin 缺省作用域为 os', () => {
|
|
22
|
+
expect(mod.dictAdmin()).toBeInstanceOf(Dict);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('Dict', () => {
|
|
27
|
+
let tmp_dir;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
tmp_dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mm-dict-'));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
fs.rmSync(tmp_dir, { recursive: true, force: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('构造', () => {
|
|
38
|
+
it('默认不传文件路径时 file 为空字符串', () => {
|
|
39
|
+
const dict = new Dict();
|
|
40
|
+
expect(dict._file).toBe('');
|
|
41
|
+
expect(dict.list()).toEqual({});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('传入文件路径时保存路径', () => {
|
|
45
|
+
const file = path.join(tmp_dir, 'dict.json');
|
|
46
|
+
const dict = new Dict(file);
|
|
47
|
+
expect(dict._file).toBe(file);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('set / get', () => {
|
|
52
|
+
it('set 字符串后可通过 get 读取,并支持链式调用', () => {
|
|
53
|
+
const dict = new Dict();
|
|
54
|
+
expect(dict.set('val', 'value')).toBe(dict);
|
|
55
|
+
expect(dict.get('val')).toBe('value');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('set 可覆盖已有值', () => {
|
|
59
|
+
const dict = new Dict();
|
|
60
|
+
dict.set('val', 'value');
|
|
61
|
+
dict.set('val', 'value2');
|
|
62
|
+
expect(dict.get('val')).toBe('value2');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('set 非字符串值抛 TypeError', () => {
|
|
66
|
+
const dict = new Dict();
|
|
67
|
+
expect(() => dict.set('val', 123)).toThrow('dict 的值必须是字符串');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('get 不存在的键返回 undefined', () => {
|
|
71
|
+
const dict = new Dict();
|
|
72
|
+
expect(dict.get('nope')).toBeUndefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('set 非字符串键抛 TypeError', () => {
|
|
76
|
+
const dict = new Dict();
|
|
77
|
+
expect(() => dict.set(123, 'x')).toThrow('键名必须是非空字符串');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('set 空字符串键抛 TypeError', () => {
|
|
81
|
+
const dict = new Dict();
|
|
82
|
+
expect(() => dict.set('', 'x')).toThrow('键名必须是非空字符串');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('get 非字符串键抛 TypeError', () => {
|
|
86
|
+
const dict = new Dict();
|
|
87
|
+
expect(() => dict.get(null)).toThrow('键名必须是非空字符串');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('del / has', () => {
|
|
92
|
+
it('del 删除存在的键返回 true', () => {
|
|
93
|
+
const dict = new Dict();
|
|
94
|
+
dict.set('val', 'value');
|
|
95
|
+
expect(dict.del('val')).toBe(true);
|
|
96
|
+
expect(dict.has('val')).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('del 不存在的键返回 false', () => {
|
|
100
|
+
const dict = new Dict();
|
|
101
|
+
expect(dict.del('nope')).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('del 非字符串键抛 TypeError', () => {
|
|
105
|
+
const dict = new Dict();
|
|
106
|
+
expect(() => dict.del('')).toThrow('键名必须是非空字符串');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('has 存在返回 true,不存在返回 false', () => {
|
|
110
|
+
const dict = new Dict();
|
|
111
|
+
dict.set('val', 'value');
|
|
112
|
+
expect(dict.has('val')).toBe(true);
|
|
113
|
+
expect(dict.has('other')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('has 非字符串键抛 TypeError', () => {
|
|
117
|
+
const dict = new Dict();
|
|
118
|
+
expect(() => dict.has(undefined)).toThrow('键名必须是非空字符串');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('list / clear', () => {
|
|
123
|
+
it('list 返回副本,不引用内部对象', () => {
|
|
124
|
+
const dict = new Dict();
|
|
125
|
+
dict.set('val', 'value');
|
|
126
|
+
const list = dict.list();
|
|
127
|
+
expect(list).toEqual({ val: 'value' });
|
|
128
|
+
list.val = 'changed';
|
|
129
|
+
expect(dict.get('val')).toBe('value');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('clear 清空所有字典项', () => {
|
|
133
|
+
const dict = new Dict();
|
|
134
|
+
dict.set('a', 'x');
|
|
135
|
+
dict.set('b', 'y');
|
|
136
|
+
expect(dict.clear()).toBe(dict);
|
|
137
|
+
expect(dict.list()).toEqual({});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('save / load', () => {
|
|
142
|
+
it('未配置文件路径时 save 抛 Error', () => {
|
|
143
|
+
const dict = new Dict();
|
|
144
|
+
expect(() => dict.save()).toThrow('未配置持久化文件路径');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('save 写入文件返回 true', () => {
|
|
148
|
+
const file = path.join(tmp_dir, 'dict.json');
|
|
149
|
+
const dict = new Dict(file);
|
|
150
|
+
dict.set('val', 'value');
|
|
151
|
+
expect(dict.save()).toBe(true);
|
|
152
|
+
expect(JSON.parse(fs.readFileSync(file, 'utf8'))).toEqual({ val: 'value' });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('未配置文件路径时 load 返回 false', () => {
|
|
156
|
+
const dict = new Dict();
|
|
157
|
+
expect(dict.load()).toBe(false);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('文件不存在时 load 返回 false', () => {
|
|
161
|
+
const dict = new Dict(path.join(tmp_dir, 'nope.json'));
|
|
162
|
+
expect(dict.load()).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('文件存在时 load 加载数据返回 true', () => {
|
|
166
|
+
const file = path.join(tmp_dir, 'dict.json');
|
|
167
|
+
fs.writeFileSync(file, JSON.stringify({ val: 'value', key: 'k' }));
|
|
168
|
+
const dict = new Dict(file);
|
|
169
|
+
expect(dict.load()).toBe(true);
|
|
170
|
+
expect(dict.get('val')).toBe('value');
|
|
171
|
+
expect(dict.get('key')).toBe('k');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('文件含非字符串值时 load 抛 TypeError', () => {
|
|
175
|
+
const file = path.join(tmp_dir, 'dict.json');
|
|
176
|
+
fs.writeFileSync(file, JSON.stringify({ val: 123 }));
|
|
177
|
+
const dict = new Dict(file);
|
|
178
|
+
expect(() => dict.load()).toThrow('dict 的值必须是字符串');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|