diff --git a/src/core/Dish.mjs b/src/core/Dish.mjs index c6c68ae4..7a7efdc4 100755 --- a/src/core/Dish.mjs +++ b/src/core/Dish.mjs @@ -17,16 +17,27 @@ class Dish { /** * Dish constructor * - * @param {Dish} [dish=null] - A dish to clone + * @param {Dish || *} [dishOrInput=null] - A dish to clone OR an object + * literal to make into a dish + * @param {Enum} [type=null] (optional) - A type to accompany object + * literal input */ - constructor(dish=null) { + constructor(dishOrInput=null, type = null) { this.value = []; this.type = Dish.BYTE_ARRAY; - if (dish && - dish.hasOwnProperty("value") && - dish.hasOwnProperty("type")) { - this.set(dish.value, dish.type); + // Case: dishOrInput is dish object + if (dishOrInput && + dishOrInput.hasOwnProperty("value") && + dishOrInput.hasOwnProperty("type")) { + this.set(dishOrInput.value, dishOrInput.type); + // input and type defined separately + } else if (dishOrInput && type) { + this.set(dishOrInput, type); + // No type declared, so infer it. + } else if (dishOrInput) { + const inferredType = Dish.typeEnum(dishOrInput.constructor.name); + this.set(dishOrInput, inferredType); } } @@ -56,6 +67,7 @@ class Dish { case "big number": return Dish.BIG_NUMBER; case "json": + case "object": // object constructor name. To allow JSON input in node. return Dish.JSON; case "file": return Dish.FILE; diff --git a/src/node/SyncDish.mjs b/src/node/SyncDish.mjs index a3d87e14..a6d0a984 100644 --- a/src/node/SyncDish.mjs +++ b/src/node/SyncDish.mjs @@ -18,13 +18,13 @@ import * as ops from "./index"; class SyncDish extends Dish { /** */ - constructor(dish=null) { - super(dish); - + constructor(inputOrDish=null, type=null) { + super(inputOrDish); + + // Add operations to make it composable for (const op in ops) { this[op] = () => ops[op](this.value); } - } /** diff --git a/src/node/config/scripts/generateNodeIndex.mjs b/src/node/config/scripts/generateNodeIndex.mjs index bc0a8bfe..5acbbf60 100644 --- a/src/node/config/scripts/generateNodeIndex.mjs +++ b/src/node/config/scripts/generateNodeIndex.mjs @@ -39,6 +39,7 @@ let code = `/** import "babel-polyfill"; +import SyncDish from "./SyncDish"; import { wrap, help, bake } from "./api"; import { `; @@ -80,6 +81,7 @@ code += ` }; const chef = generateChef(); chef.help = help; +chef.dish = SyncDish; `; includedOperations.forEach((op) => { @@ -106,7 +108,7 @@ includedOperations.forEach((op) => { code += ` ${decapitalise(op)},\n`; }); - +code += " SyncDish as Dish\n"; code += "};\n"; diff --git a/src/node/index.mjs b/src/node/index.mjs index 513e853e..d803485f 100644 --- a/src/node/index.mjs +++ b/src/node/index.mjs @@ -10,6 +10,7 @@ import "babel-polyfill"; +import SyncDish from "./SyncDish"; import { wrap, help, bake } from "./api"; import { ADD as core_ADD, @@ -514,6 +515,7 @@ function generateChef() { const chef = generateChef(); chef.help = help; +chef.dish = SyncDish; const ADD = chef.ADD; const AESDecrypt = chef.AESDecrypt; const AESEncrypt = chef.AESEncrypt; @@ -1237,4 +1239,5 @@ export { XPathExpression, zlibDeflate, zlibInflate, + SyncDish as Dish }; diff --git a/test/tests/nodeApi/nodeApi.mjs b/test/tests/nodeApi/nodeApi.mjs index e9009414..88a285b2 100644 --- a/test/tests/nodeApi/nodeApi.mjs +++ b/test/tests/nodeApi/nodeApi.mjs @@ -16,7 +16,7 @@ import chef from "../../../src/node/index"; import OperationError from "../../../src/core/errors/OperationError"; import SyncDish from "../../../src/node/SyncDish"; -import { toBase32 } from "../../../src/node/index"; +import { toBase32, Dish } from "../../../src/node/index"; import TestRegister from "../../TestRegister"; TestRegister.addApiTests([ @@ -282,4 +282,40 @@ TestRegister.addApiTests([ assert.strictEqual(result.toString(), "begin_something_anananaaaaak_da_aaak_da_aaaaananaaaaaaan_da_aaaaaaanan_da_aaak_end_something"); }), + it("Composable Dish: Should have top level Dish object", () => { + assert.ok(Dish); + }), + + it("Composable Dish: Should construct empty dish object", () => { + const dish = new Dish(); + assert.deepEqual(dish.value, []); + assert.strictEqual(dish.type, 0); + }), + + it("Composable Dish: constructed dish should have operation prototype functions", () => { + const dish = new Dish(); + assert.ok(dish.translateDateTimeFormat); + assert.ok(dish.stripHTTPHeaders); + assert.throws(() => dish.someInvalidFunction()); + }), + + it("Composable Dish: composed function returns another dish", () => { + const result = new Dish("some input").toBase32(); + assert.ok(result instanceof SyncDish); + }), + + it("Composable dish: infers type from input if needed", () => { + const dish = new Dish("string input"); + assert.strictEqual(dish.type, 1); + + const numberDish = new Dish(333); + assert.strictEqual(numberDish.type, 2); + + const arrayBufferDish = new Dish(Buffer.from("some buffer input").buffer); + assert.strictEqual(arrayBufferDish.type, 4); + + const JSONDish = new Dish({key: "value"}); + assert.strictEqual(JSONDish.type, 6); + }), + ]); diff --git a/test/tests/nodeApi/ops.mjs b/test/tests/nodeApi/ops.mjs index abba985f..589826df 100644 --- a/test/tests/nodeApi/ops.mjs +++ b/test/tests/nodeApi/ops.mjs @@ -791,7 +791,7 @@ jmPGsv1elXxVzqs58UZLD2c3vBhGkU2BV6kRKh+lj/EcVrzsFhGCz/7DKxPoDHLS it("Scan for embedded files", () => { const result = chef.scanForEmbeddedFiles(fs.readFileSync("src/web/static/images/cook_male-32x32.png")); - const expected = "Scanning data for \'magic bytes\' which may indicate embedded files."; + const expected = "Scanning data for 'magic bytes' which may indicate embedded files."; assert.ok(result.toString().indexOf(expected) === 0); }),