patroon 0.4.2 → 1.1.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 +72 -20
- package/README.mz +57 -9
- package/package.json +1 -1
- package/src/helpers.js +0 -5
- package/src/index.js +15 -5
- package/src/index.test.js +9 -3
- package/src/walkable.js +10 -4
- package/tape-test +1 -0
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Pattern matching in JavaScript without additional syntax.
|
|
|
27
27
|
+ [NoMatchError](#nomatcherror)
|
|
28
28
|
+ [UnevenArgumentCountError](#unevenargumentcounterror)
|
|
29
29
|
+ [PatroonError](#patroonerror)
|
|
30
|
+
- [Examples](#examples)
|
|
30
31
|
- [Tests](#tests)
|
|
31
32
|
- [Contribute](#contribute)
|
|
32
33
|
* [Contributors](#contributors)
|
|
@@ -192,8 +193,8 @@ builtin node error constructors in this example.
|
|
|
192
193
|
|
|
193
194
|
```js ./tape-test
|
|
194
195
|
patroon(
|
|
195
|
-
TypeError, 'is a type error',
|
|
196
|
-
Error, 'is an error'
|
|
196
|
+
instanceOf(TypeError), 'is a type error',
|
|
197
|
+
instanceOf(Error), 'is an error'
|
|
197
198
|
)(new Error())
|
|
198
199
|
```
|
|
199
200
|
```
|
|
@@ -213,8 +214,8 @@ Because of this you can match a TypeError with an Error.
|
|
|
213
214
|
|
|
214
215
|
```js ./tape-test
|
|
215
216
|
patroon(
|
|
216
|
-
Error, 'matches on error',
|
|
217
|
-
TypeError, 'matches on type error'
|
|
217
|
+
instanceOf(Error), 'matches on error',
|
|
218
|
+
instanceOf(TypeError), 'matches on type error'
|
|
218
219
|
)(new TypeError())
|
|
219
220
|
```
|
|
220
221
|
```
|
|
@@ -226,9 +227,9 @@ Here you should use the every helper.
|
|
|
226
227
|
|
|
227
228
|
```js ./tape-test
|
|
228
229
|
patroon(
|
|
229
|
-
every(TypeError, { value: 20 }), 'type error where value is 20',
|
|
230
|
-
every(Error, { value: 30 }), 'error where value is 30',
|
|
231
|
-
every(Error, { value: 20 }), 'error where value is 20'
|
|
230
|
+
every(instanceOf(TypeError), { value: 20 }), 'type error where value is 20',
|
|
231
|
+
every(instanceOf(Error), { value: 30 }), 'error where value is 30',
|
|
232
|
+
every(instanceOf(Error), { value: 20 }), 'error where value is 20'
|
|
232
233
|
)(Object.assign(new TypeError(), { value: 20 }))
|
|
233
234
|
```
|
|
234
235
|
```
|
|
@@ -251,6 +252,35 @@ patroon([], 'is array')([])
|
|
|
251
252
|
patroon(Array, 'is array')([])
|
|
252
253
|
```
|
|
253
254
|
|
|
255
|
+
A less intuitive case:
|
|
256
|
+
|
|
257
|
+
```js ./tape-test > /dev/null
|
|
258
|
+
patroon({}, 'is object')([])
|
|
259
|
+
patroon([], 'is array')({})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Patroon allows this because Arrays can have properties defined.
|
|
263
|
+
|
|
264
|
+
```js ./tape-test > /dev/null
|
|
265
|
+
const array = []
|
|
266
|
+
array.prop = 42
|
|
267
|
+
|
|
268
|
+
patroon({prop: _}, 'has prop')(array)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The other way around is also allowed even if it seems weird.
|
|
272
|
+
|
|
273
|
+
```js ./tape-test > /dev/null
|
|
274
|
+
const object = {0: 42}
|
|
275
|
+
patroon([42], 'has 0th')(object)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
If you do not desire this loose behavior you can use a predicate to make sure
|
|
279
|
+
something is an array or object.
|
|
280
|
+
|
|
281
|
+
```js ./tape-test > /dev/null
|
|
282
|
+
patroon(Array.isArray, 'is array')([])
|
|
283
|
+
```
|
|
254
284
|
|
|
255
285
|
### Reference
|
|
256
286
|
|
|
@@ -259,7 +289,7 @@ helper.
|
|
|
259
289
|
|
|
260
290
|
```js ./tape-test
|
|
261
291
|
patroon(
|
|
262
|
-
Error, 'is an instance of Error',
|
|
292
|
+
instanceOf(Error), 'is an instance of Error',
|
|
263
293
|
reference(Error), 'is the Error constructor'
|
|
264
294
|
)(Error)
|
|
265
295
|
```
|
|
@@ -309,6 +339,20 @@ patroon(
|
|
|
309
339
|
Index 6 has value 7
|
|
310
340
|
```
|
|
311
341
|
|
|
342
|
+
A function that returns the lenght of an array:
|
|
343
|
+
|
|
344
|
+
```js ./tape-test
|
|
345
|
+
const count = patroon(
|
|
346
|
+
[_], ([, ...xs]) => 1 + count(xs),
|
|
347
|
+
[], 0
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
count([0,1,2,3])
|
|
351
|
+
```
|
|
352
|
+
```
|
|
353
|
+
4
|
|
354
|
+
```
|
|
355
|
+
|
|
312
356
|
A function that looks for a certain pattern in an array:
|
|
313
357
|
|
|
314
358
|
```js ./tape-test
|
|
@@ -474,12 +518,12 @@ const oneIsTwo = patroon(1, 2)
|
|
|
474
518
|
oneIsTwo(3)
|
|
475
519
|
```
|
|
476
520
|
```
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
521
|
+
[ 3 ]
|
|
522
|
+
/home/ant/projects/patroon/src/index.js:92
|
|
523
|
+
throw error
|
|
524
|
+
^
|
|
480
525
|
|
|
481
|
-
NoMatchError: Not able to match any pattern for
|
|
482
|
-
at /home/ant/projects/patroon/src/index.js:88:31
|
|
526
|
+
NoMatchError: Not able to match any pattern for arguments
|
|
483
527
|
```
|
|
484
528
|
|
|
485
529
|
#### UnevenArgumentCountError
|
|
@@ -490,12 +534,12 @@ Another error that occurs is when the patroon function is not used correctly.
|
|
|
490
534
|
patroon(1)
|
|
491
535
|
```
|
|
492
536
|
```
|
|
493
|
-
/home/ant/projects/patroon/src/index.js:
|
|
537
|
+
/home/ant/projects/patroon/src/index.js:79
|
|
494
538
|
if (!isEven(list.length)) { throw new UnevenArgumentCountError('Patroon should have an even amount of arguments.') }
|
|
495
539
|
^
|
|
496
540
|
|
|
497
541
|
UnevenArgumentCountError: Patroon should have an even amount of arguments.
|
|
498
|
-
at patroon (/home/ant/projects/patroon/src/index.js:
|
|
542
|
+
at patroon (/home/ant/projects/patroon/src/index.js:79:37)
|
|
499
543
|
```
|
|
500
544
|
|
|
501
545
|
#### PatroonError
|
|
@@ -503,7 +547,7 @@ UnevenArgumentCountError: Patroon should have an even amount of arguments.
|
|
|
503
547
|
All errors patroon produces can be matched against the PatroonError using `instanceof`.
|
|
504
548
|
|
|
505
549
|
```js ./tape-test
|
|
506
|
-
const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
|
|
550
|
+
const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
|
|
507
551
|
|
|
508
552
|
isPatroonError(new NoMatchError())
|
|
509
553
|
isPatroonError(new UnevenArgumentCountError())
|
|
@@ -512,6 +556,14 @@ isPatroonError(new UnevenArgumentCountError())
|
|
|
512
556
|
patroon is causing an error
|
|
513
557
|
```
|
|
514
558
|
|
|
559
|
+
## Examples
|
|
560
|
+
|
|
561
|
+
Patroon can be used in any context that can benefit from pattern matching.
|
|
562
|
+
|
|
563
|
+
- The following tests show how patroon can help you test your JSON API by
|
|
564
|
+
pattern matching on status codes and the body:
|
|
565
|
+
https://github.com/bas080/didomi/blob/master/index.test.js.
|
|
566
|
+
|
|
515
567
|
## Tests
|
|
516
568
|
|
|
517
569
|
[./src/index.test.js][5] - Contains some tests for edge cases and it defines
|
|
@@ -531,7 +583,7 @@ npx nyc npm t | npx tap-nyc
|
|
|
531
583
|
npx nyc check-coverage
|
|
532
584
|
```
|
|
533
585
|
```
|
|
534
|
-
> patroon@
|
|
586
|
+
> patroon@1.1.1 test
|
|
535
587
|
> tape ./src/index.test.js
|
|
536
588
|
-------------|---------|----------|---------|---------|-------------------
|
|
537
589
|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
|
|
@@ -542,10 +594,10 @@ npx nyc check-coverage
|
|
|
542
594
|
walkable.js | 100 | 100 | 100 | 100 |
|
|
543
595
|
-------------|---------|----------|---------|---------|-------------------
|
|
544
596
|
|
|
545
|
-
total:
|
|
546
|
-
passing:
|
|
597
|
+
total: 16
|
|
598
|
+
passing: 16
|
|
547
599
|
|
|
548
|
-
duration:
|
|
600
|
+
duration: 1.6s
|
|
549
601
|
|
|
550
602
|
```
|
|
551
603
|
|
package/README.mz
CHANGED
|
@@ -142,8 +142,8 @@ builtin node error constructors in this example.
|
|
|
142
142
|
|
|
143
143
|
```js ./tape-test
|
|
144
144
|
patroon(
|
|
145
|
-
TypeError, 'is a type error',
|
|
146
|
-
Error, 'is an error'
|
|
145
|
+
instanceOf(TypeError), 'is a type error',
|
|
146
|
+
instanceOf(Error), 'is an error'
|
|
147
147
|
)(new Error())
|
|
148
148
|
```
|
|
149
149
|
|
|
@@ -157,8 +157,8 @@ Because of this you can match a TypeError with an Error.
|
|
|
157
157
|
|
|
158
158
|
```js ./tape-test
|
|
159
159
|
patroon(
|
|
160
|
-
Error, 'matches on error',
|
|
161
|
-
TypeError, 'matches on type error'
|
|
160
|
+
instanceOf(Error), 'matches on error',
|
|
161
|
+
instanceOf(TypeError), 'matches on type error'
|
|
162
162
|
)(new TypeError())
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -167,9 +167,9 @@ Here you should use the every helper.
|
|
|
167
167
|
|
|
168
168
|
```js ./tape-test
|
|
169
169
|
patroon(
|
|
170
|
-
every(TypeError, { value: 20 }), 'type error where value is 20',
|
|
171
|
-
every(Error, { value: 30 }), 'error where value is 30',
|
|
172
|
-
every(Error, { value: 20 }), 'error where value is 20'
|
|
170
|
+
every(instanceOf(TypeError), { value: 20 }), 'type error where value is 20',
|
|
171
|
+
every(instanceOf(Error), { value: 30 }), 'error where value is 30',
|
|
172
|
+
every(instanceOf(Error), { value: 20 }), 'error where value is 20'
|
|
173
173
|
)(Object.assign(new TypeError(), { value: 20 }))
|
|
174
174
|
```
|
|
175
175
|
|
|
@@ -189,6 +189,35 @@ patroon([], 'is array')([])
|
|
|
189
189
|
patroon(Array, 'is array')([])
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
+
A less intuitive case:
|
|
193
|
+
|
|
194
|
+
```js ./tape-test > /dev/null
|
|
195
|
+
patroon({}, 'is object')([])
|
|
196
|
+
patroon([], 'is array')({})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Patroon allows this because Arrays can have properties defined.
|
|
200
|
+
|
|
201
|
+
```js ./tape-test > /dev/null
|
|
202
|
+
const array = []
|
|
203
|
+
array.prop = 42
|
|
204
|
+
|
|
205
|
+
patroon({prop: _}, 'has prop')(array)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The other way around is also allowed even if it seems weird.
|
|
209
|
+
|
|
210
|
+
```js ./tape-test > /dev/null
|
|
211
|
+
const object = {0: 42}
|
|
212
|
+
patroon([42], 'has 0th')(object)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
If you do not desire this loose behavior you can use a predicate to make sure
|
|
216
|
+
something is an array or object.
|
|
217
|
+
|
|
218
|
+
```js ./tape-test > /dev/null
|
|
219
|
+
patroon(Array.isArray, 'is array')([])
|
|
220
|
+
```
|
|
192
221
|
|
|
193
222
|
### Reference
|
|
194
223
|
|
|
@@ -197,7 +226,7 @@ helper.
|
|
|
197
226
|
|
|
198
227
|
```js ./tape-test
|
|
199
228
|
patroon(
|
|
200
|
-
Error, 'is an instance of Error',
|
|
229
|
+
instanceOf(Error), 'is an instance of Error',
|
|
201
230
|
reference(Error), 'is the Error constructor'
|
|
202
231
|
)(Error)
|
|
203
232
|
```
|
|
@@ -235,6 +264,17 @@ patroon(
|
|
|
235
264
|
)([1, 2, 3, 4, 5, 6, 7])
|
|
236
265
|
```
|
|
237
266
|
|
|
267
|
+
A function that returns the lenght of an array:
|
|
268
|
+
|
|
269
|
+
```js ./tape-test
|
|
270
|
+
const count = patroon(
|
|
271
|
+
[_], ([, ...xs]) => 1 + count(xs),
|
|
272
|
+
[], 0
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
count([0,1,2,3])
|
|
276
|
+
```
|
|
277
|
+
|
|
238
278
|
A function that looks for a certain pattern in an array:
|
|
239
279
|
|
|
240
280
|
```js ./tape-test
|
|
@@ -379,12 +419,20 @@ patroon(1)
|
|
|
379
419
|
All errors patroon produces can be matched against the PatroonError using `instanceof`.
|
|
380
420
|
|
|
381
421
|
```js ./tape-test
|
|
382
|
-
const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
|
|
422
|
+
const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
|
|
383
423
|
|
|
384
424
|
isPatroonError(new NoMatchError())
|
|
385
425
|
isPatroonError(new UnevenArgumentCountError())
|
|
386
426
|
```
|
|
387
427
|
|
|
428
|
+
## Examples
|
|
429
|
+
|
|
430
|
+
Patroon can be used in any context that can benefit from pattern matching.
|
|
431
|
+
|
|
432
|
+
- The following tests show how patroon can help you test your JSON API by
|
|
433
|
+
pattern matching on status codes and the body:
|
|
434
|
+
https://github.com/bas080/didomi/blob/master/index.test.js.
|
|
435
|
+
|
|
388
436
|
## Tests
|
|
389
437
|
|
|
390
438
|
[./src/index.test.js][5] - Contains some tests for edge cases and it defines
|
package/package.json
CHANGED
package/src/helpers.js
CHANGED
|
@@ -8,10 +8,6 @@ const toPairs = items => {
|
|
|
8
8
|
|
|
9
9
|
const isNil = x => !(x != null)
|
|
10
10
|
|
|
11
|
-
function isConstructor (func) {
|
|
12
|
-
return Boolean(func && typeof func === 'function' && func.prototype && func.prototype.constructor)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
function isDefined (x) {
|
|
16
12
|
return x != null
|
|
17
13
|
}
|
|
@@ -24,7 +20,6 @@ const is = Ctor => instance => isDefined(instance) && (instance.constructor ===
|
|
|
24
20
|
module.exports = {
|
|
25
21
|
isDefined,
|
|
26
22
|
always: x => () => x,
|
|
27
|
-
isConstructor,
|
|
28
23
|
is,
|
|
29
24
|
toPairs,
|
|
30
25
|
isNil,
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { mapLeaves, path, PathError } = require('./walkable')()
|
|
2
|
-
const {
|
|
2
|
+
const { isFunction, equals, T, is, tryCatch, isEven, isNil, toPairs, always } = require('./helpers')
|
|
3
3
|
|
|
4
4
|
const deprecated = (fn, message) => (...args) => {
|
|
5
5
|
console.error(`[patroon] deprecated: ${message}`)
|
|
@@ -39,9 +39,7 @@ const predicate = pattern => {
|
|
|
39
39
|
if (pattern && pattern[multiSymbol]) { return (...args) => pattern(...args) }
|
|
40
40
|
|
|
41
41
|
const normalize = (value, pth) => {
|
|
42
|
-
if (isRegExp(value)) return arg => value.test(arg)
|
|
43
|
-
|
|
44
|
-
if (isConstructor(value)) { return is(value) }
|
|
42
|
+
if (isRegExp(value)) return arg => value.test(path(pth, arg))
|
|
45
43
|
|
|
46
44
|
if (isFunction(value)) { return arg => value(path(pth, arg)) }
|
|
47
45
|
|
|
@@ -85,7 +83,14 @@ const patroon = (...list) => {
|
|
|
85
83
|
return (...args) => {
|
|
86
84
|
const found = patterns.find(([matches]) => matches(...args))
|
|
87
85
|
|
|
88
|
-
if (isNil(found)) {
|
|
86
|
+
if (isNil(found)) {
|
|
87
|
+
const error = new NoMatchError('Not able to match any pattern for arguments')
|
|
88
|
+
error.arguments = args
|
|
89
|
+
|
|
90
|
+
console.error(args)
|
|
91
|
+
|
|
92
|
+
throw error
|
|
93
|
+
}
|
|
89
94
|
|
|
90
95
|
return found[1](...args)
|
|
91
96
|
}
|
|
@@ -95,6 +100,10 @@ function reference (any) {
|
|
|
95
100
|
return value => any === value
|
|
96
101
|
}
|
|
97
102
|
|
|
103
|
+
function instanceOf (ctor) {
|
|
104
|
+
return value => value instanceof ctor
|
|
105
|
+
}
|
|
106
|
+
|
|
98
107
|
module.exports = Object.assign(patroon, {
|
|
99
108
|
NoMatchError,
|
|
100
109
|
UnevenArgumentCountError,
|
|
@@ -107,5 +116,6 @@ module.exports = Object.assign(patroon, {
|
|
|
107
116
|
reference,
|
|
108
117
|
typed,
|
|
109
118
|
t: typed,
|
|
119
|
+
instanceOf,
|
|
110
120
|
_: T
|
|
111
121
|
})
|
package/src/index.test.js
CHANGED
|
@@ -2,6 +2,7 @@ const { test } = require('tape')
|
|
|
2
2
|
const { check, gen } = require('tape-check')
|
|
3
3
|
const {
|
|
4
4
|
reference,
|
|
5
|
+
instanceOf,
|
|
5
6
|
typed,
|
|
6
7
|
t,
|
|
7
8
|
ref,
|
|
@@ -23,6 +24,11 @@ test('Matches on a simple primitive', t => {
|
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
test('Matches using a RegExp', t => {
|
|
27
|
+
patroon(
|
|
28
|
+
{ a: /^bunion/ }, () => t.fail(),
|
|
29
|
+
{ a: /^banana/ }, () => t.pass()
|
|
30
|
+
)({ a: 'banana tree' })
|
|
31
|
+
|
|
26
32
|
patroon(
|
|
27
33
|
/^bunion/, () => t.fail(),
|
|
28
34
|
/^banana/, () => t.end()
|
|
@@ -39,9 +45,9 @@ test('Match using reference', t => {
|
|
|
39
45
|
|
|
40
46
|
test('Matches on type and value', t => {
|
|
41
47
|
patroon(
|
|
42
|
-
every(Error, { x: 20 }), () => t.fail(),
|
|
43
|
-
every(Error, { x: 10 }), () => t.end(),
|
|
44
|
-
|
|
48
|
+
every(instanceOf(Error), { x: 20 }), () => t.fail(),
|
|
49
|
+
every(instanceOf(Error), { x: 10 }), () => t.end(),
|
|
50
|
+
instanceOf(Error), () => t.fail()
|
|
45
51
|
)(Object.assign(new Error(), { x: 10 }))
|
|
46
52
|
})
|
|
47
53
|
|
package/src/walkable.js
CHANGED
|
@@ -35,17 +35,23 @@ function mapLeaves (config, cb, item) {
|
|
|
35
35
|
|
|
36
36
|
class PathError extends TypeError {}
|
|
37
37
|
|
|
38
|
-
function path (pth, v) {
|
|
38
|
+
function path (pth, v, errorData) {
|
|
39
|
+
if (isNil(errorData)) { return path(pth, v, { path: pth, value: v }) }
|
|
40
|
+
|
|
39
41
|
if (pth.length === 0) { return v }
|
|
40
42
|
|
|
41
43
|
const [key, ...rest] = pth
|
|
42
44
|
|
|
43
45
|
if (!(key in v)) {
|
|
44
|
-
|
|
45
|
-
`Cannot read
|
|
46
|
+
const error = new PathError(
|
|
47
|
+
`Cannot read property '${JSON.stringify(pth)}' of ${JSON.stringify(v)}`)
|
|
48
|
+
|
|
49
|
+
Object.assign(error, errorData)
|
|
50
|
+
|
|
51
|
+
throw error
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
return path(rest, v[key])
|
|
54
|
+
return path(rest, v[key], errorData)
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
function map (fn, x) {
|