diff --git a/README.md b/README.md index bd4d99f..20800f4 100644 --- a/README.md +++ b/README.md @@ -367,7 +367,7 @@ A class for random table objects that can be used by the tableRoller. A great va * @property {String[]|Object[]} [table] default table. array of strings or objects. removed after initialization. * @property {Object} [tables] a property for each subtables. * @property {RandomTableEntries[]} tables[subtablename] Entries for subtables. -* @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys to be rolled on in order +* @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys (and optional subtables) to be rolled on in order * @property {Map[DisplayOptions]} [display_opt] Display options for the subtables. * @property {Array} [dependencies] table keys that are needed to get full results from this table diff --git a/docs/tableformat.md b/docs/tableformat.md index 06f8d90..186b282 100644 --- a/docs/tableformat.md +++ b/docs/tableformat.md @@ -17,7 +17,7 @@ These are properties on the RandomTable object. Most are optional. * @property {String[]|Object[]} [table] default table. array of strings or objects. removed after initialization. * @property {Object} [tables] a property for each subtables. * @property {RandomTableEntries[]} tables[subtablename] Entries for subtables. -* @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys to be rolled on in order +* @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys and optionsl subtableas to be rolled on in order * @property {Map[DisplayOptions]} [display_opt] Display options for the subtables. * @property {Array} [dependencies] table keys that are needed to get full results from this table @@ -245,3 +245,5 @@ In this case, passing this table to ```randomizer.getTableResult()``` will gener ``` If the `macro` property is set the `sequence` and `tables` properties will be ignored. + +You can also select subtables by using the form `table:subtable` (just like a table token) in your macros. diff --git a/src/RandomTable.js b/src/RandomTable.js index 8f6d2dd..ae2f5f3 100644 --- a/src/RandomTable.js +++ b/src/RandomTable.js @@ -21,7 +21,7 @@ export default class RandomTable { * @property {String[]|Object[]} [table] default table. array of strings or objects. removed after initialization. * @property {Object} [tables] a property for each subtables. * @property {RandomTableEntries[]} tables[subtablename] Entries for subtables. - * @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys to be rolled on in order + * @property {String[]} [macro] for tables that are only used to aggregate result from other tables, this array consists of table keys and optionsl subtables to be rolled on in order * @property {Map[DisplayOptions]} [display_opt] Display options for the subtables. * @property {Array} [dependencies] table keys that are needed to get full results from this table * @@ -231,6 +231,14 @@ export default class RandomTable { } // iterate over the tables and look for table tokens let dep = []; + + // Check macro + this.macro.forEach((macro) => { + const parts = macro.split(':'); + if (parts.length > 0 && parts[0] !== 'this') { + dep.push(parts[0]); + } + }); const tokenRegExp = /({{2}.+?}{2})/g; const tnames = Object.keys(this.tables); tnames.forEach((n) => { diff --git a/src/TableRoller.js b/src/TableRoller.js index 209fd63..0b599c3 100644 --- a/src/TableRoller.js +++ b/src/TableRoller.js @@ -90,8 +90,9 @@ class TableRoller { // Select from each subtable and add to results. entry.subtable.forEach((subtableName) => { - const r2 = this._selectFromTable(rtable, subtableName); - o = o.concat(r2); + const subresult = this._selectFromTable(rtable, subtableName); + // concat because subresult is an array. + o = o.concat(subresult); }); return o; } @@ -101,17 +102,28 @@ class TableRoller { * @returns {RandomTableResult[]} */ _getTableMacroResult (rtable) { - const results = []; + let results = []; try { - rtable.macro.forEach((tableKey) => { + rtable.macro.forEach((macroKey) => { + const parts = macroKey.split(':'); + const tableKey = parts[0]; + const subtable = parts[1] || ''; if (tableKey === rtable.key) { throw new TableError(`Macros can't self reference.`); } - const set = this.getTableResultSetByKey(tableKey); - // @todo maybe this could be handled better - // because forcing the result set to a string is not versatile. - const result = new RandomTableResult({ table: tableKey, result: set.niceString() }); - results.push(result); + try { + const mtable = this.getTableByKey(tableKey); + const result = this.getTableResult(mtable, subtable); + // concat because result is an array. + results = results.concat(result); + } catch (e) { + if (e instanceof TableError) { + results.push(this._getErrorResult(e.message, tableKey)); + } else { + // Rethrow unexpected errors + throw e; + } + } }); } catch (e) { if (e instanceof RangeError) { diff --git a/test/RandomTableTest.js b/test/RandomTableTest.js index 1cdb963..473e752 100644 --- a/test/RandomTableTest.js +++ b/test/RandomTableTest.js @@ -83,8 +83,17 @@ describe('RandomTable', function () { new RandomTableEntry({ label: 'blue {{table:weather:wind}}' }), new RandomTableEntry({ label: 'blue {{table:directions}} {{table:dragons}}' }) ]; + rtable.macro = [ + 'pattern', + 'pattern:plaid', + 'texture', + 'sample:colors' + ]; expect(rtable.findDependencies()).to.deep.equal([ + 'pattern', + 'texture', + 'sample', 'weather', 'directions', 'dragons' diff --git a/test/TableRollerTest.js b/test/TableRollerTest.js index a56104d..271f95a 100644 --- a/test/TableRollerTest.js +++ b/test/TableRollerTest.js @@ -55,7 +55,7 @@ const complicatedTable = new RandomTable({ 'down' ] }, - macro: ['colors', 'nonexistent'] + macro: ['colors:secondary', 'nonexistent'] }); const getTables = function (key) { @@ -116,14 +116,22 @@ describe('TableRoller', function () { const results = roller._getTableMacroResult(complicatedTable); expect(results.length).to.equal(2); expect(results[0]).to.deep.include({ - table: 'colors', - result: 'Primary: Blue' + table: 'secondary', + result: 'green' }); expect(results[1]).to.deep.include({ table: 'nonexistent', - result: 'Error: No table found for key: nonexistent' + result: 'No table found for key: nonexistent' }); }); + + it('should throw error for self reference', function () { + const selfTable = new RandomTable({ + key: 'self', + macro: ['colors', 'self'] + }); + expect(() => { roller._getTableMacroResult(selfTable); }).to.throw(TableError, 'self reference'); + }); }); describe('findToken', function () { @@ -210,10 +218,10 @@ describe('TableRoller', function () { describe('getTableResult', function () { it('should handle macros in tables', function () { const macroStub = stub(roller, '_getTableMacroResult'); - const resultSet = new RandomTableResultSet({ title: 'macro set' }); - macroStub.returns(resultSet); + const resultSet = new RandomTableResult({ table: 'macro' }); + macroStub.returns([resultSet]); - expect(roller.getTableResult(complicatedTable)).to.equal(resultSet); + expect(roller.getTableResult(complicatedTable)).to.deep.equal([resultSet]); macroStub.restore(); });