@jdeighan/coffee-utils 12.0.1 → 13.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jdeighan/coffee-utils",
3
3
  "type": "module",
4
- "version": "12.0.1",
4
+ "version": "13.0.0",
5
5
  "description": "A set of utility functions for CoffeeScript",
6
6
  "main": "coffee_utils.js",
7
7
  "exports": {
@@ -22,6 +22,8 @@
22
22
  "./section": "./src/Section.js",
23
23
  "./sectionmap": "./src/SectionMap.js",
24
24
  "./fsa": "./src/fsa.js",
25
+ "./giftset": "./src/GiftSet.js",
26
+ "./keyedset": "./src/KeyedSet.js",
25
27
  "./package.json": "./package.json"
26
28
  },
27
29
  "engines": {
@@ -48,14 +50,15 @@
48
50
  },
49
51
  "homepage": "https://github.com/johndeighan/coffee-utils#readme",
50
52
  "dependencies": {
51
- "@jdeighan/base-utils": "^1.0.10",
53
+ "@jdeighan/base-utils": "^2.0.0",
52
54
  "cross-env": "^7.0.3",
55
+ "immer": "^9.0.16",
53
56
  "js-yaml": "^4.1.0",
54
57
  "n-readlines": "^1.0.1",
55
58
  "readline-sync": "^1.4.10",
56
- "svelte": "^3.53.1"
59
+ "svelte": "^3.54.0"
57
60
  },
58
61
  "devDependencies": {
59
- "@jdeighan/unit-tester": "^2.0.69"
62
+ "@jdeighan/unit-tester": "^3.0.0"
60
63
  }
61
64
  }
@@ -10,6 +10,7 @@ import {
10
10
  withExt, slurp, barf, newerDestFileExists,
11
11
  } from '@jdeighan/coffee-utils/fs'
12
12
  import {fromTAML} from '@jdeighan/coffee-utils/taml'
13
+ import {createDraft, finishDraft, produce} from 'immer'
13
14
 
14
15
  # ---------------------------------------------------------------------------
15
16
 
@@ -18,8 +19,8 @@ export class WritableDataStore
18
19
  constructor: (value=undef) ->
19
20
  @store = writable value
20
21
 
21
- subscribe: (callback) ->
22
- return @store.subscribe(callback)
22
+ subscribe: (func) ->
23
+ return @store.subscribe(func)
23
24
 
24
25
  set: (value) ->
25
26
  @store.set(value)
@@ -29,6 +30,91 @@ export class WritableDataStore
29
30
 
30
31
  # ---------------------------------------------------------------------------
31
32
 
33
+ export class BaseDataStore
34
+
35
+ constructor: (@value=undef) ->
36
+ @lSubscribers = []
37
+
38
+ subscribe: (func) ->
39
+ func @value
40
+ @lSubscribers.push func
41
+ return () ->
42
+ pos = @lSubscribers.indexOf func
43
+ @lSubscribers.splice pos, 1
44
+
45
+ set: (val) ->
46
+ @value = val
47
+ @alertSubscribers()
48
+ return
49
+
50
+ update: (func) ->
51
+ @value = func(@value)
52
+ @alertSubscribers()
53
+ return
54
+
55
+ alertSubscribers: () ->
56
+ for func in @lSubscribers
57
+ func @value
58
+ return
59
+
60
+ # ---------------------------------------------------------------------------
61
+
62
+ export class ImmerDataStore extends BaseDataStore
63
+
64
+ constructor: () ->
65
+ super [] # initialize with an empty array
66
+
67
+ getNewState: () ->
68
+
69
+ return produce state, draft =>
70
+ @addGift draft, description, image
71
+ return
72
+
73
+ addGift: (draft, description, image) ->
74
+ draft.push {
75
+ id: 1
76
+ description
77
+ image
78
+ }
79
+
80
+ # ---------------------------------------------------------------------------
81
+
82
+ export class ToDoDataStore
83
+ # --- implemented with immer
84
+
85
+ constructor: () ->
86
+ @lToDos = []
87
+ @lSubscribers = []
88
+
89
+ subscribe: (func) ->
90
+ func(@lToDos)
91
+ @lSubscribers.push func
92
+ return () ->
93
+ index = @lSubscribers.indexOf func
94
+ @lSubscribers.splice index, 1
95
+
96
+ alertSubscribers: () ->
97
+ for func in @lSubscribers
98
+ func(@lToDos)
99
+ return
100
+
101
+ set: (value) ->
102
+ # --- Set new value
103
+ @alertSubscribers()
104
+
105
+ update: (func) ->
106
+ # --- Update value
107
+ @alertSubscribers()
108
+
109
+ add: (name) ->
110
+ @lToDos.push {
111
+ text: name
112
+ done: false
113
+ }
114
+ return
115
+
116
+ # ---------------------------------------------------------------------------
117
+
32
118
  export class LocalStorageDataStore extends WritableDataStore
33
119
 
34
120
  constructor: (@masterKey, defValue=undef) ->
package/src/DataStores.js CHANGED
@@ -33,14 +33,20 @@ import {
33
33
  fromTAML
34
34
  } from '@jdeighan/coffee-utils/taml';
35
35
 
36
+ import {
37
+ createDraft,
38
+ finishDraft,
39
+ produce
40
+ } from 'immer';
41
+
36
42
  // ---------------------------------------------------------------------------
37
43
  export var WritableDataStore = class WritableDataStore {
38
44
  constructor(value = undef) {
39
45
  this.store = writable(value);
40
46
  }
41
47
 
42
- subscribe(callback) {
43
- return this.store.subscribe(callback);
48
+ subscribe(func) {
49
+ return this.store.subscribe(func);
44
50
  }
45
51
 
46
52
  set(value) {
@@ -53,6 +59,112 @@ export var WritableDataStore = class WritableDataStore {
53
59
 
54
60
  };
55
61
 
62
+ // ---------------------------------------------------------------------------
63
+ export var BaseDataStore = class BaseDataStore {
64
+ constructor(value1 = undef) {
65
+ this.value = value1;
66
+ this.lSubscribers = [];
67
+ }
68
+
69
+ subscribe(func) {
70
+ func(this.value);
71
+ this.lSubscribers.push(func);
72
+ return function() {
73
+ var pos;
74
+ pos = this.lSubscribers.indexOf(func);
75
+ return this.lSubscribers.splice(pos, 1);
76
+ };
77
+ }
78
+
79
+ set(val) {
80
+ this.value = val;
81
+ this.alertSubscribers();
82
+ }
83
+
84
+ update(func) {
85
+ this.value = func(this.value);
86
+ this.alertSubscribers();
87
+ }
88
+
89
+ alertSubscribers() {
90
+ var func, i, len, ref;
91
+ ref = this.lSubscribers;
92
+ for (i = 0, len = ref.length; i < len; i++) {
93
+ func = ref[i];
94
+ func(this.value);
95
+ }
96
+ }
97
+
98
+ };
99
+
100
+ // ---------------------------------------------------------------------------
101
+ export var ImmerDataStore = class ImmerDataStore extends BaseDataStore {
102
+ constructor() {
103
+ super([]); // initialize with an empty array
104
+ }
105
+
106
+ getNewState() {
107
+ return produce(state, draft(() => {
108
+ return this.addGift(draft, description, image);
109
+ }));
110
+ }
111
+
112
+ addGift(draft, description, image) {
113
+ return draft.push({
114
+ id: 1,
115
+ description,
116
+ image
117
+ });
118
+ }
119
+
120
+ };
121
+
122
+ // ---------------------------------------------------------------------------
123
+ export var ToDoDataStore = class ToDoDataStore {
124
+ // --- implemented with immer
125
+ constructor() {
126
+ this.lToDos = [];
127
+ this.lSubscribers = [];
128
+ }
129
+
130
+ subscribe(func) {
131
+ func(this.lToDos);
132
+ this.lSubscribers.push(func);
133
+ return function() {
134
+ var index;
135
+ index = this.lSubscribers.indexOf(func);
136
+ return this.lSubscribers.splice(index, 1);
137
+ };
138
+ }
139
+
140
+ alertSubscribers() {
141
+ var func, i, len, ref;
142
+ ref = this.lSubscribers;
143
+ for (i = 0, len = ref.length; i < len; i++) {
144
+ func = ref[i];
145
+ func(this.lToDos);
146
+ }
147
+ }
148
+
149
+ set(value) {
150
+ // --- Set new value
151
+ return this.alertSubscribers();
152
+ }
153
+
154
+ update(func) {
155
+ // --- Update value
156
+ return this.alertSubscribers();
157
+ }
158
+
159
+ add(name) {
160
+ this.lToDos.push({
161
+ text: name,
162
+ done: false
163
+ });
164
+ }
165
+
166
+ };
167
+
56
168
  // ---------------------------------------------------------------------------
57
169
  export var LocalStorageDataStore = class LocalStorageDataStore extends WritableDataStore {
58
170
  constructor(masterKey1, defValue = undef) {
@@ -0,0 +1,61 @@
1
+ # GiftSet.coffee
2
+
3
+ import {produce, enableMapSet} from 'immer'
4
+ import {assert, croak, LOG, LOGVALUE} from '@jdeighan/base-utils'
5
+ import {
6
+ dbgEnter, dbgReturn, dbg,
7
+ } from '@jdeighan/base-utils/debug'
8
+ import {
9
+ undef, defined, notdefined, OL,
10
+ isString, isNonEmptyString, isArray, isHash, isArrayOfStrings,
11
+ isEmpty, nonEmpty,
12
+ } from '@jdeighan/coffee-utils'
13
+
14
+ enableMapSet()
15
+
16
+ # ---------------------------------------------------------------------------
17
+
18
+ export LOGMAP = (label, map) ->
19
+
20
+ lLines = ["MAP #{label}:"]
21
+ for [key, value] from map.entries()
22
+ lLines.push " #{OL(key)}: #{OL(value)}"
23
+ LOG lLines.join("\n")
24
+ LOG()
25
+ return
26
+
27
+ # ---------------------------------------------------------------------------
28
+
29
+ export addGift = produce (draft, giftName, hData={}) ->
30
+
31
+ assert (draft instanceof Map), "draft is not a Map"
32
+ if draft.get giftName
33
+ throw new Error("Gift #{giftName} already exists")
34
+ hValue = {hData...}
35
+ hValue.name = giftName
36
+ draft.set giftName, hValue
37
+ return
38
+
39
+ # ---------------------------------------------------------------------------
40
+
41
+ export reserveGift = produce (draft, giftName, user) ->
42
+
43
+ assert (draft instanceof Map), "draft is not a Map"
44
+ gift = draft.get giftName
45
+ assert gift?, "No such gift: #{giftName}"
46
+ gift.reservedBy = user
47
+ return
48
+
49
+ # ---------------------------------------------------------------------------
50
+
51
+ export cancelReservation = produce (draft, giftName) ->
52
+
53
+ assert (draft instanceof Map), "draft is not a Map"
54
+ gift = draft.get giftName
55
+ assert defined(gift), "No such gift: #{giftName}"
56
+ delete gift.reservedBy
57
+ return
58
+
59
+ # ---------------------------------------------------------------------------
60
+
61
+
package/src/GiftSet.js ADDED
@@ -0,0 +1,80 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // GiftSet.coffee
3
+ import {
4
+ produce,
5
+ enableMapSet
6
+ } from 'immer';
7
+
8
+ import {
9
+ assert,
10
+ croak,
11
+ LOG,
12
+ LOGVALUE
13
+ } from '@jdeighan/base-utils';
14
+
15
+ import {
16
+ dbgEnter,
17
+ dbgReturn,
18
+ dbg
19
+ } from '@jdeighan/base-utils/debug';
20
+
21
+ import {
22
+ undef,
23
+ defined,
24
+ notdefined,
25
+ OL,
26
+ isString,
27
+ isNonEmptyString,
28
+ isArray,
29
+ isHash,
30
+ isArrayOfStrings,
31
+ isEmpty,
32
+ nonEmpty
33
+ } from '@jdeighan/coffee-utils';
34
+
35
+ enableMapSet();
36
+
37
+ // ---------------------------------------------------------------------------
38
+ export var LOGMAP = function(label, map) {
39
+ var key, lLines, ref, value, x;
40
+ lLines = [`MAP ${label}:`];
41
+ ref = map.entries();
42
+ for (x of ref) {
43
+ [key, value] = x;
44
+ lLines.push(` ${OL(key)}: ${OL(value)}`);
45
+ }
46
+ LOG(lLines.join("\n"));
47
+ LOG();
48
+ };
49
+
50
+ // ---------------------------------------------------------------------------
51
+ export var addGift = produce(function(draft, giftName, hData = {}) {
52
+ var hValue;
53
+ assert(draft instanceof Map, "draft is not a Map");
54
+ if (draft.get(giftName)) {
55
+ throw new Error(`Gift ${giftName} already exists`);
56
+ }
57
+ hValue = {...hData};
58
+ hValue.name = giftName;
59
+ draft.set(giftName, hValue);
60
+ });
61
+
62
+ // ---------------------------------------------------------------------------
63
+ export var reserveGift = produce(function(draft, giftName, user) {
64
+ var gift;
65
+ assert(draft instanceof Map, "draft is not a Map");
66
+ gift = draft.get(giftName);
67
+ assert(gift != null, `No such gift: ${giftName}`);
68
+ gift.reservedBy = user;
69
+ });
70
+
71
+ // ---------------------------------------------------------------------------
72
+ export var cancelReservation = produce(function(draft, giftName) {
73
+ var gift;
74
+ assert(draft instanceof Map, "draft is not a Map");
75
+ gift = draft.get(giftName);
76
+ assert(defined(gift), `No such gift: ${giftName}`);
77
+ delete gift.reservedBy;
78
+ });
79
+
80
+ // ---------------------------------------------------------------------------
@@ -0,0 +1,153 @@
1
+ # KeyedSet.coffee
2
+
3
+ import {assert, croak, LOG, LOGVALUE} from '@jdeighan/base-utils'
4
+ import {
5
+ dbgEnter, dbgReturn, dbg,
6
+ } from '@jdeighan/base-utils/debug'
7
+ import {
8
+ undef, defined, notdefined, OL, deepCopy,
9
+ isString, isNonEmptyString, isArray, isHash, isArrayOfStrings,
10
+ isEmpty, nonEmpty,
11
+ } from '@jdeighan/coffee-utils'
12
+
13
+ # ---------------------------------------------------------------------------
14
+
15
+ export class KeyedSet extends Map
16
+
17
+ constructor: (@setName, lKeyNames, @sep='|') ->
18
+ # --- lKeyNames can be:
19
+ # 1. a non-empty string
20
+ # 2. an array of non-empty strings
21
+
22
+ dbgEnter 'KeyedSet'
23
+ super()
24
+ assert isNonEmptyString(@setName), "bad set name: #{OL(@setName)}"
25
+ if isString(lKeyNames)
26
+ assert nonEmpty(lKeyNames), "empty string key name"
27
+ @lKeyNames = [lKeyNames]
28
+ @numKeys = 1
29
+ else if isArray(lKeyNames)
30
+ assert nonEmpty(lKeyNames), "empty key name array"
31
+ for name in lKeyNames
32
+ assert isNonEmptyString(name),
33
+ "name not a non-empty string: #{OL(name)}"
34
+ @numKeys = lKeyNames.length
35
+ @lKeyNames = lKeyNames
36
+ else
37
+ croak "Invalid key names: #{OL(lKeyNames)}"
38
+ dbg "key is #{OL(@lKeyNames)}"
39
+ dbgReturn 'KeyedSet'
40
+
41
+ # ..........................................................
42
+
43
+ add: (keyVal, hData={}) ->
44
+
45
+ dbgEnter 'add', keyVal, hData
46
+ assert ! @has(keyVal), "adding duplicate key #{OL(keyVal)}"
47
+ assert isHash(hData), "hData not a hash: #{OL(hData)}"
48
+ dbg "not a duplicate"
49
+
50
+ hItem = deepCopy hData
51
+
52
+ # --- Add key values to hItem
53
+ lKeyVals = @getKeyValues keyVal
54
+ for name,i in @lKeyNames
55
+ assert notdefined(hItem[name]),
56
+ "hData has key #{name}"
57
+ hItem[name] = lKeyVals[i]
58
+
59
+ key = @getKey(keyVal)
60
+ dbg 'key', key
61
+ dbg 'value', hItem
62
+ @set key, hItem # set() is a method in Map, the base class
63
+ @length = @size # add to all methods that change size
64
+ dbgReturn 'add'
65
+ return this # allow chaining
66
+
67
+ # ..........................................................
68
+
69
+ getKey: (keyVal) ->
70
+ # --- Get the actual key used in the underlying Map object
71
+
72
+ dbgEnter 'getKey'
73
+ key = @getKeyValues(keyVal).join(@sep)
74
+ dbgReturn 'getKey', key
75
+ return key
76
+
77
+ # ..........................................................
78
+
79
+ getKeyValues: (keyVal) ->
80
+ # --- Accepts either a string or an array of strings
81
+ # But all keys must be non-empty strings
82
+ # Always returns an array
83
+
84
+ dbgEnter 'getKeyValues', keyVal
85
+ if isString(keyVal)
86
+ lKeyVals = [keyVal]
87
+ else if isArray(keyVal)
88
+ lKeyVals = keyVal
89
+ else
90
+ croak "Bad key value: #{OL(keyVal)}"
91
+ assert (lKeyVals.length == @numKeys), "Bad # keys in #{OL(keyVal)}"
92
+ for val in lKeyVals
93
+ assert isNonEmptyString(val), "Bad key val: #{OL(val)}"
94
+ dbgReturn 'getKeyValues', lKeyVals
95
+ return lKeyVals
96
+
97
+ # ..........................................................
98
+
99
+ has: (keyVal) ->
100
+
101
+ return super @getKey(keyVal)
102
+
103
+ # ..........................................................
104
+
105
+ update: (keyVal, hData={}) ->
106
+
107
+ dbgEnter 'update', keyVal, hData
108
+ key = @getKey(keyVal)
109
+ dbg "key = #{OL(key)}"
110
+ hItem = @get(key)
111
+ dbg 'hItem', hItem
112
+ assert defined(hItem), "updating missing key #{OL(keyVal)}"
113
+ for key,val of hData
114
+ hItem[key] = val
115
+ dbgReturn 'update'
116
+ return this # allow chaining
117
+
118
+ # ..........................................................
119
+
120
+ remove: (keyVal) ->
121
+
122
+ key = @getKey(keyVal)
123
+ if ! @delete key
124
+ croak "No key #{OL(keyVal)} in #{@setName}"
125
+ @length = @size # add to all methods that change size
126
+ return this # allow chaining
127
+
128
+ # ..........................................................
129
+
130
+ getAllItems: () ->
131
+ # --- Useful for unit tests, but it's usually better
132
+ # to use a generator like .entries()
133
+
134
+ return Array.from(@values())
135
+
136
+ # ..........................................................
137
+
138
+ get: (keyVal) ->
139
+ # --- Override to require that it exists
140
+
141
+ item = super @getKey(keyVal)
142
+ assert defined(item), "No such item: #{OL(keyVal)} in #{@setName}"
143
+ return item
144
+
145
+ # ..........................................................
146
+
147
+ dump: () ->
148
+
149
+ console.log "DUMP #{@setName}:"
150
+ for [key, value] from @entries()
151
+ console.log "#{OL(key)}: #{OL(value)}"
152
+
153
+ # ---------------------------------------------------------------------------
@@ -0,0 +1,187 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ // KeyedSet.coffee
3
+ import {
4
+ assert,
5
+ croak,
6
+ LOG,
7
+ LOGVALUE
8
+ } from '@jdeighan/base-utils';
9
+
10
+ import {
11
+ dbgEnter,
12
+ dbgReturn,
13
+ dbg
14
+ } from '@jdeighan/base-utils/debug';
15
+
16
+ import {
17
+ undef,
18
+ defined,
19
+ notdefined,
20
+ OL,
21
+ deepCopy,
22
+ isString,
23
+ isNonEmptyString,
24
+ isArray,
25
+ isHash,
26
+ isArrayOfStrings,
27
+ isEmpty,
28
+ nonEmpty
29
+ } from '@jdeighan/coffee-utils';
30
+
31
+ // ---------------------------------------------------------------------------
32
+ export var KeyedSet = class KeyedSet extends Map {
33
+ constructor(setName, lKeyNames, sep = '|') {
34
+ var j, len, name;
35
+ // --- lKeyNames can be:
36
+ // 1. a non-empty string
37
+ // 2. an array of non-empty strings
38
+ dbgEnter('KeyedSet');
39
+ super();
40
+ this.setName = setName;
41
+ this.sep = sep;
42
+ assert(isNonEmptyString(this.setName), `bad set name: ${OL(this.setName)}`);
43
+ if (isString(lKeyNames)) {
44
+ assert(nonEmpty(lKeyNames), "empty string key name");
45
+ this.lKeyNames = [lKeyNames];
46
+ this.numKeys = 1;
47
+ } else if (isArray(lKeyNames)) {
48
+ assert(nonEmpty(lKeyNames), "empty key name array");
49
+ for (j = 0, len = lKeyNames.length; j < len; j++) {
50
+ name = lKeyNames[j];
51
+ assert(isNonEmptyString(name), `name not a non-empty string: ${OL(name)}`);
52
+ }
53
+ this.numKeys = lKeyNames.length;
54
+ this.lKeyNames = lKeyNames;
55
+ } else {
56
+ croak(`Invalid key names: ${OL(lKeyNames)}`);
57
+ }
58
+ dbg(`key is ${OL(this.lKeyNames)}`);
59
+ dbgReturn('KeyedSet');
60
+ }
61
+
62
+ // ..........................................................
63
+ add(keyVal, hData = {}) {
64
+ var hItem, i, j, key, lKeyVals, len, name, ref;
65
+ dbgEnter('add', keyVal, hData);
66
+ assert(!this.has(keyVal), `adding duplicate key ${OL(keyVal)}`);
67
+ assert(isHash(hData), `hData not a hash: ${OL(hData)}`);
68
+ dbg("not a duplicate");
69
+ hItem = deepCopy(hData);
70
+ // --- Add key values to hItem
71
+ lKeyVals = this.getKeyValues(keyVal);
72
+ ref = this.lKeyNames;
73
+ for (i = j = 0, len = ref.length; j < len; i = ++j) {
74
+ name = ref[i];
75
+ assert(notdefined(hItem[name]), `hData has key ${name}`);
76
+ hItem[name] = lKeyVals[i];
77
+ }
78
+ key = this.getKey(keyVal);
79
+ dbg('key', key);
80
+ dbg('value', hItem);
81
+ this.set(key, hItem); // set() is a method in Map, the base class
82
+ this.length = this.size; // add to all methods that change size
83
+ dbgReturn('add');
84
+ return this; // allow chaining
85
+ }
86
+
87
+
88
+ // ..........................................................
89
+ getKey(keyVal) {
90
+ var key;
91
+ // --- Get the actual key used in the underlying Map object
92
+ dbgEnter('getKey');
93
+ key = this.getKeyValues(keyVal).join(this.sep);
94
+ dbgReturn('getKey', key);
95
+ return key;
96
+ }
97
+
98
+ // ..........................................................
99
+ getKeyValues(keyVal) {
100
+ var j, lKeyVals, len, val;
101
+ // --- Accepts either a string or an array of strings
102
+ // But all keys must be non-empty strings
103
+ // Always returns an array
104
+ dbgEnter('getKeyValues', keyVal);
105
+ if (isString(keyVal)) {
106
+ lKeyVals = [keyVal];
107
+ } else if (isArray(keyVal)) {
108
+ lKeyVals = keyVal;
109
+ } else {
110
+ croak(`Bad key value: ${OL(keyVal)}`);
111
+ }
112
+ assert(lKeyVals.length === this.numKeys, `Bad # keys in ${OL(keyVal)}`);
113
+ for (j = 0, len = lKeyVals.length; j < len; j++) {
114
+ val = lKeyVals[j];
115
+ assert(isNonEmptyString(val), `Bad key val: ${OL(val)}`);
116
+ }
117
+ dbgReturn('getKeyValues', lKeyVals);
118
+ return lKeyVals;
119
+ }
120
+
121
+ // ..........................................................
122
+ has(keyVal) {
123
+ return super.has(this.getKey(keyVal));
124
+ }
125
+
126
+ // ..........................................................
127
+ update(keyVal, hData = {}) {
128
+ var hItem, key, val;
129
+ dbgEnter('update', keyVal, hData);
130
+ key = this.getKey(keyVal);
131
+ dbg(`key = ${OL(key)}`);
132
+ hItem = this.get(key);
133
+ dbg('hItem', hItem);
134
+ assert(defined(hItem), `updating missing key ${OL(keyVal)}`);
135
+ for (key in hData) {
136
+ val = hData[key];
137
+ hItem[key] = val;
138
+ }
139
+ dbgReturn('update');
140
+ return this; // allow chaining
141
+ }
142
+
143
+
144
+ // ..........................................................
145
+ remove(keyVal) {
146
+ var key;
147
+ key = this.getKey(keyVal);
148
+ if (!this.delete(key)) {
149
+ croak(`No key ${OL(keyVal)} in ${this.setName}`);
150
+ }
151
+ this.length = this.size; // add to all methods that change size
152
+ return this; // allow chaining
153
+ }
154
+
155
+
156
+ // ..........................................................
157
+ getAllItems() {
158
+ // --- Useful for unit tests, but it's usually better
159
+ // to use a generator like .entries()
160
+ return Array.from(this.values());
161
+ }
162
+
163
+ // ..........................................................
164
+ get(keyVal) {
165
+ var item;
166
+ // --- Override to require that it exists
167
+ item = super.get(this.getKey(keyVal));
168
+ assert(defined(item), `No such item: ${OL(keyVal)} in ${this.setName}`);
169
+ return item;
170
+ }
171
+
172
+ // ..........................................................
173
+ dump() {
174
+ var key, ref, results, value, x;
175
+ console.log(`DUMP ${this.setName}:`);
176
+ ref = this.entries();
177
+ results = [];
178
+ for (x of ref) {
179
+ [key, value] = x;
180
+ results.push(console.log(`${OL(key)}: ${OL(value)}`));
181
+ }
182
+ return results;
183
+ }
184
+
185
+ };
186
+
187
+ // ---------------------------------------------------------------------------