diff --git a/gtfs.js b/gtfs.js index ba81f95..edbb0c2 100644 --- a/gtfs.js +++ b/gtfs.js @@ -9,7 +9,7 @@ const forEachWithLog = require('./helpers/logging_iterator_wrapper'); const { exportGtfs } = require('./helpers/export'); const getters = require('./helpers/getters'); const { importTable } = require('./helpers/import'); -const schema = require('./helpers/schema'); +const defaultSchema = require('./helpers/schema'); /** * Table-generic function to add items in a table of a GTFS. @@ -24,7 +24,7 @@ function addItems(gtfs, tableName, items) { } const indexedTable = gtfs.getIndexedTable(tableName); - const indexKeys = schema.indexKeysByTableName[tableName]; + const indexKeys = gtfs._schema.indexKeysByTableName[tableName]; if (indexKeys.indexKey) { items.forEach(item => indexedTable.set(item[indexKeys.indexKey], item)); @@ -76,7 +76,7 @@ function forEachItem(gtfs, tableName, iterator) { } const indexedTable = gtfs.getIndexedTable(tableName); - const deepness = schema.deepnessByTableName[tableName]; + const deepness = gtfs._schema.deepnessByTableName[tableName]; if (deepness === 1) { forEachWithLog(`Iterating:${tableName}`, indexedTable, (item) => { @@ -105,7 +105,7 @@ function removeItems(gtfs, tableName, items) { } const indexedTable = gtfs.getIndexedTable(tableName); - const indexKeys = schema.indexKeysByTableName[tableName]; + const indexKeys = gtfs._schema.indexKeysByTableName[tableName]; if (indexKeys.indexKey) { items.forEach(item => indexedTable.delete(item[indexKeys.indexKey])); @@ -133,7 +133,7 @@ function removeItems(gtfs, tableName, items) { * @param {Object|Map} indexedItems Object properly indexed as the schema requires the table to be (see schema.js). */ function setIndexedItems(gtfs, tableName, indexedItems) { - if (indexedItems instanceof Map === false && schema.deepnessByTableName[tableName] !== 0) { + if (indexedItems instanceof Map === false && gtfs._schema.deepnessByTableName[tableName] !== 0) { throw new Error(`indexedItems must be a Map instead of: ${indexedItems}`); } @@ -165,18 +165,28 @@ class Gtfs { * }] * }; * + * * # options.throws * * Optional ad-hoc boolean. Default is true. Will force the parser to ignore invalid rows in the tables. * + * # options.forcedSchema + * + * Will overwrite the default schema by the value passed. + * + * * @param {string} [path] Path to the folder that contains the GTFS text files. * @param {{ * regexPatternObjectsByTableName: Map.>, - * throws: boolean + * throws: boolean, + * forcedSchema, * }} [options] Optional. See list above. * @return {Gtfs} gtfs Instanciated GTFS object. */ - constructor(path, {regexPatternObjectsByTableName = new Map(), throws = true} = {}) { + constructor( + path, + { regexPatternObjectsByTableName = new Map(), throws = true, postImportTableFunction, forcedSchema } = {} + ) { if (path !== undefined) { if (typeof path !== 'string' || path.length === 0) { throw new Error(`Gtfs need a valid input path as string, instead of: "${path}".`); @@ -194,7 +204,9 @@ class Gtfs { this._path = path; this._regexPatternObjectsByTableName = regexPatternObjectsByTableName; this._shouldThrow = throws; + this._postImportTableFunction = postImportTableFunction; this._tables = new Map(); + this._schema = defaultSchema || forcedSchema; } /* Input/Output */ @@ -273,14 +285,21 @@ class Gtfs { * * @return {Set.} Array of the table names */ - getTableNames() { return new Set([...schema.tableNames, ...this._tables.keys()]); } + getTableNames() { return new Set([...this._schema.tableNames, ...this._tables.keys()]); } + + /** + * Get the default schema of the GTFS. For safety, the schema is cloned before being passed. + * + * @return {Object} The schema + */ + static getDefaultSchema() { return JSON.parse(JSON.stringify(defaultSchema)); } /** - * Get the schema of the GTFS. For safety, the schema is cloned before beeing passed. + * Get the schema of the GTFS. For safety, the schema is cloned before being passed. * * @return {Object} The schema */ - static getSchema() { return JSON.parse(JSON.stringify(schema)); } + getSchema() { return JSON.parse(JSON.stringify(this._schema)); } /** * Build the list of the keys used in a table of the GTFS. Since the GTFS specification allows any additional field, diff --git a/helpers/export.js b/helpers/export.js index 62f1018..da0f33e 100644 --- a/helpers/export.js +++ b/helpers/export.js @@ -10,7 +10,6 @@ const errorLog = require('debug')('gtfsNodeLib:e'); const fs = require('fs-extra'); const { fromObjectToCsvString } = require('./csv'); -const schema = require('./schema'); /** * Private functions @@ -80,7 +79,7 @@ function exportTable(tableName, gtfs, outputPath, callback) { will be fixed. 2015-03-10 */ - const deepness = schema.deepnessByTableName[tableName]; + const deepness = gtfs._schema.deepnessByTableName[tableName]; if (deepness === 0) { const row = fromObjectToCsvString(gtfs.getIndexedTable(tableName), keys); diff --git a/helpers/getters.js b/helpers/getters.js index 5b2eb4c..216bd16 100644 --- a/helpers/getters.js +++ b/helpers/getters.js @@ -1,6 +1,6 @@ 'use strict'; -const schema = require('./schema'); +/* eslint-disable no-underscore-dangle */ /** * Build the list of the keys used in a table of the GTFS. Since the GTFS specification allows any additional field, @@ -12,8 +12,8 @@ const schema = require('./schema'); */ function getActualKeysForTable(gtfs, tableName) { const getSample = iterable => ((iterable && iterable.values().next()) ? iterable.values().next().value : undefined); - const keys = [...schema.keysByTableName[tableName]]; - const deepness = schema.deepnessByTableName[tableName]; + const keys = [...gtfs._schema.keysByTableName[tableName]]; + const deepness = gtfs._schema.deepnessByTableName[tableName]; const table = gtfs.getIndexedTable(tableName); let sampleItem; @@ -27,7 +27,7 @@ function getActualKeysForTable(gtfs, tableName) { if (sampleItem) { Object.keys(sampleItem).forEach((key) => { - if (schema.keysByTableName[tableName].includes(key) === false) { + if (gtfs._schema.keysByTableName[tableName].includes(key) === false) { keys.push(key); } }); @@ -57,15 +57,15 @@ function getGrandParentItem(itemWithForeignIndexId, parentTableName, grandParent ) { throw new Error(`itemWithForeignIndexId must be a plain object, instead of an "${typeof itemWithForeignIndexId}"`); } - if (schema.tableNames.includes(parentTableName) === false) { + if (gtfs._schema.tableNames.includes(parentTableName) === false) { throw new Error(`Cannot find table with name "${parentTableName}"`); } - if (schema.tableNames.includes(grandParentTableName) === false) { + if (gtfs._schema.tableNames.includes(grandParentTableName) === false) { throw new Error(`Cannot find table with name "${grandParentTableName}"`); } /* Reach parent item */ - const parentIndexKey = schema.indexKeysByTableName[parentTableName].indexKey; + const parentIndexKey = gtfs._schema.indexKeysByTableName[parentTableName].indexKey; if (itemWithForeignIndexId[parentIndexKey] === undefined) { throw new Error(`itemWithForeignIndexId should contain the foreign index key "${parentIndexKey}"`); @@ -78,7 +78,7 @@ function getGrandParentItem(itemWithForeignIndexId, parentTableName, grandParent } /* Reach grandparent item */ - const grandParentIndexKey = schema.indexKeysByTableName[grandParentTableName].indexKey; + const grandParentIndexKey = gtfs._schema.indexKeysByTableName[grandParentTableName].indexKey; if (!parentItem[grandParentIndexKey]) { throw new Error(`parentItem should contain the foreign index key "${grandParentIndexKey}"${parentItem}`); @@ -96,14 +96,14 @@ function getGrandParentItem(itemWithForeignIndexId, parentTableName, grandParent * @return {Map.} Indexed child items. */ function getIndexedItemsWithParent(parentItem, tableName, gtfs) { - if (schema.deepnessByTableName[tableName] !== 2) { + if (gtfs._schema.deepnessByTableName[tableName] !== 2) { throw new Error(`Table "${tableName}" is not of deepness 2.`); } if (parentItem === undefined || parentItem === null || typeof parentItem !== 'object') { throw new Error(`Parent item should be a plain object, instead of an "${typeof parentItem}"`); } - const firstIndexKey = schema.indexKeysByTableName[tableName].firstIndexKey; + const firstIndexKey = gtfs._schema.indexKeysByTableName[tableName].firstIndexKey; if (parentItem[firstIndexKey] === undefined) { throw new Error(`Parent item should contain the foreign index key "${firstIndexKey}"`); @@ -123,7 +123,7 @@ function getIndexedItemsWithParent(parentItem, tableName, gtfs) { * @return {Map.} Indexed child items. */ function getIndexedItemsWithParentIndex(parentIndex, tableName, gtfs) { - if (schema.deepnessByTableName[tableName] !== 2) { + if (gtfs._schema.deepnessByTableName[tableName] !== 2) { throw new Error(`Table "${tableName}" is not of deepness 2.`); } if (typeof parentIndex !== 'string') { @@ -146,7 +146,7 @@ function getIndexedItemsWithParentIndex(parentIndex, tableName, gtfs) { * @param {Gtfs} gtfs Gtfs object */ function getItemWithIndex(index, tableName, gtfs) { - if (schema.deepnessByTableName[tableName] !== 1) { + if (gtfs._schema.deepnessByTableName[tableName] !== 1) { throw new Error(`Cannot access item with only one index in "${tableName}", since the deepness is not 1.`); } if (typeof index !== 'string') { @@ -170,7 +170,7 @@ function getItemWithIndex(index, tableName, gtfs) { * @param {Gtfs} gtfs Gtfs object */ function getItemWithIndexes(firstIndex, secondIndex, tableName, gtfs) { - if (schema.deepnessByTableName[tableName] !== 2) { + if (gtfs._schema.deepnessByTableName[tableName] !== 2) { throw new Error(`Cannot access item with two indexes in "${tableName}", since the deep is not 2.`); } if (firstIndex === undefined || firstIndex === null || typeof firstIndex !== 'string') { @@ -202,7 +202,7 @@ function getParentItem(itemWithForeignIndexId, tableName, gtfs) { throw new Error(`itemWithForeignIndexId must be a plain object, instead of an "${typeof itemWithForeignIndexId}"`); } - const indexKey = schema.indexKeysByTableName[tableName].indexKey; + const indexKey = gtfs._schema.indexKeysByTableName[tableName].indexKey; if (itemWithForeignIndexId[indexKey] === undefined) { throw new Error( diff --git a/helpers/import.js b/helpers/import.js index 6e5efa8..2406ae5 100644 --- a/helpers/import.js +++ b/helpers/import.js @@ -7,7 +7,6 @@ const fs = require('fs-extra'); const eachWithLog = require('./logging_iterator_wrapper'); const { fromCsvStringToArray } = require('./csv'); -const schema = require('./schema'); /** * Import a table in the GTFS. @@ -17,7 +16,7 @@ const schema = require('./schema'); */ exports.importTable = (gtfs, tableName) => { - const indexKeys = schema.indexKeysByTableName[tableName]; + const indexKeys = gtfs._schema.indexKeysByTableName[tableName]; const fullPath = `${gtfs.getPath() + tableName}.txt`; if (fs.existsSync(fullPath)) { diff --git a/package-lock.json b/package-lock.json index 6278c85..edc073e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,7 +2,6 @@ "name": "@transit/gtfs", "version": "3.0.0", "lockfileVersion": 1, - "requires": true, "dependencies": { "acomb": { "version": "1.2.2", @@ -1573,6 +1572,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -1584,15 +1592,6 @@ "strip-ansi": "3.0.1" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", diff --git a/tests.js b/tests.js index 179347d..2fb8cde 100644 --- a/tests.js +++ b/tests.js @@ -669,11 +669,11 @@ describe('Tests on GTFS', () => { const funkyStop = {}; gtfs.addStop(funkyStop); - expect(Gtfs.getSchema().keysByTableName.stops).to.deep.equal(gtfs.getActualKeysForTable('stops')); + expect(gtfs.getSchema().keysByTableName.stops).to.deep.equal(gtfs.getActualKeysForTable('stops')); funkyStop.route_funky_name = 'Tshboom tshboom'; funkyStop.route_esoteric_float = 120.37; - const standardRouteKeys = Gtfs.getSchema().keysByTableName.stops; + const standardRouteKeys = gtfs.getSchema().keysByTableName.stops; const actualRouteKeys = gtfs.getActualKeysForTable('stops'); expect(standardRouteKeys).to.not.deep.equal(actualRouteKeys);