patroon 0.4.3 → 1.1.2
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 +58 -20
- package/README.mz +46 -9
- package/package.json +1 -1
- package/src/helpers.js +0 -5
- package/src/index.js +16 -5
- package/src/index.test.js +28 -3
- package/src/walkable.js +1 -1
- 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
|
```
|
|
@@ -488,12 +518,12 @@ const oneIsTwo = patroon(1, 2)
|
|
|
488
518
|
oneIsTwo(3)
|
|
489
519
|
```
|
|
490
520
|
```
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
521
|
+
[ 3 ]
|
|
522
|
+
/home/ant/projects/patroon/src/index.js:93
|
|
523
|
+
throw error
|
|
524
|
+
^
|
|
494
525
|
|
|
495
|
-
NoMatchError: Not able to match any pattern for
|
|
496
|
-
at /home/ant/projects/patroon/src/index.js:88:31
|
|
526
|
+
NoMatchError: Not able to match any pattern for arguments
|
|
497
527
|
```
|
|
498
528
|
|
|
499
529
|
#### UnevenArgumentCountError
|
|
@@ -504,12 +534,12 @@ Another error that occurs is when the patroon function is not used correctly.
|
|
|
504
534
|
patroon(1)
|
|
505
535
|
```
|
|
506
536
|
```
|
|
507
|
-
/home/ant/projects/patroon/src/index.js:
|
|
537
|
+
/home/ant/projects/patroon/src/index.js:80
|
|
508
538
|
if (!isEven(list.length)) { throw new UnevenArgumentCountError('Patroon should have an even amount of arguments.') }
|
|
509
539
|
^
|
|
510
540
|
|
|
511
541
|
UnevenArgumentCountError: Patroon should have an even amount of arguments.
|
|
512
|
-
at patroon (/home/ant/projects/patroon/src/index.js:
|
|
542
|
+
at patroon (/home/ant/projects/patroon/src/index.js:80:37)
|
|
513
543
|
```
|
|
514
544
|
|
|
515
545
|
#### PatroonError
|
|
@@ -517,7 +547,7 @@ UnevenArgumentCountError: Patroon should have an even amount of arguments.
|
|
|
517
547
|
All errors patroon produces can be matched against the PatroonError using `instanceof`.
|
|
518
548
|
|
|
519
549
|
```js ./tape-test
|
|
520
|
-
const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
|
|
550
|
+
const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
|
|
521
551
|
|
|
522
552
|
isPatroonError(new NoMatchError())
|
|
523
553
|
isPatroonError(new UnevenArgumentCountError())
|
|
@@ -526,6 +556,14 @@ isPatroonError(new UnevenArgumentCountError())
|
|
|
526
556
|
patroon is causing an error
|
|
527
557
|
```
|
|
528
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
|
+
|
|
529
567
|
## Tests
|
|
530
568
|
|
|
531
569
|
[./src/index.test.js][5] - Contains some tests for edge cases and it defines
|
|
@@ -545,7 +583,7 @@ npx nyc npm t | npx tap-nyc
|
|
|
545
583
|
npx nyc check-coverage
|
|
546
584
|
```
|
|
547
585
|
```
|
|
548
|
-
> patroon@
|
|
586
|
+
> patroon@1.1.2 test
|
|
549
587
|
> tape ./src/index.test.js
|
|
550
588
|
-------------|---------|----------|---------|---------|-------------------
|
|
551
589
|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
|
|
@@ -556,10 +594,10 @@ npx nyc check-coverage
|
|
|
556
594
|
walkable.js | 100 | 100 | 100 | 100 |
|
|
557
595
|
-------------|---------|----------|---------|---------|-------------------
|
|
558
596
|
|
|
559
|
-
total:
|
|
560
|
-
passing:
|
|
597
|
+
total: 19
|
|
598
|
+
passing: 19
|
|
561
599
|
|
|
562
|
-
duration:
|
|
600
|
+
duration: 1.5s
|
|
563
601
|
|
|
564
602
|
```
|
|
565
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
|
```
|
|
@@ -390,12 +419,20 @@ patroon(1)
|
|
|
390
419
|
All errors patroon produces can be matched against the PatroonError using `instanceof`.
|
|
391
420
|
|
|
392
421
|
```js ./tape-test
|
|
393
|
-
const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
|
|
422
|
+
const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
|
|
394
423
|
|
|
395
424
|
isPatroonError(new NoMatchError())
|
|
396
425
|
isPatroonError(new UnevenArgumentCountError())
|
|
397
426
|
```
|
|
398
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
|
+
|
|
399
436
|
## Tests
|
|
400
437
|
|
|
401
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,6 @@
|
|
|
1
1
|
const { mapLeaves, path, PathError } = require('./walkable')()
|
|
2
|
-
const {
|
|
2
|
+
const { isFunction, equals, T, is, tryCatch, isEven, isNil, toPairs, always } = require('./helpers')
|
|
3
|
+
const { inspect } = require('util')
|
|
3
4
|
|
|
4
5
|
const deprecated = (fn, message) => (...args) => {
|
|
5
6
|
console.error(`[patroon] deprecated: ${message}`)
|
|
@@ -39,9 +40,7 @@ const predicate = pattern => {
|
|
|
39
40
|
if (pattern && pattern[multiSymbol]) { return (...args) => pattern(...args) }
|
|
40
41
|
|
|
41
42
|
const normalize = (value, pth) => {
|
|
42
|
-
if (isRegExp(value)) return arg => value.test(arg)
|
|
43
|
-
|
|
44
|
-
if (isConstructor(value)) { return is(value) }
|
|
43
|
+
if (isRegExp(value)) return arg => value.test(path(pth, arg))
|
|
45
44
|
|
|
46
45
|
if (isFunction(value)) { return arg => value(path(pth, arg)) }
|
|
47
46
|
|
|
@@ -85,7 +84,14 @@ const patroon = (...list) => {
|
|
|
85
84
|
return (...args) => {
|
|
86
85
|
const found = patterns.find(([matches]) => matches(...args))
|
|
87
86
|
|
|
88
|
-
if (isNil(found)) {
|
|
87
|
+
if (isNil(found)) {
|
|
88
|
+
const error = new NoMatchError('Not able to match any pattern for arguments')
|
|
89
|
+
error.arguments = args
|
|
90
|
+
|
|
91
|
+
console.error(inspect(args))
|
|
92
|
+
|
|
93
|
+
throw error
|
|
94
|
+
}
|
|
89
95
|
|
|
90
96
|
return found[1](...args)
|
|
91
97
|
}
|
|
@@ -95,6 +101,10 @@ function reference (any) {
|
|
|
95
101
|
return value => any === value
|
|
96
102
|
}
|
|
97
103
|
|
|
104
|
+
function instanceOf (ctor) {
|
|
105
|
+
return value => value instanceof ctor
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
module.exports = Object.assign(patroon, {
|
|
99
109
|
NoMatchError,
|
|
100
110
|
UnevenArgumentCountError,
|
|
@@ -107,5 +117,6 @@ module.exports = Object.assign(patroon, {
|
|
|
107
117
|
reference,
|
|
108
118
|
typed,
|
|
109
119
|
t: typed,
|
|
120
|
+
instanceOf,
|
|
110
121
|
_: T
|
|
111
122
|
})
|
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
|
|
|
@@ -165,3 +171,22 @@ test('Deprecated functions', ts => {
|
|
|
165
171
|
console.error = consoleError
|
|
166
172
|
ts.end()
|
|
167
173
|
})
|
|
174
|
+
|
|
175
|
+
// Circular reference
|
|
176
|
+
const circular = {}
|
|
177
|
+
circular.a = circular
|
|
178
|
+
|
|
179
|
+
test('At the moment recursive patterns throw and print', t => {
|
|
180
|
+
t.plan(1)
|
|
181
|
+
t.throws(() => patroon(circular, true)({}))
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
test('Throws and prints because path is not defined', t => {
|
|
185
|
+
t.plan(1)
|
|
186
|
+
t.throws(() => patroon({ b: _ }, true)(circular))
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('Does not throw when value is recursive', t => {
|
|
190
|
+
t.plan(1)
|
|
191
|
+
t.ok(patroon({ a: _ }, true)(circular))
|
|
192
|
+
})
|
package/src/walkable.js
CHANGED