Browse code
chore: reformat
fiddlerwoaroof authored on 07/07/2020 08:10:18
Showing 17 changed files
Showing 17 changed files
- .babelrc
- .eslintrc
- gen-fun.html
- jsconfig.json
- package.json
- react-demo/.babelrc
- react-demo/Model.js
- react-demo/index.html
- react-demo/main.js
- react-demo/main.test.js
- react-demo/package.json
- react-demo/render.js
- react-demo/rollup.config.js
- src/genfun_formatter.js
- src/genfuns.js
- src/genfuns.test.js
- src/main.js
... | ... |
@@ -1,26 +1,26 @@ |
1 | 1 |
{ |
2 |
- "extends": [ |
|
3 |
- "eslint:recommended", |
|
4 |
- "plugin:react/recommended" |
|
5 |
- ], |
|
6 |
- "env": { |
|
7 |
- "browser": true, |
|
8 |
- "node": true, |
|
9 |
- "jest": true, |
|
10 |
- "es6": true |
|
11 |
- }, |
|
12 |
- "parserOptions": { |
|
13 |
- "ecmaVersion": 2018, |
|
14 |
- "sourceType": "module" |
|
15 |
- }, |
|
16 |
- "rules": { |
|
17 |
- "no-unused-vars": ["error", { |
|
18 |
- "argsIgnorePattern": "(^[_][_]*.*$)|(^.$)" |
|
19 |
- }] |
|
20 |
- }, |
|
21 |
- "settings": { |
|
22 |
- "react": { |
|
23 |
- "version": "detect" |
|
24 |
- } |
|
2 |
+ "extends": ["eslint:recommended", "plugin:react/recommended"], |
|
3 |
+ "env": { |
|
4 |
+ "browser": true, |
|
5 |
+ "node": true, |
|
6 |
+ "jest": true, |
|
7 |
+ "es6": true |
|
8 |
+ }, |
|
9 |
+ "parserOptions": { |
|
10 |
+ "ecmaVersion": 2018, |
|
11 |
+ "sourceType": "module" |
|
12 |
+ }, |
|
13 |
+ "rules": { |
|
14 |
+ "no-unused-vars": [ |
|
15 |
+ "error", |
|
16 |
+ { |
|
17 |
+ "argsIgnorePattern": "(^[_][_]*.*$)|(^.$)" |
|
18 |
+ } |
|
19 |
+ ] |
|
20 |
+ }, |
|
21 |
+ "settings": { |
|
22 |
+ "react": { |
|
23 |
+ "version": "detect" |
|
25 | 24 |
} |
25 |
+ } |
|
26 | 26 |
} |
... | ... |
@@ -1,21 +1,27 @@ |
1 |
-<!doctype html> |
|
1 |
+<!DOCTYPE html> |
|
2 | 2 |
<html lang="en"> |
3 |
- <head> |
|
4 |
- <meta charset="UTF-8"/> |
|
5 |
- <title>Document</title> |
|
6 |
- <style> |
|
7 |
- the-name {font-weight: bold} |
|
8 |
- the-address {font-style: italic} |
|
9 |
- </style> |
|
10 |
- </head> |
|
11 |
- <body> |
|
12 |
- <section class="root"> |
|
13 |
- <the-name></the-name> |
|
14 |
- <the-address></the-address> |
|
15 |
- </section> |
|
16 |
- <script>var exports = {};</script> |
|
17 |
- <script src="./dist/genfuns.js"></script> |
|
18 |
- <script src="./dist/genfun_formatter.js"></script> |
|
19 |
- <script src="./dist/main.js"></script> |
|
20 |
- </body> |
|
3 |
+ <head> |
|
4 |
+ <meta charset="UTF-8" /> |
|
5 |
+ <title>Document</title> |
|
6 |
+ <style> |
|
7 |
+ the-name { |
|
8 |
+ font-weight: bold; |
|
9 |
+ } |
|
10 |
+ the-address { |
|
11 |
+ font-style: italic; |
|
12 |
+ } |
|
13 |
+ </style> |
|
14 |
+ </head> |
|
15 |
+ <body> |
|
16 |
+ <section class="root"> |
|
17 |
+ <the-name></the-name> |
|
18 |
+ <the-address></the-address> |
|
19 |
+ </section> |
|
20 |
+ <script> |
|
21 |
+ var exports = {}; |
|
22 |
+ </script> |
|
23 |
+ <script src="./dist/genfuns.js"></script> |
|
24 |
+ <script src="./dist/genfun_formatter.js"></script> |
|
25 |
+ <script src="./dist/main.js"></script> |
|
26 |
+ </body> |
|
21 | 27 |
</html> |
... | ... |
@@ -1,24 +1,22 @@ |
1 | 1 |
{ |
2 |
- "compilerOptions": { |
|
3 |
- "target": "es2017", |
|
4 |
- "baseUrl": "src", |
|
5 |
- "allowSyntheticDefaultImports": true, |
|
6 |
- "noEmit": true, |
|
7 |
- "checkJs": true, |
|
8 |
- "lib": [ "es2017" ], |
|
9 |
- "rootDirs": [ |
|
10 |
- "src" |
|
11 |
- ], |
|
12 |
- "module": "commonjs" |
|
13 |
- }, |
|
14 |
- "exclude": [ |
|
15 |
- ".history/*", |
|
16 |
- "frontend", |
|
17 |
- "node_modules", |
|
18 |
- "node_modules/**/*", |
|
19 |
- "**/node_modules/**/*", |
|
20 |
- "**/node_modules/*", |
|
21 |
- "/.#*", |
|
22 |
- "**/.#*" |
|
23 |
- ] |
|
2 |
+ "compilerOptions": { |
|
3 |
+ "target": "es2017", |
|
4 |
+ "baseUrl": "src", |
|
5 |
+ "allowSyntheticDefaultImports": true, |
|
6 |
+ "noEmit": true, |
|
7 |
+ "checkJs": true, |
|
8 |
+ "lib": ["es2017"], |
|
9 |
+ "rootDirs": ["src"], |
|
10 |
+ "module": "commonjs" |
|
11 |
+ }, |
|
12 |
+ "exclude": [ |
|
13 |
+ ".history/*", |
|
14 |
+ "frontend", |
|
15 |
+ "node_modules", |
|
16 |
+ "node_modules/**/*", |
|
17 |
+ "**/node_modules/**/*", |
|
18 |
+ "**/node_modules/*", |
|
19 |
+ "/.#*", |
|
20 |
+ "**/.#*" |
|
21 |
+ ] |
|
24 | 22 |
} |
... | ... |
@@ -1,46 +1,46 @@ |
1 | 1 |
{ |
2 |
- "author": "Ed L", |
|
3 |
- "license": "ISC", |
|
4 |
- "name": "js-generic-functions", |
|
5 |
- "version": "1.0.9", |
|
6 |
- "description": "A CLOS-inspired implementation of generic functions", |
|
7 |
- "repository": "https://github.com/fiddlerwoaroof/js-generic-functions", |
|
8 |
- "homepage": "https://fiddlerwoaroof.github.io/js-generic-functions/", |
|
9 |
- "main": "dist/genfuns.js", |
|
10 |
- "dependencies": {}, |
|
11 |
- "devDependencies": { |
|
12 |
- "@babel/cli": "^7.10.4", |
|
13 |
- "@babel/core": "^7.10.4", |
|
14 |
- "@babel/plugin-proposal-object-rest-spread": "^7.10.4", |
|
15 |
- "@babel/preset-env": "^7.10.4", |
|
16 |
- "babel-core": "^7.0.0-bridge.0", |
|
17 |
- "eslint": "^7.1.0", |
|
18 |
- "eslint-plugin-react": "^7.20.3", |
|
19 |
- "jest": "^26.0.1", |
|
20 |
- "jest-junit": "^11.0.1", |
|
21 |
- "prettier": "^2.0.5" |
|
22 |
- }, |
|
23 |
- "files": [ |
|
24 |
- "/dist/*.js", |
|
25 |
- "/dist/*.css", |
|
26 |
- "/dist/*.html", |
|
27 |
- "/src/*.js", |
|
28 |
- "/src/*.css", |
|
29 |
- "/src/*.html" |
|
30 |
- ], |
|
31 |
- "scripts": { |
|
32 |
- "test": "jest src", |
|
33 |
- "format": "prettier --write .", |
|
34 |
- "build": "babel -d dist src", |
|
35 |
- "prepublishOnly": "babel -d dist src && jest src && eslint src" |
|
36 |
- }, |
|
37 |
- "jest-junit": { |
|
38 |
- "outputDirectory": "./test-results/jest", |
|
39 |
- "outputName": "./results.xml" |
|
40 |
- }, |
|
41 |
- "prettier": { |
|
42 |
- "trailingComma": "es5", |
|
43 |
- "jsxBracketSameLine": true, |
|
44 |
- "arrowParens": "avoid" |
|
45 |
- } |
|
2 |
+ "author": "Ed L", |
|
3 |
+ "license": "ISC", |
|
4 |
+ "name": "js-generic-functions", |
|
5 |
+ "version": "1.0.9", |
|
6 |
+ "description": "A CLOS-inspired implementation of generic functions", |
|
7 |
+ "repository": "https://github.com/fiddlerwoaroof/js-generic-functions", |
|
8 |
+ "homepage": "https://fiddlerwoaroof.github.io/js-generic-functions/", |
|
9 |
+ "main": "dist/genfuns.js", |
|
10 |
+ "dependencies": {}, |
|
11 |
+ "devDependencies": { |
|
12 |
+ "@babel/cli": "^7.10.4", |
|
13 |
+ "@babel/core": "^7.10.4", |
|
14 |
+ "@babel/plugin-proposal-object-rest-spread": "^7.10.4", |
|
15 |
+ "@babel/preset-env": "^7.10.4", |
|
16 |
+ "babel-core": "^7.0.0-bridge.0", |
|
17 |
+ "eslint": "^7.1.0", |
|
18 |
+ "eslint-plugin-react": "^7.20.3", |
|
19 |
+ "jest": "^26.0.1", |
|
20 |
+ "jest-junit": "^11.0.1", |
|
21 |
+ "prettier": "^2.0.5" |
|
22 |
+ }, |
|
23 |
+ "files": [ |
|
24 |
+ "/dist/*.js", |
|
25 |
+ "/dist/*.css", |
|
26 |
+ "/dist/*.html", |
|
27 |
+ "/src/*.js", |
|
28 |
+ "/src/*.css", |
|
29 |
+ "/src/*.html" |
|
30 |
+ ], |
|
31 |
+ "scripts": { |
|
32 |
+ "test": "jest src", |
|
33 |
+ "format": "prettier --write .", |
|
34 |
+ "build": "babel -d dist src", |
|
35 |
+ "prepublishOnly": "babel -d dist src && jest src && eslint src" |
|
36 |
+ }, |
|
37 |
+ "jest-junit": { |
|
38 |
+ "outputDirectory": "./test-results/jest", |
|
39 |
+ "outputName": "./results.xml" |
|
40 |
+ }, |
|
41 |
+ "prettier": { |
|
42 |
+ "trailingComma": "es5", |
|
43 |
+ "jsxBracketSameLine": true, |
|
44 |
+ "arrowParens": "avoid" |
|
45 |
+ } |
|
46 | 46 |
} |
... | ... |
@@ -1,4 +1,7 @@ |
1 | 1 |
{ |
2 |
- "presets": ["@babel/env", "@babel/preset-react"], |
|
3 |
- "plugins": ["@babel/plugin-transform-react-jsx", "@babel/plugin-proposal-object-rest-spread"] |
|
2 |
+ "presets": ["@babel/env", "@babel/preset-react"], |
|
3 |
+ "plugins": [ |
|
4 |
+ "@babel/plugin-transform-react-jsx", |
|
5 |
+ "@babel/plugin-proposal-object-rest-spread" |
|
6 |
+ ] |
|
4 | 7 |
} |
... | ... |
@@ -2,18 +2,28 @@ export class Item { |
2 | 2 |
constructor(price) { |
3 | 3 |
this.price = price; |
4 | 4 |
} |
5 |
- get tax() { throw "not implemented"; } |
|
6 |
- get subtotal() { return this.price + this.tax; } |
|
5 |
+ get tax() { |
|
6 |
+ throw "not implemented"; |
|
7 |
+ } |
|
8 |
+ get subtotal() { |
|
9 |
+ return this.price + this.tax; |
|
10 |
+ } |
|
7 | 11 |
} |
8 | 12 |
|
9 | 13 |
export class AlcoholicBeverage extends Item { |
10 |
- get tax() { return this.price * 0.25; } |
|
14 |
+ get tax() { |
|
15 |
+ return this.price * 0.25; |
|
16 |
+ } |
|
11 | 17 |
} |
12 | 18 |
|
13 | 19 |
export class NormalFood extends Item { |
14 |
- get tax() { return 0; } |
|
20 |
+ get tax() { |
|
21 |
+ return 0; |
|
22 |
+ } |
|
15 | 23 |
} |
16 | 24 |
|
17 | 25 |
export class NonFood extends Item { |
18 |
- get tax() { return this.price * 0.0825; } |
|
26 |
+ get tax() { |
|
27 |
+ return this.price * 0.0825; |
|
28 |
+ } |
|
19 | 29 |
} |
... | ... |
@@ -1,25 +1,51 @@ |
1 | 1 |
<!DOCTYPE html> |
2 | 2 |
<html lang="en"> |
3 |
+ <head> |
|
4 |
+ <meta charset="UTF-8" /> |
|
5 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
6 |
+ <meta http-equiv="X-UA-Compatible" content="ie=edge" /> |
|
7 |
+ <title>Document</title> |
|
8 |
+ <script src="dist/main.o.js" defer></script> |
|
9 |
+ <style> |
|
10 |
+ * { |
|
11 |
+ box-sizing: border-box; |
|
12 |
+ } |
|
13 |
+ h1 > span { |
|
14 |
+ display: inline-block; |
|
15 |
+ white-space: nowrap; |
|
16 |
+ } |
|
17 |
+ h1 > span + span::before { |
|
18 |
+ content: " "; |
|
19 |
+ display: inline-block; |
|
20 |
+ } |
|
21 |
+ h1 .desc, |
|
22 |
+ h1 .price { |
|
23 |
+ display: inline; |
|
24 |
+ width: auto; |
|
25 |
+ } |
|
26 |
+ ul { |
|
27 |
+ padding: 0; |
|
28 |
+ list-style: none; |
|
29 |
+ width: 14em; |
|
30 |
+ font-family: "Source Code Pro", monospace; |
|
31 |
+ } |
|
32 |
+ li { |
|
33 |
+ text-align: right; |
|
34 |
+ } |
|
35 |
+ .desc { |
|
36 |
+ display: inline-block; |
|
37 |
+ vertical-align: baseline; |
|
38 |
+ width: 75%; |
|
39 |
+ } |
|
40 |
+ .price { |
|
41 |
+ display: inline-block; |
|
42 |
+ vertical-align: baseline; |
|
43 |
+ width: 25%; |
|
44 |
+ } |
|
45 |
+ </style> |
|
46 |
+ </head> |
|
3 | 47 |
|
4 |
-<head> |
|
5 |
- <meta charset="UTF-8"> |
|
6 |
- <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
7 |
- <meta http-equiv="X-UA-Compatible" content="ie=edge"> |
|
8 |
- <title>Document</title> |
|
9 |
- <script src="dist/main.o.js" defer></script> |
|
10 |
- <style> |
|
11 |
- * { box-sizing: border-box; } |
|
12 |
- h1 > span {display: inline-block; white-space: nowrap;} |
|
13 |
- h1 > span + span::before {content: ' '; display: inline-block; } |
|
14 |
- h1 .desc, h1 .price {display: inline; width: auto; } |
|
15 |
- ul { padding: 0; list-style: none; width: 14em; font-family: 'Source Code Pro', monospace } |
|
16 |
- li { text-align: right;} |
|
17 |
- .desc { display: inline-block; vertical-align: baseline; width: 75%;} |
|
18 |
- .price { display: inline-block; vertical-align: baseline; width: 25%; } |
|
19 |
- </style> |
|
20 |
-</head> |
|
21 |
- |
|
22 |
-<body> |
|
23 |
- <main></main> |
|
24 |
-</body> |
|
25 |
-</html> |
|
26 | 48 |
\ No newline at end of file |
49 |
+ <body> |
|
50 |
+ <main></main> |
|
51 |
+ </body> |
|
52 |
+</html> |
... | ... |
@@ -1,31 +1,49 @@ |
1 |
-import React from 'react'; |
|
2 |
-import ReactDOM from 'react-dom'; |
|
3 |
-import '../src/genfun_formatter'; |
|
4 |
-import { Receipt } from './render'; |
|
5 |
-import * as m from './Model'; |
|
1 |
+import React from "react"; |
|
2 |
+import ReactDOM from "react-dom"; |
|
3 |
+import "../src/genfun_formatter"; |
|
4 |
+import { Receipt } from "./render"; |
|
5 |
+import * as m from "./Model"; |
|
6 | 6 |
|
7 |
-import * as gf from '../src/genfuns'; |
|
7 |
+import * as gf from "../src/genfuns"; |
|
8 | 8 |
|
9 |
-const Editable = |
|
10 |
- gf.defgeneric("Editable", "props") |
|
11 |
- .around([Object], function (_) { |
|
12 |
- return <div style={{ display: 'inline-block' }}>{this.call_next_method()} </div> |
|
13 |
- }) |
|
14 |
- .primary([gf.Shape("label", "htmlFor")], ({ label, htmlFor }) => |
|
15 |
- <label {...{htmlFor}}>{label}</label>) |
|
16 |
- .primary([gf.Shape("label", "htmlFor", "value")], function ({ value, onChange }) { |
|
17 |
- return <> |
|
9 |
+const Editable = gf |
|
10 |
+ .defgeneric("Editable", "props") |
|
11 |
+ .around([Object], function (_) { |
|
12 |
+ return ( |
|
13 |
+ <div style={{ display: "inline-block" }}>{this.call_next_method()} </div> |
|
14 |
+ ); |
|
15 |
+ }) |
|
16 |
+ .primary([gf.Shape("label", "htmlFor")], ({ label, htmlFor }) => ( |
|
17 |
+ <label {...{ htmlFor }}>{label}</label> |
|
18 |
+ )) |
|
19 |
+ .primary([gf.Shape("label", "htmlFor", "value")], function ({ |
|
20 |
+ value, |
|
21 |
+ onChange, |
|
22 |
+ }) { |
|
23 |
+ return ( |
|
24 |
+ <> |
|
18 | 25 |
{this.call_next_method()} |
19 | 26 |
{onChange ? null : <span> {value}</span>} |
20 | 27 |
</> |
21 |
- }) |
|
22 |
- .primary([gf.Shape("label", "htmlFor", "value", "onChange")], function ({ value, htmlFor, onChange }) { |
|
23 |
- return <> |
|
28 |
+ ); |
|
29 |
+ }) |
|
30 |
+ .primary([gf.Shape("label", "htmlFor", "value", "onChange")], function ({ |
|
31 |
+ value, |
|
32 |
+ htmlFor, |
|
33 |
+ onChange, |
|
34 |
+ }) { |
|
35 |
+ return ( |
|
36 |
+ <> |
|
24 | 37 |
{this.call_next_method()} |
25 |
- <input type="text" value={value} id={htmlFor} name={htmlFor} onChange={onChange}></input> |
|
38 |
+ <input |
|
39 |
+ type="text" |
|
40 |
+ value={value} |
|
41 |
+ id={htmlFor} |
|
42 |
+ name={htmlFor} |
|
43 |
+ onChange={onChange}></input> |
|
26 | 44 |
</> |
27 |
- }) |
|
28 |
- .fn; |
|
45 |
+ ); |
|
46 |
+ }).fn; |
|
29 | 47 |
|
30 | 48 |
class Field extends React.Component { |
31 | 49 |
constructor(props) { |
... | ... |
@@ -33,21 +51,26 @@ class Field extends React.Component { |
33 | 51 |
this.state = { editing: false, val: props.value }; |
34 | 52 |
} |
35 | 53 |
click() { |
36 |
- this.setState(() => ({ editing: !this.state.editing, val: this.state.val })); |
|
54 |
+ this.setState(() => ({ |
|
55 |
+ editing: !this.state.editing, |
|
56 |
+ val: this.state.val, |
|
57 |
+ })); |
|
37 | 58 |
} |
38 | 59 |
onChange(e) { |
39 | 60 |
const val = e.target.value; |
40 |
- this.setState(() => ({ editing: true, val })) |
|
61 |
+ this.setState(() => ({ editing: true, val })); |
|
41 | 62 |
} |
42 | 63 |
render() { |
43 | 64 |
const editingProps = {}; |
44 | 65 |
if (this.state.editing) { |
45 | 66 |
editingProps.onChange = this.onChange.bind(this); |
46 | 67 |
} |
47 |
- return <div> |
|
48 |
- <Editable {...this.props} {...editingProps} value={this.state.val} /> |
|
49 |
- <button onClick={this.click.bind(this)}>Toggle</button> |
|
50 |
- </div> |
|
68 |
+ return ( |
|
69 |
+ <div> |
|
70 |
+ <Editable {...this.props} {...editingProps} value={this.state.val} /> |
|
71 |
+ <button onClick={this.click.bind(this)}>Toggle</button> |
|
72 |
+ </div> |
|
73 |
+ ); |
|
51 | 74 |
} |
52 | 75 |
} |
53 | 76 |
|
... | ... |
@@ -56,17 +79,19 @@ ReactDOM.render( |
56 | 79 |
<Field label="the field" htmlFor="the-field" value="foo" /> |
57 | 80 |
<Editable label="bazquuxes" htmlFor="bazquux" value="foo" display /> |
58 | 81 |
<Editable label="bazquuxes" htmlFor="bazquux" value="foo" editable /> |
59 |
- <Receipt items={[ |
|
60 |
- new m.AlcoholicBeverage(11), |
|
61 |
- new m.AlcoholicBeverage(12), |
|
62 |
- new m.AlcoholicBeverage(13), |
|
63 |
- new m.NormalFood(11), |
|
64 |
- new m.NonFood(11), |
|
65 |
- new m.AlcoholicBeverage(10) |
|
66 |
- ]} /> |
|
82 |
+ <Receipt |
|
83 |
+ items={[ |
|
84 |
+ new m.AlcoholicBeverage(11), |
|
85 |
+ new m.AlcoholicBeverage(12), |
|
86 |
+ new m.AlcoholicBeverage(13), |
|
87 |
+ new m.NormalFood(11), |
|
88 |
+ new m.NonFood(11), |
|
89 |
+ new m.AlcoholicBeverage(10), |
|
90 |
+ ]} |
|
91 |
+ /> |
|
67 | 92 |
</div>, |
68 |
- document.querySelector('main'), |
|
93 |
+ document.querySelector("main"), |
|
69 | 94 |
() => { |
70 |
- console.log('rendered'); |
|
95 |
+ console.log("rendered"); |
|
71 | 96 |
} |
72 |
-); |
|
73 | 97 |
\ No newline at end of file |
98 |
+); |
... | ... |
@@ -1,8 +1,8 @@ |
1 |
-import * as m from './Model'; |
|
2 |
-import * as r from './render'; |
|
1 |
+import * as m from "./Model"; |
|
2 |
+import * as r from "./render"; |
|
3 | 3 |
|
4 |
-describe('Model', () => { |
|
5 |
- test('base behavior', () => { |
|
4 |
+describe("Model", () => { |
|
5 |
+ test("base behavior", () => { |
|
6 | 6 |
const Foo = class extends m.Item { |
7 | 7 |
get tax() { |
8 | 8 |
return 1; |
... | ... |
@@ -13,10 +13,9 @@ describe('Model', () => { |
13 | 13 |
expect(new Foo(2).subtotal).toEqual(3); |
14 | 14 |
}); |
15 | 15 |
|
16 |
- test('taxes', () => { |
|
16 |
+ test("taxes", () => { |
|
17 | 17 |
expect(Math.floor(new m.NonFood(1).tax * 10000)).toEqual(825); |
18 | 18 |
expect(Math.floor(new m.AlcoholicBeverage(1).tax * 100)).toEqual(25); |
19 | 19 |
expect(Math.floor(new m.NormalFood(1).tax)).toEqual(0); |
20 |
- }) |
|
20 |
+ }); |
|
21 | 21 |
}); |
22 |
- |
... | ... |
@@ -1,30 +1,30 @@ |
1 | 1 |
{ |
2 |
- "author": "Ed L", |
|
3 |
- "license": "ISC", |
|
4 |
- "name": "react-demo", |
|
5 |
- "version": "1.0.7", |
|
6 |
- "description": "A CLOS-inspired implementation of generic functions", |
|
7 |
- "dependencies": { |
|
8 |
- "react": "^16.5.0", |
|
9 |
- "react-dom": "^16.5.0" |
|
10 |
- }, |
|
11 |
- "devDependencies": { |
|
12 |
- "@babel/cli": "^7.5.0", |
|
13 |
- "@babel/core": "^7.5.0", |
|
14 |
- "@babel/plugin-proposal-object-rest-spread": "^7.5.0", |
|
15 |
- "@babel/plugin-transform-react-jsx": "^7.0.0", |
|
16 |
- "@babel/preset-env": "^7.5.0", |
|
17 |
- "@babel/preset-react": "^7.0.0", |
|
18 |
- "babel-core": "^7.0.0-bridge.0", |
|
19 |
- "eslint-plugin-react": "^7.11.1", |
|
20 |
- "npm": "^6.13.4", |
|
21 |
- "ramda": "*", |
|
22 |
- "rollup": "^1.16.6", |
|
23 |
- "rollup-plugin-babel": "^4.3.2", |
|
24 |
- "rollup-plugin-commonjs": "^10.0.0", |
|
25 |
- "rollup-plugin-livereload": "^1.0.0", |
|
26 |
- "rollup-plugin-node-resolve": "^5.0.0", |
|
27 |
- "rollup-plugin-replace": "^2.0.0", |
|
28 |
- "rollup-plugin-serve": "^1.0.1" |
|
29 |
- } |
|
2 |
+ "author": "Ed L", |
|
3 |
+ "license": "ISC", |
|
4 |
+ "name": "react-demo", |
|
5 |
+ "version": "1.0.7", |
|
6 |
+ "description": "A CLOS-inspired implementation of generic functions", |
|
7 |
+ "dependencies": { |
|
8 |
+ "react": "^16.5.0", |
|
9 |
+ "react-dom": "^16.5.0" |
|
10 |
+ }, |
|
11 |
+ "devDependencies": { |
|
12 |
+ "@babel/cli": "^7.5.0", |
|
13 |
+ "@babel/core": "^7.5.0", |
|
14 |
+ "@babel/plugin-proposal-object-rest-spread": "^7.5.0", |
|
15 |
+ "@babel/plugin-transform-react-jsx": "^7.0.0", |
|
16 |
+ "@babel/preset-env": "^7.5.0", |
|
17 |
+ "@babel/preset-react": "^7.0.0", |
|
18 |
+ "babel-core": "^7.0.0-bridge.0", |
|
19 |
+ "eslint-plugin-react": "^7.11.1", |
|
20 |
+ "npm": "^6.13.4", |
|
21 |
+ "ramda": "*", |
|
22 |
+ "rollup": "^1.16.6", |
|
23 |
+ "rollup-plugin-babel": "^4.3.2", |
|
24 |
+ "rollup-plugin-commonjs": "^10.0.0", |
|
25 |
+ "rollup-plugin-livereload": "^1.0.0", |
|
26 |
+ "rollup-plugin-node-resolve": "^5.0.0", |
|
27 |
+ "rollup-plugin-replace": "^2.0.0", |
|
28 |
+ "rollup-plugin-serve": "^1.0.1" |
|
29 |
+ } |
|
30 | 30 |
} |
... | ... |
@@ -1,59 +1,78 @@ |
1 |
-import React from 'react'; |
|
2 |
-import * as gf from '../src/genfuns'; |
|
3 |
-import * as m from './Model'; |
|
1 |
+import React from "react"; |
|
2 |
+import * as gf from "../src/genfuns"; |
|
3 |
+import * as m from "./Model"; |
|
4 | 4 |
|
5 | 5 |
class Summary { |
6 |
- constructor(wrapper = 'span') { |
|
6 |
+ constructor(wrapper = "span") { |
|
7 | 7 |
this.wrapper = wrapper; |
8 | 8 |
} |
9 | 9 |
} |
10 |
-class Detail { } |
|
10 |
+class Detail {} |
|
11 | 11 |
|
12 |
-const total_receipt = items => items.reduce((acc, item) => acc + item.subtotal, 0); |
|
12 |
+const total_receipt = items => |
|
13 |
+ items.reduce((acc, item) => acc + item.subtotal, 0); |
|
13 | 14 |
const subtotal = items => items.reduce((acc, item) => acc + item.price, 0); |
14 | 15 |
const total_tax = items => items.reduce((acc, item) => acc + item.tax, 0); |
15 | 16 |
|
16 | 17 |
const display_money = amount => amount.toFixed(2); |
17 | 18 |
|
18 | 19 |
const ItemLabel = ({ desc, amount, wrapper, ...restProps }) => |
19 |
- React.createElement(wrapper, restProps, |
|
20 |
- [ |
|
21 |
- <span className="desc" key="0"> {desc}: </span>, |
|
22 |
- <span className="price" key="1">{display_money(amount)}</span> |
|
23 |
- ] |
|
24 |
- ); |
|
20 |
+ React.createElement(wrapper, restProps, [ |
|
21 |
+ <span className="desc" key="0"> |
|
22 |
+ {" "} |
|
23 |
+ {desc}:{" "} |
|
24 |
+ </span>, |
|
25 |
+ <span className="price" key="1"> |
|
26 |
+ {display_money(amount)} |
|
27 |
+ </span>, |
|
28 |
+ ]); |
|
25 | 29 |
|
26 |
-export const Items = gf.defgeneric("Items", "animaltorender") |
|
30 |
+export const Items = gf |
|
31 |
+ .defgeneric("Items", "animaltorender") |
|
27 | 32 |
.primary([gf.Shape("items", "view")], ({ items, view }) => Items(items, view)) |
28 | 33 |
.primary([Array, Summary], (items, view) => ( |
29 | 34 |
<> |
30 |
- <ItemLabel desc="Subtotal" amount={subtotal(items)} wrapper={view.wrapper} /> |
|
35 |
+ <ItemLabel |
|
36 |
+ desc="Subtotal" |
|
37 |
+ amount={subtotal(items)} |
|
38 |
+ wrapper={view.wrapper} |
|
39 |
+ /> |
|
31 | 40 |
<ItemLabel desc="Tax" amount={total_tax(items)} wrapper={view.wrapper} /> |
32 |
- <ItemLabel desc="Total" amount={total_receipt(items)} wrapper={view.wrapper} /> |
|
41 |
+ <ItemLabel |
|
42 |
+ desc="Total" |
|
43 |
+ amount={total_receipt(items)} |
|
44 |
+ wrapper={view.wrapper} |
|
45 |
+ /> |
|
33 | 46 |
</> |
34 | 47 |
)) |
35 | 48 |
.primary([Array, Detail], items => ( |
36 | 49 |
<ul> |
37 |
- {items.map((a, idx) => <Item item={a} key={idx} />, items)} |
|
38 |
- <Items items={items} view={new Summary('li')} /> |
|
50 |
+ {items.map( |
|
51 |
+ (a, idx) => ( |
|
52 |
+ <Item item={a} key={idx} /> |
|
53 |
+ ), |
|
54 |
+ items |
|
55 |
+ )} |
|
56 |
+ <Items items={items} view={new Summary("li")} /> |
|
39 | 57 |
</ul> |
40 |
- )) |
|
41 |
- .fn; |
|
58 |
+ )).fn; |
|
42 | 59 |
|
43 |
-export const Item = gf.defgeneric("Item", "itemtorender") |
|
60 |
+export const Item = gf |
|
61 |
+ .defgeneric("Item", "itemtorender") |
|
44 | 62 |
.primary([gf.Shape("item")], ({ item }) => Item(item)) |
45 | 63 |
.around([m.Item], function (_) { |
46 | 64 |
const [desc, price] = this.call_next_method(); |
47 |
- return <ItemLabel desc={desc} amount={price} wrapper='li' />; |
|
65 |
+ return <ItemLabel desc={desc} amount={price} wrapper="li" />; |
|
48 | 66 |
}) |
49 | 67 |
.primary([m.NonFood], item => ["Non-food Item", item.price]) |
50 | 68 |
.primary([m.NormalFood], item => ["Food", item.price]) |
51 |
- .primary([m.AlcoholicBeverage], item => ["Alcohol", item.price]) |
|
52 |
- .fn; |
|
69 |
+ .primary([m.AlcoholicBeverage], item => ["Alcohol", item.price]).fn; |
|
53 | 70 |
|
54 |
-export const Receipt = ({items}) => ( |
|
71 |
+export const Receipt = ({ items }) => ( |
|
55 | 72 |
<div> |
56 |
- <h1><Items {...{ items }} view={new Summary()} /></h1> |
|
73 |
+ <h1> |
|
74 |
+ <Items {...{ items }} view={new Summary()} /> |
|
75 |
+ </h1> |
|
57 | 76 |
<Items {...{ items }} view={new Detail()} /> |
58 | 77 |
</div> |
59 | 78 |
); |
... | ... |
@@ -1,22 +1,22 @@ |
1 |
-import babel from 'rollup-plugin-babel'; |
|
2 |
-import npm_resolve from 'rollup-plugin-node-resolve'; |
|
3 |
-import commonjs from 'rollup-plugin-commonjs'; |
|
4 |
-import replace from 'rollup-plugin-replace'; |
|
5 |
-import livereload from 'rollup-plugin-livereload'; |
|
1 |
+import babel from "rollup-plugin-babel"; |
|
2 |
+import npm_resolve from "rollup-plugin-node-resolve"; |
|
3 |
+import commonjs from "rollup-plugin-commonjs"; |
|
4 |
+import replace from "rollup-plugin-replace"; |
|
5 |
+import livereload from "rollup-plugin-livereload"; |
|
6 | 6 |
|
7 | 7 |
export default { |
8 |
- input: 'main.js', |
|
8 |
+ input: "main.js", |
|
9 | 9 |
output: { |
10 |
- file: 'dist/main.o.js', |
|
10 |
+ file: "dist/main.o.js", |
|
11 | 11 |
browser: true, |
12 | 12 |
sourcemap: true, |
13 |
- format: 'iife', |
|
13 |
+ format: "iife", |
|
14 | 14 |
}, |
15 | 15 |
plugins: [ |
16 | 16 |
//livereload({watch: 'dist'}), |
17 |
- replace({'process.env.NODE_ENV': JSON.stringify('development')}), |
|
18 |
- babel({ exclude: 'node_modules/**' }), |
|
17 |
+ replace({ "process.env.NODE_ENV": JSON.stringify("development") }), |
|
18 |
+ babel({ exclude: "node_modules/**" }), |
|
19 | 19 |
npm_resolve({ module: true, jsnext: true, main: true, browser: true }), |
20 | 20 |
commonjs(), |
21 |
- ] |
|
21 |
+ ], |
|
22 | 22 |
}; |
... | ... |
@@ -1,63 +1,73 @@ |
1 | 1 |
/* eslint-disable */ |
2 | 2 |
|
3 | 3 |
window.devtoolsFormatters = [ |
4 |
- { |
|
5 |
- header(obj, config) { |
|
6 |
- if (config && config.genfunFormatter) { |
|
7 |
- return ["div", {}, config.key]; |
|
8 |
- } else if (!(obj.gf || obj instanceof GenericFunction)) { |
|
9 |
- return null; |
|
10 |
- } else if (obj.gf) { |
|
11 |
- const args = obj.gf.lambda_list.join(', '); |
|
12 |
- const method_count = obj.gf.methods.length |
|
13 |
- return [ |
|
14 |
- 'div', {}, |
|
15 |
- `GenericFunction lambda: ${obj.gf.name}(${args}) ` |
|
16 |
- + `[${method_count} methods]` |
|
17 |
- ]; |
|
4 |
+ { |
|
5 |
+ header(obj, config) { |
|
6 |
+ if (config && config.genfunFormatter) { |
|
7 |
+ return ["div", {}, config.key]; |
|
8 |
+ } else if (!(obj.gf || obj instanceof GenericFunction)) { |
|
9 |
+ return null; |
|
10 |
+ } else if (obj.gf) { |
|
11 |
+ const args = obj.gf.lambda_list.join(", "); |
|
12 |
+ const method_count = obj.gf.methods.length; |
|
13 |
+ return [ |
|
14 |
+ "div", |
|
15 |
+ {}, |
|
16 |
+ `GenericFunction lambda: ${obj.gf.name}(${args}) ` + |
|
17 |
+ `[${method_count} methods]`, |
|
18 |
+ ]; |
|
19 |
+ } else { |
|
20 |
+ const args = obj.lambda_list.join(", "); |
|
21 |
+ const method_count = obj.methods.length; |
|
22 |
+ return [ |
|
23 |
+ "div", |
|
24 |
+ {}, |
|
25 |
+ `#<GenericFunction: ${obj.name}(${args}) [${method_count} methods]>`, |
|
26 |
+ ]; |
|
27 |
+ } |
|
28 |
+ }, |
|
29 |
+ hasBody(obj) { |
|
30 |
+ return obj instanceof GenericFunction || obj instanceof StandardMethod; |
|
31 |
+ }, |
|
32 |
+ body(obj, config) { |
|
33 |
+ if (!(obj instanceof GenericFunction || obj instanceof StandardMethod)) { |
|
34 |
+ return null; |
|
35 |
+ } else if (obj instanceof StandardMethod) { |
|
36 |
+ return ["div", { style: "margin-left: 2em" }].concat( |
|
37 |
+ Object.keys(obj).map(key => { |
|
38 |
+ if (obj[key] instanceof String) { |
|
39 |
+ return ["div", {}, `${key}: ${obj[key]},`]; |
|
18 | 40 |
} else { |
19 |
- const args = obj.lambda_list.join(', '); |
|
20 |
- const method_count = obj.methods.length |
|
21 |
- return [ |
|
22 |
- 'div', {}, |
|
23 |
- `#<GenericFunction: ${obj.name}(${args}) [${method_count} methods]>` |
|
24 |
- ]; |
|
25 |
- } |
|
26 |
- }, |
|
27 |
- hasBody(obj) {return obj instanceof GenericFunction || obj instanceof StandardMethod;}, |
|
28 |
- body(obj, config) { |
|
29 |
- if (! (obj instanceof GenericFunction || obj instanceof StandardMethod) ) { |
|
30 |
- return null; |
|
31 |
- } else if ( obj instanceof StandardMethod ) { |
|
32 |
- return ["div", {style: 'margin-left: 2em'}].concat( |
|
33 |
- Object.keys(obj).map( |
|
34 |
- key => { |
|
35 |
- if (obj[key] instanceof String) { |
|
36 |
- return ["div", {}, `${key}: ${obj[key]},`]; |
|
37 |
- } else { |
|
38 |
- return ["div", {}, `${key}: `, ["object", {object: obj[key]}], ',']; |
|
39 |
- } |
|
40 |
- } |
|
41 |
- ) |
|
42 |
- ); |
|
41 |
+ return [ |
|
42 |
+ "div", |
|
43 |
+ {}, |
|
44 |
+ `${key}: `, |
|
45 |
+ ["object", { object: obj[key] }], |
|
46 |
+ ",", |
|
47 |
+ ]; |
|
43 | 48 |
} |
49 |
+ }) |
|
50 |
+ ); |
|
51 |
+ } |
|
44 | 52 |
|
45 |
- const children = obj.methods.map( |
|
46 |
- (method,idx) => { |
|
47 |
- const child = [ |
|
48 |
- "object", { |
|
49 |
- object: method, |
|
50 |
- config: { |
|
51 |
- genfunFormatter: true, |
|
52 |
- key: `#<StandardMethod ${method.qualifiers.map(x => ':'+x.toString().slice(7,-1)).join(' ')} (${method.specializers.map(x => x.name.toString())})>`, |
|
53 |
- }, |
|
54 |
- }, |
|
55 |
- ]; |
|
56 |
- return ["div", {style: `margin-left: 2em;`}, |
|
57 |
- child] |
|
58 |
- } |
|
59 |
- ); |
|
60 |
- return ["div", {}].concat(children); |
|
61 |
- } |
|
62 |
- } |
|
53 |
+ const children = obj.methods.map((method, idx) => { |
|
54 |
+ const child = [ |
|
55 |
+ "object", |
|
56 |
+ { |
|
57 |
+ object: method, |
|
58 |
+ config: { |
|
59 |
+ genfunFormatter: true, |
|
60 |
+ key: `#<StandardMethod ${method.qualifiers |
|
61 |
+ .map(x => ":" + x.toString().slice(7, -1)) |
|
62 |
+ .join(" ")} (${method.specializers.map(x => |
|
63 |
+ x.name.toString() |
|
64 |
+ )})>`, |
|
65 |
+ }, |
|
66 |
+ }, |
|
67 |
+ ]; |
|
68 |
+ return ["div", { style: `margin-left: 2em;` }, child]; |
|
69 |
+ }); |
|
70 |
+ return ["div", {}].concat(children); |
|
71 |
+ }, |
|
72 |
+ }, |
|
63 | 73 |
]; |
... | ... |
@@ -16,7 +16,7 @@ const Method = { |
16 | 16 |
qualifiers: [], |
17 | 17 |
specializers: [], |
18 | 18 |
body: () => {}, |
19 |
- generic_function: null |
|
19 |
+ generic_function: null, |
|
20 | 20 |
}; |
21 | 21 |
|
22 | 22 |
let genfun_prototype = { |
... | ... |
@@ -41,15 +41,15 @@ let genfun_prototype = { |
41 | 41 |
}, |
42 | 42 |
get fn() { |
43 | 43 |
const gf = this; |
44 |
- const lambda = function() { |
|
44 |
+ const lambda = function () { |
|
45 | 45 |
return apply_generic_function(gf, [].slice.call(arguments)); |
46 | 46 |
}.bind(gf); |
47 | 47 |
return Object.defineProperties(lambda, { |
48 | 48 |
name: { value: gf.name }, |
49 | 49 |
lambda_list: { value: gf.lambda_list }, |
50 |
- gf: { value: gf } |
|
50 |
+ gf: { value: gf }, |
|
51 | 51 |
}); |
52 |
- } |
|
52 |
+ }, |
|
53 | 53 |
}; |
54 | 54 |
|
55 | 55 |
/** |
... | ... |
@@ -146,7 +146,7 @@ function method_more_specific_p(m1, m2 /*, required_classes*/) { |
146 | 146 |
let result = null; |
147 | 147 |
for (let [spec1, spec2] of m1specializers.map((el, idx) => [ |
148 | 148 |
el, |
149 |
- m2specializers[idx] |
|
149 |
+ m2specializers[idx], |
|
150 | 150 |
])) { |
151 | 151 |
if (spec1 !== spec2) { |
152 | 152 |
result = sub_specializer_p(spec1, spec2); |
... | ... |
@@ -168,7 +168,7 @@ export function sub_specializer_p(c1, c2) { |
168 | 168 |
} |
169 | 169 |
|
170 | 170 |
const idS = Symbol.for("id"); |
171 |
-Object.prototype[idS] = function() { |
|
171 |
+Object.prototype[idS] = function () { |
|
172 | 172 |
return this; |
173 | 173 |
}; |
174 | 174 |
|
... | ... |
@@ -179,7 +179,7 @@ Specializer.prototype = { |
179 | 179 |
}, |
180 | 180 |
super_of(_obj) { |
181 | 181 |
return false; |
182 |
- } |
|
182 |
+ }, |
|
183 | 183 |
}; |
184 | 184 |
|
185 | 185 |
function isSuperset(superset, subset) { |
... | ... |
@@ -218,7 +218,7 @@ Shape.prototype = Object.assign(new Specializer(), { |
218 | 218 |
} else { |
219 | 219 |
return isSuperset(spec.keys, this.keys); |
220 | 220 |
} |
221 |
- } |
|
221 |
+ }, |
|
222 | 222 |
}); |
223 | 223 |
|
224 | 224 |
// function trace(fun) { |
... | ... |
@@ -354,7 +354,7 @@ function apply_methods(gf, args, applicable_methods) { |
354 | 354 |
afters.reverse(); |
355 | 355 |
|
356 | 356 |
const main_call = Object.defineProperty( |
357 |
- function() { |
|
357 |
+ function () { |
|
358 | 358 |
if (primaries.length === 0) { |
359 | 359 |
throw new NoPrimaryMethodError(`No primary method for ${gf.name}`); |
360 | 360 |
} |
... | ... |
@@ -409,7 +409,7 @@ function apply_method(method, args, next_methods) { |
409 | 409 |
|
410 | 410 |
get next_method_p() { |
411 | 411 |
return next_methods.length !== 0; |
412 |
- } |
|
412 |
+ }, |
|
413 | 413 |
}; |
414 | 414 |
|
415 | 415 |
return method.body |
... | ... |
@@ -1,303 +1,361 @@ |
1 |
-import * as uut from './genfuns'; |
|
2 |
-import { fail } from 'assert'; |
|
3 |
- |
|
4 |
-describe('matches_specializer', () => { |
|
5 |
- test('works in expected cases', () => { |
|
6 |
- function AThing() { } |
|
7 |
- const an_instance = new AThing(); |
|
8 |
- |
|
9 |
- expect(uut.matches_specializer(an_instance, AThing)).toBeTruthy(); |
|
10 |
- expect(uut.matches_specializer(an_instance, String)).toBeFalsy(); |
|
11 |
- expect(uut.matches_specializer(an_instance, Object)).toBeTruthy(); |
|
12 |
- |
|
13 |
- expect(uut.matches_specializer([], Array)).toBeTruthy(); |
|
14 |
- expect(uut.matches_specializer([], Object)).toBeTruthy(); |
|
15 |
- expect(uut.matches_specializer([], Number)).toBeFalsy(); |
|
16 |
- |
|
17 |
- function Foo() { } |
|
18 |
- Foo.prototype = Object.create(null); |
|
19 |
- const inst = new Foo(); |
|
20 |
- expect(uut.matches_specializer(inst, Foo)).toBeTruthy(); |
|
21 |
- expect(uut.matches_specializer(inst, Object)).toBeFalsy(); |
|
22 |
- |
|
23 |
- expect(uut.matches_specializer({ a: 1 }, uut.Shape('a'))).toBeTruthy(); |
|
24 |
- expect(uut.matches_specializer({ a: 1, b: 2 }, uut.Shape('a'))).toBeTruthy(); |
|
25 |
- expect(uut.matches_specializer({ b: 2 }, uut.Shape('a'))).toBeFalsy(); |
|
26 |
- |
|
27 |
- expect(uut.matches_specializer({ a: 1, b: 2, c: 3 }, uut.Shape('a', 'b', 'c'))).toBeTruthy(); |
|
28 |
- expect(uut.matches_specializer({ a: 1, b: 2, c: 3, d: 4 }, uut.Shape('a', 'b', 'c'))).toBeTruthy(); |
|
29 |
- expect(uut.matches_specializer({ a: 1, c: 3 }, uut.Shape('a', 'b', 'c'))).toBeFalsy(); |
|
30 |
- expect(uut.matches_specializer({ c: 3 }, uut.Shape('a', 'b', 'c'))).toBeFalsy(); |
|
31 |
- expect(uut.matches_specializer({ d: 3 }, uut.Shape('a', 'b', 'c'))).toBeFalsy(); |
|
32 |
- }); |
|
33 |
- |
|
34 |
- test('null behavior', () => { |
|
35 |
- expect(uut.matches_specializer(null, null)).toBeTruthy(); |
|
36 |
- expect(uut.matches_specializer(null, Number)).toBeFalsy(); |
|
37 |
- expect(uut.matches_specializer(null, String)).toBeFalsy(); |
|
38 |
- expect(uut.matches_specializer(null, Object)).toBeFalsy(); |
|
39 |
- }); |
|
40 |
- |
|
41 |
- test('undefined (the value) behavior', () => { |
|
42 |
- expect(uut.matches_specializer(undefined, undefined)).toBeTruthy(); |
|
43 |
- expect(uut.matches_specializer(undefined, Number)).toBeFalsy(); |
|
44 |
- expect(uut.matches_specializer(undefined, String)).toBeFalsy(); |
|
45 |
- expect(uut.matches_specializer(undefined, Object)).toBeFalsy(); |
|
46 |
- }); |
|
47 |
- |
|
48 |
- test('works for numbers', () => { |
|
49 |
- expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy(); |
|
50 |
- expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy(); |
|
51 |
- expect(uut.matches_specializer(new Number(1), String)).toBeFalsy(); |
|
52 |
- |
|
53 |
- expect(uut.matches_specializer(1, Number)).toBeTruthy(); |
|
54 |
- expect(uut.matches_specializer(1, Object)).toBeTruthy(); |
|
55 |
- expect(uut.matches_specializer(1, String)).toBeFalsy(); |
|
56 |
- }); |
|
57 |
- |
|
58 |
- test('handles strings', () => { |
|
59 |
- expect(uut.matches_specializer(new String("foobar"), String)).toBeTruthy(); |
|
60 |
- expect(uut.matches_specializer(new String("foobar"), Object)).toBeTruthy(); |
|
61 |
- |
|
62 |
- expect(uut.matches_specializer("1", String)).toBeTruthy(); |
|
63 |
- expect(uut.matches_specializer("1", Object)).toBeTruthy(); |
|
64 |
- expect(uut.matches_specializer("1", Number)).toBeFalsy(); |
|
65 |
- }) |
|
1 |
+import * as uut from "./genfuns"; |
|
2 |
+import { fail } from "assert"; |
|
3 |
+ |
|
4 |
+describe("matches_specializer", () => { |
|
5 |
+ test("works in expected cases", () => { |
|
6 |
+ function AThing() {} |
|
7 |
+ const an_instance = new AThing(); |
|
8 |
+ |
|
9 |
+ expect(uut.matches_specializer(an_instance, AThing)).toBeTruthy(); |
|
10 |
+ expect(uut.matches_specializer(an_instance, String)).toBeFalsy(); |
|
11 |
+ expect(uut.matches_specializer(an_instance, Object)).toBeTruthy(); |
|
12 |
+ |
|
13 |
+ expect(uut.matches_specializer([], Array)).toBeTruthy(); |
|
14 |
+ expect(uut.matches_specializer([], Object)).toBeTruthy(); |
|
15 |
+ expect(uut.matches_specializer([], Number)).toBeFalsy(); |
|
16 |
+ |
|
17 |
+ function Foo() {} |
|
18 |
+ Foo.prototype = Object.create(null); |
|
19 |
+ const inst = new Foo(); |
|
20 |
+ expect(uut.matches_specializer(inst, Foo)).toBeTruthy(); |
|
21 |
+ expect(uut.matches_specializer(inst, Object)).toBeFalsy(); |
|
22 |
+ |
|
23 |
+ expect(uut.matches_specializer({ a: 1 }, uut.Shape("a"))).toBeTruthy(); |
|
24 |
+ expect( |
|
25 |
+ uut.matches_specializer({ a: 1, b: 2 }, uut.Shape("a")) |
|
26 |
+ ).toBeTruthy(); |
|
27 |
+ expect(uut.matches_specializer({ b: 2 }, uut.Shape("a"))).toBeFalsy(); |
|
28 |
+ |
|
29 |
+ expect( |
|
30 |
+ uut.matches_specializer({ a: 1, b: 2, c: 3 }, uut.Shape("a", "b", "c")) |
|
31 |
+ ).toBeTruthy(); |
|
32 |
+ expect( |
|
33 |
+ uut.matches_specializer( |
|
34 |
+ { a: 1, b: 2, c: 3, d: 4 }, |
|
35 |
+ uut.Shape("a", "b", "c") |
|
36 |
+ ) |
|
37 |
+ ).toBeTruthy(); |
|
38 |
+ expect( |
|
39 |
+ uut.matches_specializer({ a: 1, c: 3 }, uut.Shape("a", "b", "c")) |
|
40 |
+ ).toBeFalsy(); |
|
41 |
+ expect( |
|
42 |
+ uut.matches_specializer({ c: 3 }, uut.Shape("a", "b", "c")) |
|
43 |
+ ).toBeFalsy(); |
|
44 |
+ expect( |
|
45 |
+ uut.matches_specializer({ d: 3 }, uut.Shape("a", "b", "c")) |
|
46 |
+ ).toBeFalsy(); |
|
47 |
+ }); |
|
48 |
+ |
|
49 |
+ test("null behavior", () => { |
|
50 |
+ expect(uut.matches_specializer(null, null)).toBeTruthy(); |
|
51 |
+ expect(uut.matches_specializer(null, Number)).toBeFalsy(); |
|
52 |
+ expect(uut.matches_specializer(null, String)).toBeFalsy(); |
|
53 |
+ expect(uut.matches_specializer(null, Object)).toBeFalsy(); |
|
54 |
+ }); |
|
55 |
+ |
|
56 |
+ test("undefined (the value) behavior", () => { |
|
57 |
+ expect(uut.matches_specializer(undefined, undefined)).toBeTruthy(); |
|
58 |
+ expect(uut.matches_specializer(undefined, Number)).toBeFalsy(); |
|
59 |
+ expect(uut.matches_specializer(undefined, String)).toBeFalsy(); |
|
60 |
+ expect(uut.matches_specializer(undefined, Object)).toBeFalsy(); |
|
61 |
+ }); |
|
62 |
+ |
|
63 |
+ test("works for numbers", () => { |
|
64 |
+ expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy(); |
|
65 |
+ expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy(); |
|
66 |
+ expect(uut.matches_specializer(new Number(1), String)).toBeFalsy(); |
|
67 |
+ |
|
68 |
+ expect(uut.matches_specializer(1, Number)).toBeTruthy(); |
|
69 |
+ expect(uut.matches_specializer(1, Object)).toBeTruthy(); |
|
70 |
+ expect(uut.matches_specializer(1, String)).toBeFalsy(); |
|
71 |
+ }); |
|
72 |
+ |
|
73 |
+ test("handles strings", () => { |
|
74 |
+ expect(uut.matches_specializer(new String("foobar"), String)).toBeTruthy(); |
|
75 |
+ expect(uut.matches_specializer(new String("foobar"), Object)).toBeTruthy(); |
|
76 |
+ |
|
77 |
+ expect(uut.matches_specializer("1", String)).toBeTruthy(); |
|
78 |
+ expect(uut.matches_specializer("1", Object)).toBeTruthy(); |
|
79 |
+ expect(uut.matches_specializer("1", Number)).toBeFalsy(); |
|
80 |
+ }); |
|
66 | 81 |
}); |
67 | 82 |
|
68 |
-describe('defgeneric', () => { |
|
69 |
- test('methods get called appropriately', () => { |
|
70 |
- expect( |
|
71 |
- uut.defgeneric("testing1", "a", "b") |
|
72 |
- .primary([Object, Object], (_, __) => 1) |
|
73 |
- .fn(1, 2) |
|
74 |
- ).toEqual(1); |
|
75 |
- |
|
76 |
- try { |
|
77 |
- uut.defgeneric("foobar", "a") |
|
78 |
- .primary([String], function (a) { }) |
|
79 |
- .fn({}); |
|
80 |
- fail(); |
|
81 |
- } catch (err) { |
|
82 |
- expect(err).toBeInstanceOf(uut.NoApplicableMethodError); |
|
83 |
- } |
|
84 |
- |
|
85 |
- expect( |
|
86 |
- uut.defgeneric("testing1", "a", "b") |
|
87 |
- .primary([Number, Number], (_, __) => 1) |
|
88 |
- .fn(1, 2) |
|
89 |
- ).toEqual(1); |
|
90 |
- |
|
91 |
- expect( |
|
92 |
- uut.defgeneric("testing1", "a", "b") |
|
93 |
- .primary([Number, Number], (_, __) => 2) |
|
94 |
- .primary([String, String], (_, __) => 1) |
|
95 |
- .fn("1", "2") |
|
96 |
- ).toEqual(1); |
|
97 |
- |
|
98 |
- let firstCounts = 0; |
|
99 |
- expect( |
|
100 |
- uut.defgeneric("testing1", "a", "b") |
|
101 |
- .primary([Number, Number], (_, __) => firstCounts += 1) |
|
102 |
- .primary([String, String], (_, __) => firstCounts += 1) |
|
103 |
- .fn("1", "2") |
|
104 |
- ).toEqual(1); |
|
105 |
- expect(firstCounts).toEqual(1); |
|
106 |
- |
|
107 |
- let secondCounts = 0; |
|
108 |
- expect( |
|
109 |
- uut.defgeneric("testing1", "a", "b") |
|
110 |
- .primary([Object, Object], (_, __) => secondCounts += 1) |
|
111 |
- .primary([String, String], (_, __) => secondCounts += 1) |
|
112 |
- .fn("1", "2") |
|
113 |
- ).toEqual(1); |
|
114 |
- expect(secondCounts).toEqual(1); |
|
115 |
- |
|
116 |
- let thirdCounts = 0; |
|
117 |
- expect( |
|
118 |
- uut.defgeneric("testing1", "a", "b") |
|
119 |
- .before([Object, Object], (_, __) => thirdCounts += 1) |
|
120 |
- .primary([String, String], (_, __) => 'hi') |
|
121 |
- .after([Object, String], (_, __) => thirdCounts += 1) |
|
122 |
- .fn("1", "2") |
|
123 |
- ).toEqual('hi'); |
|
124 |
- expect(thirdCounts).toEqual(2); |
|
125 |
- |
|
126 |
- expect( |
|
127 |
- uut.defgeneric("foobar", "a") |
|
128 |
- .primary([Object], function (a) { |
|
129 |
- return 1 |
|
130 |
- }) |
|
131 |
- .primary([String], function (a) { |
|
132 |
- return 2 |
|
133 |
- }) |
|
134 |
- .fn("foobar")) |
|
135 |
- .toEqual(2); |
|
136 |
- }); |
|
137 |
- |
|
138 |
- test('next-method-p works', () => { |
|
139 |
- expect.assertions(3); |
|
140 |
- |
|
141 |
- uut.defgeneric("foobar", "a") |
|
142 |
- .primary([Object], function (a) { |
|
143 |
- expect(this.next_method_p).toBe(false); |
|
144 |
- }) |
|
145 |
- .fn({}); |
|
146 |
- |
|
147 |
- uut.defgeneric("foobar", "a") |
|
148 |
- .primary([Object], function (a) { |
|
149 |
- expect(this.next_method_p).toBe(false); |
|
150 |
- }) |
|
151 |
- .primary([String], function (a) { |
|
152 |
- expect(this.next_method_p).toBe(true); |
|
153 |
- }) |
|
154 |
- .fn("foobar"); |
|
155 |
- |
|
156 |
- uut.defgeneric("foobar", "a") |
|
157 |
- .primary([Object], function (a) { |
|
158 |
- expect(this.next_method_p).toBe(false); |
|
159 |
- }) |
|
160 |
- .primary([String], function (a) { |
|
161 |
- expect(this.next_method_p).toBe(true); |
|
162 |
- }) |
|
163 |
- .fn(1); |
|
164 |
- |
|
165 |
- }); |
|
166 |
- |
|
167 |
- test('call-next-method works', () => { |
|
168 |
- try { |
|
169 |
- uut.defgeneric("foobar", "a") |
|
170 |
- .primary([Object], function (a) { |
|
171 |
- this.call_next_method(); |
|
172 |
- }) |
|
173 |
- .primary([String], function (a) { |
|
174 |
- return 1; |
|
175 |
- }) |
|
176 |
- .fn({}) |
|
177 |
- fail(); |
|
178 |
- } catch (err) { |
|
179 |
- expect(err).toBeInstanceOf(uut.NoNextMethodError); |
|
180 |
- } |
|
181 |
- |
|
182 |
- expect( |
|
183 |
- uut.defgeneric("foobar", "a") |
|
184 |
- .primary([Object], function (a) { |
|
185 |
- return 1; |
|
186 |
- }) |
|
187 |
- .primary([String], function (a) { |
|
188 |
- return this.call_next_method(); |
|
189 |
- }) |
|
190 |
- .fn("foobar") |
|
191 |
- ).toEqual(1); |
|
192 |
- |
|
193 |
- expect( |
|
194 |
- uut.defgeneric("foobar", "a", "b") |
|
195 |
- .primary([String, String], function (a, b) { |
|
196 |
- return `1${this.call_next_method()}`; |
|
197 |
- }) |
|
198 |
- .primary([Object, String], function (a, b) { |
|
199 |
- return `3${this.call_next_method()}`; |
|
200 |
- }) |
|
201 |
- .primary([String, Object], function (a, b) { |
|
202 |
- return `2${this.call_next_method()}`; |
|
203 |
- }) |
|
204 |
- .primary([Object, Object], function (a, b) { |
|
205 |
- return `4`; |
|
206 |
- }).fn("a", "b") |
|
207 |
- ).toEqual("1234"); |
|
208 |
- |
|
209 |
- try { |
|
210 |
- uut.defgeneric("foobar", "a") |
|
211 |
- .primary([Object], function (a) { |
|
212 |
- this.call_next_method(); |
|
213 |
- }) |
|
214 |
- .fn({}); |
|
215 |
- fail(); |
|
216 |
- } catch (err) { |
|
217 |
- expect(err).toBeInstanceOf(uut.NoNextMethodError); |
|
218 |
- } |
|
219 |
- |
|
220 |
- }); |
|
83 |
+describe("defgeneric", () => { |
|
84 |
+ test("methods get called appropriately", () => { |
|
85 |
+ expect( |
|
86 |
+ uut |
|
87 |
+ .defgeneric("testing1", "a", "b") |
|
88 |
+ .primary([Object, Object], (_, __) => 1) |
|
89 |
+ .fn(1, 2) |
|
90 |
+ ).toEqual(1); |
|
91 |
+ |
|
92 |
+ try { |
|
93 |
+ uut |
|
94 |
+ .defgeneric("foobar", "a") |
|
95 |
+ .primary([String], function (a) {}) |
|
96 |
+ .fn({}); |
|
97 |
+ fail(); |
|
98 |
+ } catch (err) { |
|
99 |
+ expect(err).toBeInstanceOf(uut.NoApplicableMethodError); |
|
100 |
+ } |
|
101 |
+ |
|
102 |
+ expect( |
|
103 |
+ uut |
|
104 |
+ .defgeneric("testing1", "a", "b") |
|
105 |
+ .primary([Number, Number], (_, __) => 1) |
|
106 |
+ .fn(1, 2) |
|
107 |
+ ).toEqual(1); |
|
108 |
+ |
|
109 |
+ expect( |
|
110 |
+ uut |
|
111 |
+ .defgeneric("testing1", "a", "b") |
|
112 |
+ .primary([Number, Number], (_, __) => 2) |
|
113 |
+ .primary([String, String], (_, __) => 1) |
|
114 |
+ .fn("1", "2") |
|
115 |
+ ).toEqual(1); |
|
116 |
+ |
|
117 |
+ let firstCounts = 0; |
|
118 |
+ expect( |
|
119 |
+ uut |
|
120 |
+ .defgeneric("testing1", "a", "b") |
|
121 |
+ .primary([Number, Number], (_, __) => (firstCounts += 1)) |
|
122 |
+ .primary([String, String], (_, __) => (firstCounts += 1)) |
|
123 |
+ .fn("1", "2") |
|
124 |
+ ).toEqual(1); |
|
125 |
+ expect(firstCounts).toEqual(1); |
|
126 |
+ |
|
127 |
+ let secondCounts = 0; |
|
128 |
+ expect( |
|
129 |
+ uut |
|
130 |
+ .defgeneric("testing1", "a", "b") |
|
131 |
+ .primary([Object, Object], (_, __) => (secondCounts += 1)) |
|
132 |
+ .primary([String, String], (_, __) => (secondCounts += 1)) |
|
133 |
+ .fn("1", "2") |
|
134 |
+ ).toEqual(1); |
|
135 |
+ expect(secondCounts).toEqual(1); |
|
136 |
+ |
|
137 |
+ let thirdCounts = 0; |
|
138 |
+ expect( |
|
139 |
+ uut |
|
140 |
+ .defgeneric("testing1", "a", "b") |
|
141 |
+ .before([Object, Object], (_, __) => (thirdCounts += 1)) |
|
142 |
+ .primary([String, String], (_, __) => "hi") |
|
143 |
+ .after([Object, String], (_, __) => (thirdCounts += 1)) |
|
144 |
+ .fn("1", "2") |
|
145 |
+ ).toEqual("hi"); |
|
146 |
+ expect(thirdCounts).toEqual(2); |
|
147 |
+ |
|
148 |
+ expect( |
|
149 |
+ uut |
|
150 |
+ .defgeneric("foobar", "a") |
|
151 |
+ .primary([Object], function (a) { |
|
152 |
+ return 1; |
|
153 |
+ }) |
|
154 |
+ .primary([String], function (a) { |
|
155 |
+ return 2; |
|
156 |
+ }) |
|
157 |
+ .fn("foobar") |
|
158 |
+ ).toEqual(2); |
|
159 |
+ }); |
|
160 |
+ |
|
161 |
+ test("next-method-p works", () => { |
|
162 |
+ expect.assertions(3); |
|
163 |
+ |
|
164 |
+ uut |
|
165 |
+ .defgeneric("foobar", "a") |
|
166 |
+ .primary([Object], function (a) { |
|
167 |
+ expect(this.next_method_p).toBe(false); |
|
168 |
+ }) |
|
169 |
+ .fn({}); |
|
170 |
+ |
|
171 |
+ uut |
|
172 |
+ .defgeneric("foobar", "a") |
|
173 |
+ .primary([Object], function (a) { |
|
174 |
+ expect(this.next_method_p).toBe(false); |
|
175 |
+ }) |
|
176 |
+ .primary([String], function (a) { |
|
177 |
+ expect(this.next_method_p).toBe(true); |
|
178 |
+ }) |
|
179 |
+ .fn("foobar"); |
|
180 |
+ |
|
181 |
+ uut |
|
182 |
+ .defgeneric("foobar", "a") |
|
183 |
+ .primary([Object], function (a) { |
|
184 |
+ expect(this.next_method_p).toBe(false); |
|
185 |
+ }) |
|
186 |
+ .primary([String], function (a) { |
|
187 |
+ expect(this.next_method_p).toBe(true); |
|
188 |
+ }) |
|
189 |
+ .fn(1); |
|
190 |
+ }); |
|
191 |
+ |
|
192 |
+ test("call-next-method works", () => { |
|
193 |
+ try { |
|
194 |
+ uut |
|
195 |
+ .defgeneric("foobar", "a") |
|
196 |
+ .primary([Object], function (a) { |
|
197 |
+ this.call_next_method(); |
|
198 |
+ }) |
|
199 |
+ .primary([String], function (a) { |
|
200 |
+ return 1; |
|
201 |
+ }) |
|
202 |
+ .fn({}); |
|
203 |
+ fail(); |
|
204 |
+ } catch (err) { |
|
205 |
+ expect(err).toBeInstanceOf(uut.NoNextMethodError); |
|
206 |
+ } |
|
207 |
+ |
|
208 |
+ expect( |
|
209 |
+ uut |
|
210 |
+ .defgeneric("foobar", "a") |
|
211 |
+ .primary([Object], function (a) { |
|
212 |
+ return 1; |
|
213 |
+ }) |
|
214 |
+ .primary([String], function (a) { |
|
215 |
+ return this.call_next_method(); |
|
216 |
+ }) |
|
217 |
+ .fn("foobar") |
|
218 |
+ ).toEqual(1); |
|
219 |
+ |
|
220 |
+ expect( |
|
221 |
+ uut |
|
222 |
+ .defgeneric("foobar", "a", "b") |
|
223 |
+ .primary([String, String], function (a, b) { |
|
224 |
+ return `1${this.call_next_method()}`; |
|
225 |
+ }) |
|
226 |
+ .primary([Object, String], function (a, b) { |
|
227 |
+ return `3${this.call_next_method()}`; |
|
228 |
+ }) |
|
229 |
+ .primary([String, Object], function (a, b) { |
|
230 |
+ return `2${this.call_next_method()}`; |
|
231 |
+ }) |
|
232 |
+ .primary([Object, Object], function (a, b) { |
|
233 |
+ return `4`; |
|
234 |
+ }) |
|
235 |
+ .fn("a", "b") |
|
236 |
+ ).toEqual("1234"); |
|
237 |
+ |
|
238 |
+ try { |
|
239 |
+ uut |
|
240 |
+ .defgeneric("foobar", "a") |
|
241 |
+ .primary([Object], function (a) { |
|
242 |
+ this.call_next_method(); |
|
243 |
+ }) |
|
244 |
+ .fn({}); |
|
245 |
+ fail(); |
|
246 |
+ } catch (err) { |
|
247 |
+ expect(err).toBeInstanceOf(uut.NoNextMethodError); |
|
248 |
+ } |
|
249 |
+ }); |
|
221 | 250 |
}); |
222 | 251 |
|
223 |
-describe('custom specializers', () => { |
|
224 |
- test('Shape works', () => { |
|
225 |
- expect(uut.defgeneric("foobar", "a") |
|
226 |
- .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b) |
|
227 |
- .primary([Object], _ => null) |
|
228 |
- .fn({ a: 1, b: 2 })) |
|
229 |
- .toEqual(3); |
|
230 |
- |
|
231 |
- expect(uut.defgeneric("foobar", "a") |
|
232 |
- .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b) |
|
233 |
- .primary([Object], _ => null) |
|
234 |
- .fn({ a: 1, b: 2, c: 3 })) |
|
235 |
- .toEqual(3); |
|
236 |
- |
|
237 |
- expect(uut.defgeneric("foobar", "a") |
|
238 |
- .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b) |
|
239 |
- .primary([Object], _ => null) |
|
240 |
- .fn({ a: 1 })) |
|
241 |
- .toEqual(null); |
|
242 |
- |
|
243 |
- expect(uut.defgeneric("foobar", "a") |
|
244 |
- .primary([uut.Shape(['a', 1], 'b')], ({ a, b }) => a + b) |
|
245 |
- .primary([Object], _ => null) |
|
246 |
- .fn({ a: 1, b: 3 })) |
|
247 |
- .toEqual(4); |
|
248 |
- |
|
249 |
- expect(uut.defgeneric("foobar", "a") |
|
250 |
- .primary([uut.Shape(['a', null], 'b')], ({ a, b }) => b) |
|
251 |
- .primary([Object], _ => null) |
|
252 |
- .fn({ a: null, b: 3 })) |
|
253 |
- .toEqual(3); |
|
254 |
- |
|
255 |
- expect(uut.defgeneric("foobar", "a") |
|
256 |
- .primary([uut.Shape(['a', undefined], 'b')], ({ a, b }) => b) |
|
257 |
- .primary([Object], _ => null) |
|
258 |
- .fn({ b: 5 })) |
|
259 |
- .toEqual(null); //undefined is not a permissible default: treated as if the key is missing |
|
260 |
- |
|
261 |
- expect(uut.defgeneric("foobar", "a") |
|
262 |
- .primary([uut.Shape(['a', 1], 'b')], ({ a, b }) => a + b) |
|
263 |
- .primary([Object], _ => null) |
|
264 |
- .fn({ a: 2, b: 3 })) |
|
265 |
- .toEqual(null); |
|
266 |
- }); |
|
267 |
- |
|
268 |
- test('Shape, prototype precedence', () => { |
|
269 |
- expect(uut.defgeneric("foobar4", "a") |
|
270 |
- .primary([uut.Shape('a')], ({ a }) => a) |
|
271 |
- .primary([uut.Shape('a', 'b')], ({ a, b }) => { return a + b }) |
|
272 |
- .primary([Object], _ => null).fn({ a: 1, b: 3 })) |
|
273 |
- .toEqual(4); |
|
274 |
- |
|
275 |
- expect(uut.defgeneric("foobar", "a") |
|
276 |
- .primary([uut.Shape('a', 'b')], ({ a, b }) => a + b) |
|
277 |
- .primary([uut.Shape('b')], ({ b }) => b) |
|
278 |
- .primary([Object], _ => null) |
|
279 |
- .fn({ a: 1, b: 2 })) |
|
280 |
- .toEqual(3); |
|
281 |
- |
|
282 |
- const Foo = function () { } |
|
283 |
- Foo.prototype = { a: true, b: null }; |
|
284 |
- expect(uut.defgeneric("foobar", "a") |
|
285 |
- .primary([uut.Shape('a')], function ({ a }) { return `a${this.call_next_method()}`; }) |
|
286 |
- .primary([uut.Shape('a', 'b', 'c')], function ({ a, b, c }) { return `c${this.call_next_method()}`; }) |
|
287 |
- .primary([uut.Shape('a', 'b')], function ({ a, b }) { return `b${this.call_next_method()}`; }) |
|
288 |
- .primary([Object], _ => 'd') |
|
289 |
- .fn(Object.assign(new Foo(), { c: 3 }))) |
|
290 |
- .toEqual('cbad'); |
|
291 |
- }); |
|
252 |
+describe("custom specializers", () => { |
|
253 |
+ test("Shape works", () => { |
|
254 |
+ expect( |
|
255 |
+ uut |
|
256 |
+ .defgeneric("foobar", "a") |
|
257 |
+ .primary([uut.Shape("a", "b")], ({ a, b }) => a + b) |
|
258 |
+ .primary([Object], _ => null) |
|
259 |
+ .fn({ a: 1, b: 2 }) |
|
260 |
+ ).toEqual(3); |
|
261 |
+ |
|
262 |
+ expect( |
|
263 |
+ uut |
|
264 |
+ .defgeneric("foobar", "a") |
|
265 |
+ .primary([uut.Shape("a", "b")], ({ a, b }) => a + b) |
|
266 |
+ .primary([Object], _ => null) |
|
267 |
+ .fn({ a: 1, b: 2, c: 3 }) |
|
268 |
+ ).toEqual(3); |
|
269 |
+ |
|
270 |
+ expect( |
|
271 |
+ uut |
|
272 |
+ .defgeneric("foobar", "a") |
|
273 |
+ .primary([uut.Shape("a", "b")], ({ a, b }) => a + b) |
|
274 |
+ .primary([Object], _ => null) |
|
275 |
+ .fn({ a: 1 }) |
|
276 |
+ ).toEqual(null); |
|
277 |
+ |
|
278 |
+ expect( |
|
279 |
+ uut |
|
280 |
+ .defgeneric("foobar", "a") |
|
281 |
+ .primary([uut.Shape(["a", 1], "b")], ({ a, b }) => a + b) |
|
282 |
+ .primary([Object], _ => null) |
|
283 |
+ .fn({ a: 1, b: 3 }) |
|
284 |
+ ).toEqual(4); |
|
285 |
+ |
|
286 |
+ expect( |
|
287 |
+ uut |
|
288 |
+ .defgeneric("foobar", "a") |
|
289 |
+ .primary([uut.Shape(["a", null], "b")], ({ a, b }) => b) |
|
290 |
+ .primary([Object], _ => null) |
|
291 |
+ .fn({ a: null, b: 3 }) |
|
292 |
+ ).toEqual(3); |
|
293 |
+ |
|
294 |
+ expect( |
|
295 |
+ uut |
|
296 |
+ .defgeneric("foobar", "a") |
|
297 |
+ .primary([uut.Shape(["a", undefined], "b")], ({ a, b }) => b) |
|
298 |
+ .primary([Object], _ => null) |
|
299 |
+ .fn({ b: 5 }) |
|
300 |
+ ).toEqual(null); //undefined is not a permissible default: treated as if the key is missing |
|
301 |
+ |
|
302 |
+ expect( |
|
303 |
+ uut |
|
304 |
+ .defgeneric("foobar", "a") |
|
305 |
+ .primary([uut.Shape(["a", 1], "b")], ({ a, b }) => a + b) |
|
306 |
+ .primary([Object], _ => null) |
|
307 |
+ .fn({ a: 2, b: 3 }) |
|
308 |
+ ).toEqual(null); |
|
309 |
+ }); |
|
310 |
+ |
|
311 |
+ test("Shape, prototype precedence", () => { |
|
312 |
+ expect( |
|
313 |
+ uut |
|
314 |
+ .defgeneric("foobar4", "a") |
|
315 |
+ .primary([uut.Shape("a")], ({ a }) => a) |
|
316 |
+ .primary([uut.Shape("a", "b")], ({ a, b }) => { |
|
317 |
+ return a + b; |
|
318 |
+ }) |
|
319 |
+ .primary([Object], _ => null) |
|
320 |
+ .fn({ a: 1, b: 3 }) |
|
321 |
+ ).toEqual(4); |
|
322 |
+ |
|
323 |
+ expect( |
|
324 |
+ uut |
|
325 |
+ .defgeneric("foobar", "a") |
|
326 |
+ .primary([uut.Shape("a", "b")], ({ a, b }) => a + b) |
|
327 |
+ .primary([uut.Shape("b")], ({ b }) => b) |
|
328 |
+ .primary([Object], _ => null) |
|
329 |
+ .fn({ a: 1, b: 2 }) |
|
330 |
+ ).toEqual(3); |
|
331 |
+ |
|
332 |
+ const Foo = function () {}; |
|
333 |
+ Foo.prototype = { a: true, b: null }; |
|
334 |
+ expect( |
|
335 |
+ uut |
|
336 |
+ .defgeneric("foobar", "a") |
|
337 |
+ .primary([uut.Shape("a")], function ({ a }) { |
|
338 |
+ return `a${this.call_next_method()}`; |
|
339 |
+ }) |
|
340 |
+ .primary([uut.Shape("a", "b", "c")], function ({ a, b, c }) { |
|
341 |
+ return `c${this.call_next_method()}`; |
|
342 |
+ }) |
|
343 |
+ .primary([uut.Shape("a", "b")], function ({ a, b }) { |
|
344 |
+ return `b${this.call_next_method()}`; |
|
345 |
+ }) |
|
346 |
+ .primary([Object], _ => "d") |
|
347 |
+ .fn(Object.assign(new Foo(), { c: 3 })) |
|
348 |
+ ).toEqual("cbad"); |
|
349 |
+ }); |
|
292 | 350 |
}); |
293 | 351 |
|
294 | 352 |
describe("Shape", () => { |
295 |
- test('super_of', () => { |
|
296 |
- expect(uut.Shape().super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); |
|
297 |
- expect(uut.Shape('a').super_of(uut.Shape('a', 'b'))).toBeTruthy(); |
|
298 |
- expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); |
|
299 |
- expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", 3))).toBeTruthy(); |
|
300 |
- expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b"))).toBeFalsy(); |
|
301 |
- expect(uut.Shape("a", "b").super_of(uut.Shape("a"))).toBeFalsy(); |
|
302 |
- }); |
|
353 |
+ test("super_of", () => { |
|
354 |
+ expect(uut.Shape().super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); |
|
355 |
+ expect(uut.Shape("a").super_of(uut.Shape("a", "b"))).toBeTruthy(); |
|
356 |
+ expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", "c"))).toBeTruthy(); |
|
357 |
+ expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b", 3))).toBeTruthy(); |
|
358 |
+ expect(uut.Shape("a", "b").super_of(uut.Shape("a", "b"))).toBeFalsy(); |
|
359 |
+ expect(uut.Shape("a", "b").super_of(uut.Shape("a"))).toBeFalsy(); |
|
360 |
+ }); |
|
303 | 361 |
}); |
... | ... |
@@ -1,109 +1,102 @@ |
1 | 1 |
/* eslint-disable */ |
2 |
-import {GenericFunction, around_qualifier} from '../src/genfuns'; |
|
2 |
+import { GenericFunction, around_qualifier } from "../src/genfuns"; |
|
3 | 3 |
|
4 | 4 |
function zipWith(fn, ...args) { |
5 |
- const minLen = Math.min(...args.map(x => x.length)); |
|
6 |
- const res = []; |
|
5 |
+ const minLen = Math.min(...args.map(x => x.length)); |
|
6 |
+ const res = []; |
|
7 | 7 |
|
8 |
- for (let x = 0; x < minLen; x++) { |
|
9 |
- res.push(fn(...args.map(a => a[x]))); |
|
10 |
- } |
|
8 |
+ for (let x = 0; x < minLen; x++) { |
|
9 |
+ res.push(fn(...args.map(a => a[x]))); |
|
10 |
+ } |
|
11 | 11 |
|
12 |
- return res; |
|
12 |
+ return res; |
|
13 | 13 |
} |
14 | 14 |
|
15 |
-const gf = GenericFunction( |
|
16 |
- "foobar", ["a", "b"] |
|
17 |
-).before( |
|
18 |
- [Object, Array], function (a,b) {console.log('in before', this.next_method_p);} |
|
19 |
-).primary( |
|
20 |
- [Object, Array], function (a,b) { |
|
21 |
- console.info('next_result: ', this.call_next_method(), this.next_method_p); |
|
22 |
- return [a,...b]; |
|
23 |
- } |
|
24 |
-).primary( |
|
25 |
- [Object, Object], function (thing, single) { |
|
26 |
- console.log("hello from previous method", this.next_method_p); |
|
27 |
- return [thing, single]; |
|
28 |
- } |
|
29 |
-).after( |
|
30 |
- [Number, Array], function (a,b) {console.log(`in after for ${a}`, this.next_method_p);} |
|
31 |
-).fn; |
|
15 |
+const gf = GenericFunction("foobar", ["a", "b"]) |
|
16 |
+ .before([Object, Array], function (a, b) { |
|
17 |
+ console.log("in before", this.next_method_p); |
|
18 |
+ }) |
|
19 |
+ .primary([Object, Array], function (a, b) { |
|
20 |
+ console.info("next_result: ", this.call_next_method(), this.next_method_p); |
|
21 |
+ return [a, ...b]; |
|
22 |
+ }) |
|
23 |
+ .primary([Object, Object], function (thing, single) { |
|
24 |
+ console.log("hello from previous method", this.next_method_p); |
|
25 |
+ return [thing, single]; |
|
26 |
+ }) |
|
27 |
+ .after([Number, Array], function (a, b) { |
|
28 |
+ console.log(`in after for ${a}`, this.next_method_p); |
|
29 |
+ }).fn; |
|
32 | 30 |
|
33 | 31 |
function groupGFMessages(gf) { |
34 |
- return gf.method([around_qualifier], [Object,Object], function(a,b) { |
|
35 |
- console.groupCollapsed(gf.name); |
|
36 |
- try { |
|
37 |
- return this.call_next_method(); |
|
38 |
- } finally { |
|
39 |
- console.groupEnd(); |
|
40 |
- } |
|
41 |
- }) |
|
32 |
+ return gf.method([around_qualifier], [Object, Object], function (a, b) { |
|
33 |
+ console.groupCollapsed(gf.name); |
|
34 |
+ try { |
|
35 |
+ return this.call_next_method(); |
|
36 |
+ } finally { |
|
37 |
+ console.groupEnd(); |
|
38 |
+ } |
|
39 |
+ }); |
|
42 | 40 |
} |
43 | 41 |
|
44 | 42 |
groupGFMessages(gf.gf); |
45 | 43 |
|
46 |
-console.log(gf(2,["asdf"])); |
|
44 |
+console.log(gf(2, ["asdf"])); |
|
47 | 45 |
|
48 |
-const gf2 = GenericFunction( |
|
49 |
- "another", ["a"] |
|
50 |
-).primary( |
|
51 |
- [Object], function (a) { return {value: a}; } |
|
52 |
-).method( |
|
53 |
- [around_qualifier], [Number], function(thing) { |
|
54 |
- console.log('before next method in number around'); |
|
55 |
- const val = this.call_next_method(); |
|
56 |
- console.log('after next method in number around', val); |
|
57 |
- return {was_num: true, ...val}; |
|
58 |
- } |
|
59 |
-).method( |
|
60 |
- [around_qualifier], [Object], function(thing) { |
|
61 |
- console.log('before next method in generic around'); |
|
62 |
- const val = this.call_next_method(); |
|
63 |
- console.log('after next method in generic around', val); |
|
64 |
- return {was_obj: true, ...val}; |
|
65 |
- } |
|
66 |
-); |
|
46 |
+const gf2 = GenericFunction("another", ["a"]) |
|
47 |
+ .primary([Object], function (a) { |
|
48 |
+ return { value: a }; |
|
49 |
+ }) |
|
50 |
+ .method([around_qualifier], [Number], function (thing) { |
|
51 |
+ console.log("before next method in number around"); |
|
52 |
+ const val = this.call_next_method(); |
|
53 |
+ console.log("after next method in number around", val); |
|
54 |
+ return { was_num: true, ...val }; |
|
55 |
+ }) |
|
56 |
+ .method([around_qualifier], [Object], function (thing) { |
|
57 |
+ console.log("before next method in generic around"); |
|
58 |
+ const val = this.call_next_method(); |
|
59 |
+ console.log("after next method in generic around", val); |
|
60 |
+ return { was_obj: true, ...val }; |
|
61 |
+ }); |
|
67 | 62 |
|
68 | 63 |
function MyStore() { |
69 |
- this.name = 'foo'; |
|
70 |
- this.address = '1234 asdfadfd' |
|
64 |
+ this.name = "foo"; |
|
65 |
+ this.address = "1234 asdfadfd"; |
|
71 | 66 |
} |
72 | 67 |
|
73 |
-class NameField extends HTMLElement {constructor() { |
|
74 |
- super() |
|
75 |
- const style = document.createElement('style'); |
|
68 |
+class NameField extends HTMLElement { |
|
69 |
+ constructor() { |
|
70 |
+ super(); |
|
71 |
+ const style = document.createElement("style"); |
|
76 | 72 |
this.appendChild(style); |
77 |
-}} |
|
78 |
-customElements.define('the-name', NameField); |
|
73 |
+ } |
|
74 |
+} |
|
75 |
+customElements.define("the-name", NameField); |
|
79 | 76 |
|
80 | 77 |
class AddressField extends HTMLElement { |
81 |
- constructor() { super() } |
|
78 |
+ constructor() { |
|
79 |
+ super(); |
|
80 |
+ } |
|
82 | 81 |
} |
83 |
-customElements.define('the-address', AddressField); |
|
82 |
+customElements.define("the-address", AddressField); |
|
84 | 83 |
|
85 |
-const renderFn = defgeneric( |
|
86 |
- "dorender", ["component", "el"] |
|
87 |
-).primary( |
|
88 |
- [MyStore, NameField], function (comp, heading) { |
|
89 |
- console.log('heading el', this.next_method_p); |
|
90 |
- heading.textContent = comp.name; |
|
91 |
- } |
|
92 |
-).primary( |
|
93 |
- [MyStore, AddressField], function (comp, el) { |
|
94 |
- console.log('address el', this.next_method_p); |
|
95 |
- el.textContent = comp.address; |
|
96 |
- } |
|
97 |
-).primary( |
|
98 |
- [MyStore, HTMLElement], function (comp, el) { |
|
99 |
- console.log('HtmlElement el ', el, this.next_method_p); |
|
100 |
- renderFn.fn(comp, el.querySelector('the-name')); |
|
101 |
- renderFn.fn(comp, el.querySelector('the-address')); |
|
102 |
- } |
|
103 |
-).before( |
|
104 |
- [Object, HTMLElement], function (_, el) { |
|
105 |
- console.log('has next? ', this.next_method_p) |
|
106 |
- } |
|
107 |
-); |
|
84 |
+const renderFn = defgeneric("dorender", ["component", "el"]) |
|
85 |
+ .primary([MyStore, NameField], function (comp, heading) { |
|
86 |
+ console.log("heading el", this.next_method_p); |
|
87 |
+ heading.textContent = comp.name; |
|
88 |
+ }) |
|
89 |
+ .primary([MyStore, AddressField], function (comp, el) { |
|
90 |
+ console.log("address el", this.next_method_p); |
|
91 |
+ el.textContent = comp.address; |
|
92 |
+ }) |
|
93 |
+ .primary([MyStore, HTMLElement], function (comp, el) { |
|
94 |
+ console.log("HtmlElement el ", el, this.next_method_p); |
|
95 |
+ renderFn.fn(comp, el.querySelector("the-name")); |
|
96 |
+ renderFn.fn(comp, el.querySelector("the-address")); |
|
97 |
+ }) |
|
98 |
+ .before([Object, HTMLElement], function (_, el) { |
|
99 |
+ console.log("has next? ", this.next_method_p); |
|
100 |
+ }); |
|
108 | 101 |
|
109 |
-renderFn.fn(new MyStore(), document.querySelector('section')); |
|
102 |
+renderFn.fn(new MyStore(), document.querySelector("section")); |