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 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
- /home/ant/projects/patroon/src/index.js:88
478
- if (isNil(found)) { throw new NoMatchError(`Not able to match any pattern for value ${JSON.stringify(args)}`) }
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 value [3]
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:81
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:81:37)
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@0.4.1 test
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: 15
546
- passing: 15
597
+ total: 16
598
+ passing: 16
547
599
 
548
- duration: 703ms
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patroon",
3
- "version": "0.4.2",
3
+ "version": "1.1.1",
4
4
  "description": "Pattern matching library",
5
5
  "repository": "github:bas080/patroon",
6
6
  "main": "./src/index.js",
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 { isConstructor, isFunction, equals, T, is, tryCatch, isEven, isNil, toPairs, always } = require('./helpers')
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)) { throw new NoMatchError(`Not able to match any pattern for value ${JSON.stringify(args)}`) }
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
- every(Error), () => t.fail()
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
- throw new PathError(
45
- `Cannot read path '${JSON.stringify(pth)}' on ${JSON.stringify(v)}`)
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) {
package/tape-test CHANGED
@@ -4,6 +4,7 @@ cat <(echo '
4
4
  const {
5
5
  _,
6
6
  every,
7
+ instanceOf,
7
8
  some,
8
9
  multi,
9
10
  reference,