jupyter-ijavascript-utils 1.21.3 → 1.22.0

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/DOCS.md CHANGED
@@ -65,6 +65,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
65
65
 
66
66
  ## What's New
67
67
 
68
+ * 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
68
69
  * 1.21 - include {@link module:chain|chain} - simple monoid
69
70
  * 1.20 - fix vega dependency
70
71
  * 1.19 - add in {@link module:describe|describe} and {@link module:hashMap|hashMap} modules, along with {@link module:format.limitLines|format.limitLines}
@@ -94,6 +95,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
94
95
  | {@link module:aggregate} | Aggregate collections or collections of objects (ex: min, max, unique, contains, etc. |
95
96
  | {@link module:array} | Massage, sort, reshape arrays. |
96
97
  | {@link module:base64} | Convert to and from base64 encoding of strings |
98
+ | {@link module:chain} | Simple wrapper (Monad-ish) that allows for chaining statements together |
97
99
  | {@link module:datasets} | Load example <a href="https://github.com/vega/vega-datasets">datasets provided by the vega team</a> |
98
100
  | {@link module:describe} | Similar to Pandas describe, provides statistics on a set of values / objects |
99
101
  | {@link module:file} | Read and write data/text to files. |
package/Dockerfile CHANGED
@@ -1,3 +1,3 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM darkbluestudios/jupyter-ijavascript-utils:binder
3
+ FROM darkbluestudios/jupyter-ijavascript-utils:binder_1.23.0
package/README.md CHANGED
@@ -55,6 +55,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
55
55
 
56
56
  # What's New
57
57
 
58
+ * 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
58
59
  * 1.21 - include chain - simple monoid
59
60
  * 1.20 - fix vega dependency
60
61
  * 1.19 - add in describe and hashMap modules, along with format.limitLines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-ijavascript-utils",
3
- "version": "1.21.3",
3
+ "version": "1.22.0",
4
4
  "description": "Utilities for working with iJavaScript - a Jupyter Kernel",
5
5
  "homepage": "https://jupyter-ijavascript-utils.onrender.com/",
6
6
  "license": "MIT",
package/src/chain.js CHANGED
@@ -1,16 +1,369 @@
1
+ /* eslint-disable class-methods-use-this */
2
+
1
3
  /**
2
4
  * Simple monad like wrapper.
3
5
  *
6
+ * {@link module:chain|See the util.chain method} for more.
7
+ *
4
8
  * Very helpful for taking values and then progressively working on them,
5
9
  * instead of continually wrapping deeper in method calls.
6
10
  *
7
11
  * Calling `chain(3)` - gives an object with two properties:
8
12
  *
9
- * * .value - the original value passed - or 3 in this case
10
- * * .chain(function) - where it is passed the value, and returns a new Chain with that value.
11
- * * .chainMap(function) -> where it treats value as an array, and maps function on every item in the array
12
- * * .chainReduce(function, initialValue) -> where it treats value as an array, and reduces the value array
13
- * * .debug() - console.logs the value, and returns a new Chain with that value
13
+ * * {@link ChainContainer#close|.close()} - gets the value of the current chain
14
+ * * {@link ChainContainer#chain|.chain(function)} - where it is passed the value, and returns a new Chain with that value.
15
+ * * {@link ChainContainer#chainMap|.chainMap(function)} - where it treats value as an array, and maps function on every item in the array
16
+ * * {@link ChainContainer#chainReduce|.chainReduce(function, initialValue)} - where it treats value as an array, and reduces the value array
17
+ * * {@link ChainContainer#errorHandler|.errorHandler(fn)} - custom function called if an error is ever thrown
18
+ * * {@link ChainContainer#debug|.debug()} - console.logs the current value, and continues the chain with that value
19
+ */
20
+ class ChainContainer {
21
+ /**
22
+ * Custom function to run if there is any error along the chain.
23
+ * (Scoped to the current Container when running)
24
+ * @type {Function}
25
+ * @private
26
+ */
27
+ errorHandlerFn;
28
+
29
+ /**
30
+ * Value this container stores, can be anythhing.
31
+ *
32
+ * Also access through {@link ChainContainer#close}
33
+ *
34
+ * @type {any}
35
+ */
36
+ value;
37
+
38
+ /**
39
+ * Constructor that creates a new Container to pass along the chain.
40
+ *
41
+ * If the context is null, then we assume we are binding the container to the current iJavaScript cell.
42
+ *
43
+ * @param {any} value - the value to use along the chain
44
+ */
45
+ constructor(value) {
46
+ this.value = value;
47
+ }
48
+
49
+ /**
50
+ * Clones the value, and ties the output to this cell.
51
+ * @returns {ChainContainer} - new chain container to use in this cell.
52
+ * @example
53
+ *
54
+ * customErrorHandler = (err) => console.error('Some custom warning');
55
+ * initialChain = utils.chain(3)
56
+ * .errorHandler(customErrorHandler);
57
+ *
58
+ * newChain = initialChain.clone()
59
+ * .chain(v => v + 2) // 3 + 2
60
+ * .close();
61
+ *
62
+ * // 5
63
+ */
64
+ clone() {
65
+ const result = new ChainContainer(this.value);
66
+ result.errorHandlerFn = this.errorHandlerFn;
67
+ return result;
68
+ }
69
+
70
+ /**
71
+ * Creates a new chain container holding the value returned from functor(this.value)
72
+ *
73
+ * ```
74
+ * value = 2;
75
+ * plus2 = (value) => value + 2;
76
+ *
77
+ * chain(value)
78
+ * .chain(plus2) // 2 + 2
79
+ * .chain(plus2) // 4 + 2
80
+ * .debug()
81
+ * .chain(plu2) // 6 + 2
82
+ * .close();
83
+ *
84
+ * // 6
85
+ * // 8
86
+ * ```
87
+ *
88
+ * Or even combine with other utility methods
89
+ *
90
+ * ```
91
+ * badStr = 'I%20am%20the%20very%20model%20of%20a%20modern%20Major'
92
+ * + '-General%0AI\'ve%20information%20vegetable%2C%20animal%2C%20'
93
+ * + 'and%20mineral%0AI%20know%20the%20kings%20of%20England%2C%20'
94
+ * + 'and%20I%20quote%20the%20fights%0AHistorical%0AFrom%20Marath'
95
+ * + 'on%20to%20Waterloo%2C%20in%20order%20categorical';
96
+ *
97
+ * chain(badStr)
98
+ * .chain(decodeURIComponent)
99
+ * .chain(v => v.split('\n'))
100
+ * // .debug() // check the values along the way
101
+ * .chainMap(line => ({ line, length: line.length }))
102
+ * .chain(values => utils.table(values).render());
103
+ * ```
104
+ *
105
+ * and it renders out a lovely table like this:
106
+ *
107
+ * line |length
108
+ * -- |--
109
+ * I am the very model of a modern Major-General |45
110
+ * I've information vegetable, animal, and mineral |47
111
+ * I know the kings of England, and I quote the fights|51
112
+ * Historical |10
113
+ * From Marathon to Waterloo, in order categorical |47
114
+ *
115
+ * @param {Function} functor - the function given the value, returning a transformed value
116
+ * @see {@link ChainContainer#chain} - to get the value
117
+ * @see {@link ChainContainer#chainMap} - convenience to apply a function to each item in an array
118
+ * @see {@link ChainContainer#chainReduce} - convenience to reduce the array
119
+ * @see {@link ChainContainer#debug} - to see the value at a specific time
120
+ * @returns {ChainContainer} - container with the results from functor(this.value)
121
+ */
122
+ chain(functor) {
123
+ try {
124
+ return this.update(functor(this.value));
125
+ } catch (err) {
126
+ this.handleError(err);
127
+
128
+ //-- handle error throws again, line will never be called.
129
+ // this.close();
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Assuming that value is an array, this maps fn to every value in the array.
135
+ *
136
+ * ```
137
+ * initializeArray = (size) => Array.from(Array(size)).map((val, index) => index);
138
+ * initializeArray(3); // [0, 1, 2]
139
+ *
140
+ * addTwo = (value) => value + 2;
141
+ * addTwo(3); // 5
142
+ *
143
+ * utils.chain(3)
144
+ * .chain(initializeArray) // [0, 1, 2]
145
+ * .chainMap(addTwo) // [2, 3, 4] or [0 + 2, 1 + 2, 2 + 2]
146
+ * .chainMap(addTwo)
147
+ * .close();
148
+ *
149
+ * // [4, 5, 6]
150
+ * ```
151
+ *
152
+ * @param {Function} fn - applies function under every index of this.value
153
+ * @returns {ChainContainer}
154
+ */
155
+ chainMap(fn) {
156
+ if (!Array.isArray(this.value)) throw Error(`chainMap expected an array, but was passed:${this.value}`);
157
+
158
+ return this.chain((value) => value.map(fn));
159
+ }
160
+
161
+ /**
162
+ * Assuming that the value is an array, performs a reduce using fn and initialValue
163
+ *
164
+ * ```
165
+ * initializeArray = (size) => Array.from(Array(size)).map((val, index) => index);
166
+ * initializeArray(3); // [0, 1, 2]
167
+ *
168
+ * addTwo = (value) => value + 2;
169
+ * addTwo(3); // 5
170
+ *
171
+ * utils.chain(3)
172
+ * .chain(initializeArray) // [0, 1, 2]
173
+ * .chainMap(addTwo) // [2, 3, 4] or [0 + 2, 1 + 2, 2 + 2]
174
+ * .debug()
175
+ * .chainReduce((result, value) => result + value, 0)
176
+ * .close();
177
+ *
178
+ * // [2, 3, 4]
179
+ * // 9
180
+ * ```
181
+ *
182
+ * @param {Function} fn - reducer function
183
+ * @param {any} initialValue - initial value passed to the reducer
184
+ * @returns {ChainContainer}
185
+ */
186
+ chainReduce(fn, initialValue) {
187
+ if (!Array.isArray(this.value)) throw Error(`chainReduce expected an array, but was passed:${this.value}`);
188
+
189
+ return this.chain((value) => value.reduce(fn, initialValue));
190
+ }
191
+
192
+ /**
193
+ * Console.logs the value, and returns the unmodified current value.
194
+ *
195
+ * ```
196
+ * value = 2;
197
+ * plus2 = (value) => value + 2;
198
+ *
199
+ * chain(value)
200
+ * .chain(plus2) // 2 + 2
201
+ * .chain(plus2) // 4 + 2
202
+ * .debug()
203
+ * .chain(plu2) // 6 + 2
204
+ * .debug((value) => console.log(value))
205
+ * .close();
206
+ *
207
+ * // 6
208
+ * // 8
209
+ * // 8
210
+ * ```
211
+ *
212
+ * @param {Function} [fn=null] - optional custom function
213
+ * @returns {ChainContainer} - the same value as current, regardless of the result from fn.
214
+ */
215
+ debug(fn) {
216
+ if (fn) {
217
+ fn(this.value);
218
+ } else {
219
+ this.console(this.value);
220
+ }
221
+ return this;
222
+ }
223
+
224
+ /**
225
+ * Function to call if an error occurs anywhere on the chain.
226
+ *
227
+ * ```
228
+ * someErrorOccurred = false;
229
+ * flipSwitch = (err) => {
230
+ * console.log('custom handler');
231
+ * someErrorOccurred = true;
232
+ * };
233
+ * throwError = () => {
234
+ * throw Error('Custom Error');
235
+ * };
236
+ *
237
+ * chain(2)
238
+ * .errorHandler(flipSwitch)
239
+ * .chain((value) => value + 2)
240
+ * .chain(throwError);
241
+ *
242
+ * // customHandler
243
+ * // {
244
+ * // "name": "Error",
245
+ * // "message": "Custom Error"
246
+ * // }
247
+ * // /Users/proth/Documents/notebooks/jupyter-ijavascript-utils/src/chain.js:175
248
+ * // throw err;
249
+ * // ^
250
+ * //
251
+ * // Error: Custom Error
252
+ * // at throwError (evalmachine.<anonymous>:7:9)
253
+ * ```
254
+ *
255
+ * @param {Function} errorHandler - function that is passed the error caught
256
+ */
257
+ errorHandler(errorHandlerFn) {
258
+ this.errorHandlerFn = errorHandlerFn;
259
+ return this;
260
+ }
261
+
262
+ /**
263
+ * Closes the chain and returns the current value.
264
+ * @returns {any}
265
+ * @see {@link ChainContainer#chain}
266
+ * @example
267
+ *
268
+ * utils.chain(3)
269
+ * .chain(v => v + 2) // 3 + 2
270
+ * .close();
271
+ *
272
+ * // 5
273
+ */
274
+ close() {
275
+ return this.value;
276
+ }
277
+
278
+ //-- private methods
279
+
280
+ /**
281
+ * Clones the value, and ties the output to this cell.
282
+ * @param {any} newValue - new value of the clone
283
+ * @returns {ChainContainer} - new chain container to use in this cell.
284
+ * @private
285
+ */
286
+ update(newValue) {
287
+ const result = new ChainContainer(newValue);
288
+ result.errorHandlerFn = this.errorHandlerFn;
289
+ return result;
290
+ }
291
+
292
+ /**
293
+ * Default handler catching any error that happens along the chain.
294
+ * @param {Error} err - the error caught
295
+ * @private
296
+ */
297
+ handleError(err) {
298
+ if (this.errorHandlerFn) {
299
+ this.errorHandlerFn.apply(this, [err]);
300
+ }
301
+
302
+ console.error(JSON.stringify(err, ['name', 'message'], 2));
303
+
304
+ // Assert.fail('error occurred. halting.');
305
+ throw err;
306
+ }
307
+
308
+ /**
309
+ * call the console regardless of context
310
+ * @param {any} msg - message to send
311
+ * @private
312
+ */
313
+ console(msg) {
314
+ console.log(msg); // eslint-disable-line no-console
315
+ }
316
+
317
+ /**
318
+ * toString replacement
319
+ * @private
320
+ */
321
+ toString() {
322
+ const { context, ...cleanThis } = this; // eslint-disable-line
323
+ return JSON.stringify(cleanThis, 2, false);
324
+ }
325
+
326
+ /**
327
+ * inspect replacement
328
+ * @private
329
+ */
330
+ inspect() {
331
+ return this.toString();
332
+ }
333
+
334
+ /**
335
+ * toJSON replacement
336
+ * @private
337
+ */
338
+ toJSON() {
339
+ const { context, ...cleanThis } = this; // eslint-disable-line
340
+ return cleanThis;
341
+ }
342
+
343
+ /**
344
+ * toStringTag used for inspect
345
+ */
346
+ // get [Symbol.toStringTag]() {
347
+ // return 'Chain';
348
+ // }
349
+ }
350
+
351
+ /**
352
+ * Simple monad like wrapper.
353
+ *
354
+ * {@link ChainContainer|See the ChainContainer} for more.
355
+ *
356
+ * Very helpful for taking values and then progressively working on them,
357
+ * instead of continually wrapping deeper in method calls.
358
+ *
359
+ * Calling `chain(3)` - gives an object you can either use the value of, or continue the chain going.
360
+ *
361
+ * * {@link ChainContainer#close|.close()} - gets the value of the current chain
362
+ * * {@link ChainContainer#chain|.chain(function)} - where it is passed the value, and returns a new Chain with that value.
363
+ * * {@link ChainContainer#chainMap|.chainMap(function)} - where it treats value as an array, and maps function on every item in the array
364
+ * * {@link ChainContainer#chainReduce|.chainReduce(function, initialValue)} - where it treats value as an array, and reduces the value array
365
+ * * {@link ChainContainer#errorHandler|.errorHandler(fn)} - custom function called if an error is ever thrown
366
+ * * {@link ChainContainer#debug|.debug()} - console.logs the current value, and continues the chain with that value
14
367
  *
15
368
  * For example:
16
369
  *
@@ -91,26 +444,7 @@
91
444
  * @module chain
92
445
  */
93
446
  module.exports = function chain(value) {
94
- return ({
95
- value,
96
- chain: function innerChain(fn) {
97
- return chain(fn(value));
98
- },
99
- chainMap: function chainMap(fn) {
100
- if (!Array.isArray(value)) throw Error(`chainMap expected an array, but was passed:${value}`);
101
- return chain(value.map(fn));
102
- },
103
- chainReduce: function chainReduce(fn, initialValue) {
104
- if (!Array.isArray(value)) throw Error(`chainReduce expected an array, but was passed:${value}`);
105
- return chain(value.reduce(fn, initialValue));
106
- },
107
- debug: function debug(fn) {
108
- if (fn) {
109
- fn(value);
110
- } else {
111
- console.log(value);
112
- }
113
- return chain(value);
114
- }
115
- });
447
+ return new ChainContainer(value);
116
448
  };
449
+
450
+ // example gist: https://gist.github.com/paulroth3d/fc97580636ba706783c6a84467a625db