From 5b027dbbb43978fe9bf8254d52528acdbb5e6400 Mon Sep 17 00:00:00 2001 From: Nikolas Poniros Date: Sun, 24 Apr 2016 13:04:58 +0200 Subject: [PATCH 1/2] Add support for optional node data Add optional data parameter for the addNode method. If data is not given the node name is used as data. Add methods setNodeData and getNodeData to manipulate the data associated with a node. Update the hasNode method to be able to cope with falsy node data. Update README to document the new functionality. Fixes #12 --- CHANGELOG.md | 4 ++- README.md | 16 ++++++++-- lib/dep_graph.js | 29 ++++++++++++++++-- specs/dep_graph_spec.js | 65 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf41ce..138cf4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## In development -- No changes yet +- Add optional data parameter for the addNode method. (Fixes #12) +- Add methods getNodeData and setNodeData to manipulate the data associated with a node name. (Fixes #12) +- Change the hasNode method to be able to cope with falsy node data. (Fixes #12) ## 0.4.1 (Sept 3, 2015) diff --git a/README.md b/README.md index e8ef205..85f94c8 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,13 @@ To use, `npm install dependency-graph` and then `require('dependency-graph').Dep ### DepGraph -Nodes in the graph are just simple strings. +Nodes in the graph are just simple strings with optional data associated with them. - - `addNode(name)` - add a node in the graph + - `addNode(name, data)` - add a node in the graph with optional data. If `data` is not given, `name` will be used as data - `removeNode(name)` - remove a node from the graph - `hasNode(name)` - check if a node exists in the graph + - `getNodeData(name)` - get the data associated with a node (will throw an Error if the node does not exist) + - `setNodeData(name, data)` - set the data for an existing node. It will do nothing if the node does not exist - `addDependency(from, to)` - add a dependency between two nodes (will throw an Error if one of the nodes does not exist) - `removeDependency(from, to)` - remove a dependency between two nodes - `dependenciesOf(name, leavesOnly)` - get an array containing the nodes that the specified node depends on (transitively). If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned in the array. @@ -42,4 +44,12 @@ Dependency Cycles are detected when running `dependenciesOf`, `dependantsOf`, an graph.dependantsOf('c'); // ['a', 'b'] graph.overallOrder(); // ['c', 'b', 'a'] - graph.overallOrder(true); // ['c'] \ No newline at end of file + graph.overallOrder(true); // ['c'] + + graph.addNode('d', 'data'); + + graph.getNodeData('d'); // 'data' + + graph.setNodeData('d', 'newData'); + + graph.getNodeData('d'); // 'newData' \ No newline at end of file diff --git a/lib/dep_graph.js b/lib/dep_graph.js index 1508aed..f4209ac 100644 --- a/lib/dep_graph.js +++ b/lib/dep_graph.js @@ -45,9 +45,14 @@ DepGraph.prototype = { /** * Add a node to the dependency graph. If a node already exists, this method will do nothing. */ - addNode:function (node) { + addNode:function (node, data) { if (!this.hasNode(node)) { - this.nodes[node] = node; + // Checking the arguments length allows the user to add a node with undefined data + if (arguments.length === 2) { + this.nodes[node] = data; + } else { + this.nodes[node] = node; + } this.outgoingEdges[node] = []; this.incomingEdges[node] = []; } @@ -74,7 +79,25 @@ DepGraph.prototype = { * Check if a node exists in the graph */ hasNode:function (node) { - return !!this.nodes[node]; + return this.nodes.hasOwnProperty(node); + }, + /** + * Get the data associated with a node name + */ + getNodeData:function (node) { + if (this.hasNode(node)) { + return this.nodes[node]; + } else { + throw new Error('Node does not exist: ' + node); + } + }, + /** + * Set the associated data for a given node name. If the node does not exist, this method will do nothing + */ + setNodeData:function (node, data) { + if (this.hasNode(node)) { + this.nodes[node] = data; + } }, /** * Add a dependency between two nodes. If either of the nodes does not exist, diff --git a/specs/dep_graph_spec.js b/specs/dep_graph_spec.js index 6456090..c811f4e 100644 --- a/specs/dep_graph_spec.js +++ b/specs/dep_graph_spec.js @@ -17,6 +17,71 @@ describe('DepGraph', function () { expect(graph.hasNode('Bar')).toBe(false); }); + it('should treat the node data parameter as optional and use the node name as data if node data was not given', function () { + var graph = new DepGraph(); + + graph.addNode('Foo'); + + expect(graph.getNodeData('Foo')).toBe('Foo'); + }); + + it('should be able to associate a node name with data on node add', function () { + var graph = new DepGraph(); + + graph.addNode('Foo', 'data'); + + expect(graph.getNodeData('Foo')).toBe('data'); + }); + + it('should be able to add undefined as node data', function () { + var graph = new DepGraph(); + + graph.addNode('Foo', undefined); + + expect(graph.getNodeData('Foo')).toBe(undefined); + }); + + it('should return true when using hasNode with a node which has falsy data', function () { + var graph = new DepGraph(); + + var falsyData = ['', 0, null, undefined, false]; + graph.addNode('Foo'); + + falsyData.forEach(function(data) { + graph.setNodeData('Foo', data); + + expect(graph.hasNode('Foo')).toBe(true); + + // Just an extra check to make sure that the saved data is correct + expect(graph.getNodeData('Foo')).toBe(data); + }); + }); + + it('should be able to set data after a node was added', function () { + var graph = new DepGraph(); + + graph.addNode('Foo', 'data'); + graph.setNodeData('Foo', 'data2'); + + expect(graph.getNodeData('Foo')).toBe('data2'); + }); + + it('should do nothing if we try to set data for a non-existing node', function () { + var graph = new DepGraph(); + + graph.setNodeData('Foo', 'data'); + + expect(graph.hasNode('Foo')).toBe(false); + }); + + it('should throw an error if the node does not exists and we try to get data', function () { + var graph = new DepGraph(); + + expect(function () { + graph.getNodeData('Foo'); + }).toThrow(new Error('Node does not exist: Foo')); + }); + it('should do nothing if creating a node that already exists', function () { var graph = new DepGraph(); From d894169d5531c167356557eba58881d7dedf51a6 Mon Sep 17 00:00:00 2001 From: Nikolas Poniros Date: Tue, 26 Apr 2016 07:47:18 +0200 Subject: [PATCH 2/2] Change setNodeData to throw an Error The setNodeData method should throw an error if we try to set data for a non-existing node. Update tests and README --- README.md | 14 +++++++------- lib/dep_graph.js | 4 +++- specs/dep_graph_spec.js | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 85f94c8..97bf0ae 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Nodes in the graph are just simple strings with optional data associated with th - `addNode(name, data)` - add a node in the graph with optional data. If `data` is not given, `name` will be used as data - `removeNode(name)` - remove a node from the graph - `hasNode(name)` - check if a node exists in the graph - - `getNodeData(name)` - get the data associated with a node (will throw an Error if the node does not exist) - - `setNodeData(name, data)` - set the data for an existing node. It will do nothing if the node does not exist + - `getNodeData(name)` - get the data associated with a node (will throw an Error if the node does not exist) + - `setNodeData(name, data)` - set the data for an existing node (will throw an Error if the node does not exist) - `addDependency(from, to)` - add a dependency between two nodes (will throw an Error if one of the nodes does not exist) - `removeDependency(from, to)` - remove a dependency between two nodes - `dependenciesOf(name, leavesOnly)` - get an array containing the nodes that the specified node depends on (transitively). If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned in the array. @@ -45,11 +45,11 @@ Dependency Cycles are detected when running `dependenciesOf`, `dependantsOf`, an graph.overallOrder(); // ['c', 'b', 'a'] graph.overallOrder(true); // ['c'] - + graph.addNode('d', 'data'); - + graph.getNodeData('d'); // 'data' - + graph.setNodeData('d', 'newData'); - - graph.getNodeData('d'); // 'newData' \ No newline at end of file + + graph.getNodeData('d'); // 'newData' diff --git a/lib/dep_graph.js b/lib/dep_graph.js index f4209ac..9a56642 100644 --- a/lib/dep_graph.js +++ b/lib/dep_graph.js @@ -92,11 +92,13 @@ DepGraph.prototype = { } }, /** - * Set the associated data for a given node name. If the node does not exist, this method will do nothing + * Set the associated data for a given node name. If the node does not exist, this method will throw an error */ setNodeData:function (node, data) { if (this.hasNode(node)) { this.nodes[node] = data; + } else { + throw new Error('Node does not exist: ' + node); } }, /** diff --git a/specs/dep_graph_spec.js b/specs/dep_graph_spec.js index c811f4e..89aab2a 100644 --- a/specs/dep_graph_spec.js +++ b/specs/dep_graph_spec.js @@ -66,12 +66,12 @@ describe('DepGraph', function () { expect(graph.getNodeData('Foo')).toBe('data2'); }); - it('should do nothing if we try to set data for a non-existing node', function () { + it('should throw an error if we try to set data for a non-existing node', function () { var graph = new DepGraph(); - graph.setNodeData('Foo', 'data'); - - expect(graph.hasNode('Foo')).toBe(false); + expect(function () { + graph.setNodeData('Foo', 'data'); + }).toThrow(new Error('Node does not exist: Foo')); }); it('should throw an error if the node does not exists and we try to get data', function () { @@ -299,4 +299,4 @@ describe('DepGraph', function () { expect(graph.dependenciesOf('a')).toEqual(['b']); }); -}); \ No newline at end of file +});