patroon 0.4.1 → 1.1.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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Patroon
2
2
 
3
+ Pattern matching in JavaScript without additional syntax.
4
+
3
5
  [![NPM](https://img.shields.io/npm/v/patroon?color=blue&style=flat-square)](https://www.npmjs.com/package/patroon)
4
6
  [![NPM Downloads](https://img.shields.io/npm/dm/patroon?style=flat-square)](https://www.npmjs.com/package/patroon)
5
7
  [![100% Code Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square)](#tests)
@@ -7,8 +9,6 @@
7
9
  [![Standard Code Style](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com)
8
10
  [![License](https://img.shields.io/npm/l/patroon?color=brightgreen&style=flat-square)](./LICENSE)
9
11
 
10
- Pattern matching in JavaScript without additional syntax.
11
-
12
12
  - [Installation](#installation)
13
13
  - [Usage](#usage)
14
14
  * [Primitive](#primitive)
@@ -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)
@@ -43,6 +44,8 @@ npm install patroon
43
44
 
44
45
  Let's see what valid and less valid uses of patroon are.
45
46
 
47
+ > You can try out patroon over at [RunKit][runkit].
48
+
46
49
  ### Primitive
47
50
 
48
51
  The simplest thing one can do is to match on a [primitive][1].
@@ -190,8 +193,8 @@ builtin node error constructors in this example.
190
193
 
191
194
  ```js ./tape-test
192
195
  patroon(
193
- TypeError, 'is a type error',
194
- Error, 'is an error'
196
+ instanceOf(TypeError), 'is a type error',
197
+ instanceOf(Error), 'is an error'
195
198
  )(new Error())
196
199
  ```
197
200
  ```
@@ -211,8 +214,8 @@ Because of this you can match a TypeError with an Error.
211
214
 
212
215
  ```js ./tape-test
213
216
  patroon(
214
- Error, 'matches on error',
215
- TypeError, 'matches on type error'
217
+ instanceOf(Error), 'matches on error',
218
+ instanceOf(TypeError), 'matches on type error'
216
219
  )(new TypeError())
217
220
  ```
218
221
  ```
@@ -224,9 +227,9 @@ Here you should use the every helper.
224
227
 
225
228
  ```js ./tape-test
226
229
  patroon(
227
- every(TypeError, { value: 20 }), 'type error where value is 20',
228
- every(Error, { value: 30 }), 'error where value is 30',
229
- 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'
230
233
  )(Object.assign(new TypeError(), { value: 20 }))
231
234
  ```
232
235
  ```
@@ -249,6 +252,35 @@ patroon([], 'is array')([])
249
252
  patroon(Array, 'is array')([])
250
253
  ```
251
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
+ ```
252
284
 
253
285
  ### Reference
254
286
 
@@ -257,7 +289,7 @@ helper.
257
289
 
258
290
  ```js ./tape-test
259
291
  patroon(
260
- Error, 'is an instance of Error',
292
+ instanceOf(Error), 'is an instance of Error',
261
293
  reference(Error), 'is the Error constructor'
262
294
  )(Error)
263
295
  ```
@@ -307,6 +339,20 @@ patroon(
307
339
  Index 6 has value 7
308
340
  ```
309
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
+
310
356
  A function that looks for a certain pattern in an array:
311
357
 
312
358
  ```js ./tape-test
@@ -472,12 +518,12 @@ const oneIsTwo = patroon(1, 2)
472
518
  oneIsTwo(3)
473
519
  ```
474
520
  ```
475
- /home/ant/projects/patroon/src/index.js:88
476
- if (isNil(found)) { throw new NoMatchError(`Not able to match any pattern for value ${JSON.stringify(args)}`) }
477
- ^
521
+ [ 3 ]
522
+ /home/ant/projects/patroon/src/index.js:92
523
+ throw error
524
+ ^
478
525
 
479
- NoMatchError: Not able to match any pattern for value [3]
480
- at /home/ant/projects/patroon/src/index.js:88:31
526
+ NoMatchError: Not able to match any pattern for arguments
481
527
  ```
482
528
 
483
529
  #### UnevenArgumentCountError
@@ -488,12 +534,12 @@ Another error that occurs is when the patroon function is not used correctly.
488
534
  patroon(1)
489
535
  ```
490
536
  ```
491
- /home/ant/projects/patroon/src/index.js:81
537
+ /home/ant/projects/patroon/src/index.js:79
492
538
  if (!isEven(list.length)) { throw new UnevenArgumentCountError('Patroon should have an even amount of arguments.') }
493
539
  ^
494
540
 
495
541
  UnevenArgumentCountError: Patroon should have an even amount of arguments.
496
- at patroon (/home/ant/projects/patroon/src/index.js:81:37)
542
+ at patroon (/home/ant/projects/patroon/src/index.js:79:37)
497
543
  ```
498
544
 
499
545
  #### PatroonError
@@ -501,7 +547,7 @@ UnevenArgumentCountError: Patroon should have an even amount of arguments.
501
547
  All errors patroon produces can be matched against the PatroonError using `instanceof`.
502
548
 
503
549
  ```js ./tape-test
504
- const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
550
+ const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
505
551
 
506
552
  isPatroonError(new NoMatchError())
507
553
  isPatroonError(new UnevenArgumentCountError())
@@ -510,6 +556,14 @@ isPatroonError(new UnevenArgumentCountError())
510
556
  patroon is causing an error
511
557
  ```
512
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
+
513
567
  ## Tests
514
568
 
515
569
  [./src/index.test.js][5] - Contains some tests for edge cases and it defines
@@ -529,7 +583,7 @@ npx nyc npm t | npx tap-nyc
529
583
  npx nyc check-coverage
530
584
  ```
531
585
  ```
532
- > patroon@0.4.0 test
586
+ > patroon@1.1.0 test
533
587
  > tape ./src/index.test.js
534
588
  -------------|---------|----------|---------|---------|-------------------
535
589
  File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
@@ -540,10 +594,10 @@ npx nyc check-coverage
540
594
  walkable.js | 100 | 100 | 100 | 100 |
541
595
  -------------|---------|----------|---------|---------|-------------------
542
596
 
543
- total: 12
544
- passing: 12
597
+ total: 15
598
+ passing: 15
545
599
 
546
- duration: 1.5s
600
+ duration: 1.4s
547
601
 
548
602
  ```
549
603
 
@@ -566,3 +620,4 @@ polite and read the [CONTRIBUTING.md][10].
566
620
  [8]:https://github.com/istanbuljs/nyc
567
621
  [9]:https://www.npmjs.com/package/patroon
568
622
  [10]:./CONTRIBUTING.md
623
+ [runkit]:https://npm.runkit.com/patroon
package/README.mz CHANGED
@@ -1,5 +1,7 @@
1
1
  # Patroon
2
2
 
3
+ Pattern matching in JavaScript without additional syntax.
4
+
3
5
  [![NPM](https://img.shields.io/npm/v/patroon?color=blue&style=flat-square)](https://www.npmjs.com/package/patroon)
4
6
  [![NPM Downloads](https://img.shields.io/npm/dm/patroon?style=flat-square)](https://www.npmjs.com/package/patroon)
5
7
  [![100% Code Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square)](#tests)
@@ -7,8 +9,6 @@
7
9
  [![Standard Code Style](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com)
8
10
  [![License](https://img.shields.io/npm/l/patroon?color=brightgreen&style=flat-square)](./LICENSE)
9
11
 
10
- Pattern matching in JavaScript without additional syntax.
11
-
12
12
  <toc
13
13
 
14
14
  ## Installation
@@ -23,6 +23,8 @@ npm install patroon
23
23
 
24
24
  Let's see what valid and less valid uses of patroon are.
25
25
 
26
+ > You can try out patroon over at [RunKit][runkit].
27
+
26
28
  ### Primitive
27
29
 
28
30
  The simplest thing one can do is to match on a [primitive][1].
@@ -140,8 +142,8 @@ builtin node error constructors in this example.
140
142
 
141
143
  ```js ./tape-test
142
144
  patroon(
143
- TypeError, 'is a type error',
144
- Error, 'is an error'
145
+ instanceOf(TypeError), 'is a type error',
146
+ instanceOf(Error), 'is an error'
145
147
  )(new Error())
146
148
  ```
147
149
 
@@ -155,8 +157,8 @@ Because of this you can match a TypeError with an Error.
155
157
 
156
158
  ```js ./tape-test
157
159
  patroon(
158
- Error, 'matches on error',
159
- TypeError, 'matches on type error'
160
+ instanceOf(Error), 'matches on error',
161
+ instanceOf(TypeError), 'matches on type error'
160
162
  )(new TypeError())
161
163
  ```
162
164
 
@@ -165,9 +167,9 @@ Here you should use the every helper.
165
167
 
166
168
  ```js ./tape-test
167
169
  patroon(
168
- every(TypeError, { value: 20 }), 'type error where value is 20',
169
- every(Error, { value: 30 }), 'error where value is 30',
170
- 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'
171
173
  )(Object.assign(new TypeError(), { value: 20 }))
172
174
  ```
173
175
 
@@ -187,6 +189,35 @@ patroon([], 'is array')([])
187
189
  patroon(Array, 'is array')([])
188
190
  ```
189
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
+ ```
190
221
 
191
222
  ### Reference
192
223
 
@@ -195,7 +226,7 @@ helper.
195
226
 
196
227
  ```js ./tape-test
197
228
  patroon(
198
- Error, 'is an instance of Error',
229
+ instanceOf(Error), 'is an instance of Error',
199
230
  reference(Error), 'is the Error constructor'
200
231
  )(Error)
201
232
  ```
@@ -233,6 +264,17 @@ patroon(
233
264
  )([1, 2, 3, 4, 5, 6, 7])
234
265
  ```
235
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
+
236
278
  A function that looks for a certain pattern in an array:
237
279
 
238
280
  ```js ./tape-test
@@ -377,12 +419,20 @@ patroon(1)
377
419
  All errors patroon produces can be matched against the PatroonError using `instanceof`.
378
420
 
379
421
  ```js ./tape-test
380
- const isPatroonError = patroon(PatroonError, 'patroon is causing an error')
422
+ const isPatroonError = patroon(instanceOf(PatroonError), 'patroon is causing an error')
381
423
 
382
424
  isPatroonError(new NoMatchError())
383
425
  isPatroonError(new UnevenArgumentCountError())
384
426
  ```
385
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
+
386
436
  ## Tests
387
437
 
388
438
  [./src/index.test.js][5] - Contains some tests for edge cases and it defines
@@ -419,3 +469,4 @@ polite and read the [CONTRIBUTING.md][10].
419
469
  [8]:https://github.com/istanbuljs/nyc
420
470
  [9]:https://www.npmjs.com/package/patroon
421
471
  [10]:./CONTRIBUTING.md
472
+ [runkit]:https://npm.runkit.com/patroon
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patroon",
3
- "version": "0.4.1",
3
+ "version": "1.1.0",
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}`)
@@ -8,7 +8,7 @@ const deprecated = (fn, message) => (...args) => {
8
8
 
9
9
  const typed = deprecated(function typed (Ctor, pattern) {
10
10
  return every(Ctor, pattern)
11
- }, 'replace "typed(Constructor, [pattern])" with "every(Constructor, pattern)"')
11
+ }, 'replace "typed" or "t" helpers with "every(Constructor, pattern)"')
12
12
 
13
13
  const isRegExp = is(RegExp)
14
14
 
@@ -41,8 +41,6 @@ const predicate = pattern => {
41
41
  const normalize = (value, pth) => {
42
42
  if (isRegExp(value)) return arg => value.test(arg)
43
43
 
44
- if (isConstructor(value)) { return is(value) }
45
-
46
44
  if (isFunction(value)) { return arg => value(path(pth, arg)) }
47
45
 
48
46
  return arg => equals(path(pth, arg), value)
@@ -85,16 +83,25 @@ 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)
89
91
 
90
- const [, doFn] = found
92
+ throw error
93
+ }
91
94
 
92
- return doFn(...args)
95
+ return found[1](...args)
93
96
  }
94
97
  }
95
98
 
96
- function reference (fn) {
97
- return (arg) => fn === arg
99
+ function reference (any) {
100
+ return value => any === value
101
+ }
102
+
103
+ function instanceOf (ctor) {
104
+ return value => value instanceof ctor
98
105
  }
99
106
 
100
107
  module.exports = Object.assign(patroon, {
@@ -109,5 +116,6 @@ module.exports = Object.assign(patroon, {
109
116
  reference,
110
117
  typed,
111
118
  t: typed,
119
+ instanceOf,
112
120
  _: T
113
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,
@@ -39,9 +40,9 @@ test('Match using reference', t => {
39
40
 
40
41
  test('Matches on type and value', t => {
41
42
  patroon(
42
- every(Error, { x: 20 }), () => t.fail(),
43
- every(Error, { x: 10 }), () => t.end(),
44
- every(Error), () => t.fail()
43
+ every(instanceOf(Error), { x: 20 }), () => t.fail(),
44
+ every(instanceOf(Error), { x: 10 }), () => t.end(),
45
+ instanceOf(Error), () => t.fail()
45
46
  )(Object.assign(new Error(), { x: 10 }))
46
47
  })
47
48
 
@@ -152,9 +153,16 @@ test('Matches when arguments match multi pattern', check(gen.any, (t, val) => {
152
153
  }))
153
154
 
154
155
  test('Deprecated functions', ts => {
155
- ts.plan(0)
156
+ ts.plan(3)
157
+
158
+ const consoleError = console.error
159
+
160
+ console.error = message =>
161
+ ts.ok(message.startsWith('[patroon] deprecated: '))
162
+
156
163
  typed(Error, null)(new Error())
157
164
  t(Error, null)(new Error())
158
165
  ref(Error, null)(Error)
166
+ console.error = consoleError
159
167
  ts.end()
160
168
  })
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,