Skip to content

Commit 53fc9c9

Browse files
committed
Merge pull request #46 from Encapsule/chrisrus/develop
Chrisrus/develop taking fixes for issues #41 #42 #43 and #47 + a few small doc updates as v0.6.1 release.
2 parents a1251cf + 3a24da4 commit 53fc9c9

19 files changed

+502
-100
lines changed

README.md

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,48 @@
22

33
## About jsgraph
44

5-
_Graphs are mathematical abstractions that are useful for solving many types of problems in computer science. Consequently, these abstractions must also be represented in computer programs._ - [J. Siek](http://ecee.colorado.edu/~siek/resume.pdf)
5+
_"... At the other end of the spectrum is, for example, graph theory, where the basic object, a graph, can be immediately comprehended. One will not get anywhere in graph theory by sitting in an armchair and trying to understand graphs better. Neither is it particularly necessary to read much of the literature before tackling a problem: it is of course helpful to be aware of some of the most important techniques, but the interesting problems tend to be open precisely because the established techniques cannot easily be applied."_ - [W.T. Gowers](https://en.wikipedia.org/wiki/Timothy_Gowers)
66

7-
Encapsule/jsgraph is a framework for working with directed graph data models using an in-memory storage container abstraction, and a small but growing collection of powerfully-extensible graph coloring algorithms implemented using the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern). The library is specifically designed for efficient embedding in complex Node.js / HTML 5 applications that require clean separation of concerns, and a high-degree of extensibility and control.
7+
### Status
88

9-
jsgraph is based on the API design and architectural separaton of concerns for graph algorithms invented by the authors of the [Boost C++ Graph Library](http://www.boost.org/doc/libs/1_56_0/libs/graph/doc/index.html) (BGL). The port is logically close enough that the BGL documentation should be considered as an advanced resource.
9+
[![Build Status](https://travis-ci.org/Encapsule/jsgraph.svg?branch=master)](https://travis-ci.org/Encapsule/jsgraph)
1010

11-
I encourage you to take some time and experiment with the library as it's truly powerful once you get over the initial learning curve.
11+
Ich danke Ihnen sehr [Travis CI](https://travis-ci.org/Encapsule/jsgraph.svg?branch=master)
1212

13-
_"... At the other end of the spectrum is, for example, graph theory, where the basic object, a graph, can be immediately comprehended. One will not get anywhere in graph theory by sitting in an armchair and trying to understand graphs better. Neither is it particularly necessary to read much of the literature before tackling a problem: it is of course helpful to be aware of some of the most important techniques, but the interesting problems tend to be open precisely because the established techniques cannot easily be applied."_ - [W.T. Gowers](https://en.wikipedia.org/wiki/Timothy_Gowers)
13+
### Summary
14+
15+
`Encapsule/jsgraph` implements a framework for storing and processing in-memory directed graph data sets that is inspired by the research of [Jeremy Siek](http://wphomes.soic.indiana.edu/jsiek/) and his work on the [Boost C++ Graph Library](http://www.boost.org/doc/libs/1_59_0/libs/graph/doc/table_of_contents.html).
16+
17+
The library provides and normalizes the following operations independent of graph topology and semantics:
18+
19+
- Generic, multi-dimensional container abstraction for storing directed graphs in memory.
20+
- JSON/native JavaScript data object import/export API's.
21+
- Vertex and edge CRUD API's.
22+
- Application-defined vertex and edge property API's.
23+
- Vertex and edge enumeration API's.
24+
- Root/leaf vertex set enumeration API's.
25+
- Generic transposition algorithm.
26+
- Developer-extensible breadth-first visit and search algorithm.
27+
- Developer-extensible depth-first visit and search algorithm.
28+
29+
### Audience
30+
31+
jsgraph is a framework for building next-generation frameworks on Node.js and HTML 5.
32+
33+
- Use instead of in-memory object hierarchies linked by reference:
34+
- Serializable to JSON (even when cyclic).
35+
- Edges can be labeled with properties. References can't.
36+
- Much simpler to debug and test.
37+
- Promotes code consistency and readability.
38+
- Less refactoring due to data structure udpates.
39+
- Implement business logic and process as generic graph algorithms:
40+
- Considerably reduce lines of code/test.
41+
- Make the architecture of the design manifest in code.
42+
- Re-use investment on other and new data types without code modification.
43+
44+
### Training and Support
45+
46+
If you're considering using jsgraph in a commerical product, please get in touch with [Encapsule.io](https://encapsule.io) - we can save you time, and help you get the most out of your investment.
1447

1548
### Programming
1649

@@ -26,7 +59,7 @@ _"... At the other end of the spectrum is, for example, graph theory, where the
2659
In your project, install via npm.
2760

2861
$ npm install jsgraph --save
29-
jsgraph@0.5.xx node_modules/jsgraph
62+
jsgraph@0.6.xx node_modules/jsgraph
3063

3164
### Sources
3265

@@ -51,7 +84,7 @@ See also: [Encapsule/jsgraph on GitHub](https://github.com/Encapsule/jsgraph)
5184

5285
## Example
5386

54-
The following short example constructs a `DirectedGraph` container using a v0.5 jsgraph digraph data object, and derives a simple rank assignment algorithm from jsgraph's bundled `breadthFirstTraverse` algorithm. Note that the BFT visitor interface callback functions leverage the `DirectedGraph` API to get/set the data property value of each visited vertex to its rank.
87+
The following short example constructs a `DirectedGraph` container using a v0.6 jsgraph digraph data object, and derives a simple rank assignment algorithm from jsgraph's bundled `breadthFirstTraverse` algorithm. Note that the BFT visitor interface callback functions leverage the `DirectedGraph` API to get/set the data property value of each visited vertex to its rank.
5588
```javascript
5689
// Encapsule/jsgraph/examples/bft-vertex-ranking.js
5790
var jsgraph = require('jsgraph');
@@ -98,7 +131,7 @@ console.log("BFT traversal: '" +
98131
```
99132
... produces the following output with each vertex's property value set to its rank (edge hops away from a root vertex in this example).
100133

101-
```javascript
134+
```
102135
DirectedGraph: '{
103136
"vlist": [
104137
{
@@ -153,6 +186,13 @@ BFT traversal: '{
153186

154187
## Release
155188

189+
**v0.6 is a bug fix release that's API-compatible with v0.5**
190+
191+
- DFT algorithm bug fixes impacting order and identity of client visitor callbacks.
192+
- Better error handling on bad developer-supplied visitor interfaces.
193+
- Better error handling for BFT/DFT algorithm empty start vector case.
194+
- You can now set `name` and `description` string properties on a `DirectedGraph`:
195+
156196
**v0.5 is a breaking upgrade for users of v0.4**
157197

158198
- Stylistic changes are required to v0.4 clients to upgrade.
@@ -165,7 +205,7 @@ BFT traversal: '{
165205

166206
## API
167207

168-
v0.5 jsgraph has the following public export object:
208+
v0.6 jsgraph has the following public export object:
169209

170210
```javascript
171211
var jsgraph = require('jsgraph');
@@ -235,6 +275,8 @@ The `DirectedGraph` container object created by this process models "a graph" ge
235275

236276
#### DirectedGraph container methods
237277

278+
- `get/setGraphName()` - get/set the name of the graph
279+
- `get/setGraphDescription()` - set/set the description of the graph
238280
- `verticesCount()` - obtain the count of vertices in the container
239281
- `getVertices()` - retrieve an array of ID strings for all vertices in the container
240282
- `edgesCount()` - obtain the count of edges in the container
@@ -303,16 +345,6 @@ Supported visitor interface callbacks for depth-first traversal: `initializeVert
303345

304346
A depth-first traversal concludes when all reacable vertices have been visited, or when the client signals termination by returning Boolean **false** back to the algorithm from one of its visitor interface callback functions.
305347

306-
## More examples
307-
308-
The best public examples of how to use jsgraph v0.5 are embedded in the module's test suite. Take a look at the ./test directory scripts.
309-
310-
## Support
311-
312-
I'm happy to answer questions and help you get going with jsgraph in your project.
313-
314-
Ping me on Twitter ([@AlpineLakes](https://twitter.com/AlpineLakes)) or file [Issues](https://github.com/Encapsule/jsgraph/issues) tagged with the 'question' label.
315-
316348
<hr>
317349

318350
Copyright &copy; 2014-2015 [Christopher D. Russell](https://github.com/ChrisRus)

docs/begin-at-the-beginning.jpg

25.2 KB
Loading

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsgraph",
3-
"version": "0.5.14",
3+
"version": "0.6.1",
44
"description": "Generic directed graph container, and visitor algorithms based on a port of the Boost C++ Graph Library API.",
55
"main": "index.js",
66
"scripts": {

src/digraph-algorithm-bft.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
}
5555
*/
5656

57-
var helperFunctions = require('./helper-functions');
57+
var algorithmName = "BFT"; // constant string used in error messages
5858
var colors = require('./digraph-algorithm-common-colors');
5959
var visitorCallback = require('./digraph-algorithm-common-visit');
6060
var normalizeRequest = require('./digraph-algorithm-common-request');
@@ -83,7 +83,7 @@ module.exports = function (request_) {
8383
// initializeVertex visitor callback.
8484
if (nrequest.options.traverseContext.searchStatus === 'pending') {
8585
for (vertexId in nrequest.options.traverseContext.colorMap) {
86-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'initializeVertex', request: { u: vertexId, g: nrequest.digraph }});
86+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'initializeVertex', request: { u: vertexId, g: nrequest.digraph }});
8787
if (innerResponse.error) {
8888
errors.unshift(innerResponse.error);
8989
break;
@@ -119,7 +119,7 @@ module.exports = function (request_) {
119119

120120
// startVertex visitor callback.
121121
if (nrequest.options.signalStart) {
122-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'startVertex', request: { u: startingVertexId, g: nrequest.digraph }});
122+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'startVertex', request: { u: startingVertexId, g: nrequest.digraph }});
123123
if (innerResponse.error) {
124124
errors.unshift(innerResponse.error);
125125
break;
@@ -133,7 +133,7 @@ module.exports = function (request_) {
133133
}
134134

135135
// discoverVertex visitor callback.
136-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'discoverVertex', request: { u: startingVertexId, g: nrequest.digraph }});
136+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'discoverVertex', request: { u: startingVertexId, g: nrequest.digraph }});
137137
if (innerResponse.error) {
138138
errors.unshift(innerResponse.error);
139139
break;
@@ -165,7 +165,7 @@ module.exports = function (request_) {
165165
nrequest.options.traverseContext.colorMap[vertexId] = colors.black;
166166

167167
// examineVertex visitor callback.
168-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'examineVertex', request: { u: vertexId, g: nrequest.digraph }});
168+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'examineVertex', request: { u: vertexId, g: nrequest.digraph }});
169169
if (innerResponse.error) {
170170
errors.unshift(innerResponse.error);
171171
break;
@@ -182,7 +182,7 @@ module.exports = function (request_) {
182182
var outEdge = outEdges[index];
183183

184184
// examineEdge visitor callback.
185-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'examineEdge', request: { e: outEdge, g: nrequest.digraph }});
185+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'examineEdge', request: { e: outEdge, g: nrequest.digraph }});
186186
if (innerResponse.error) {
187187
errors.unshift(innerResponse.error);
188188
break;
@@ -197,7 +197,7 @@ module.exports = function (request_) {
197197

198198
case colors.white:
199199
// discoverVertex visitor callback.
200-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'discoverVertex', request: { u: outEdge.v, g: nrequest.digraph }});
200+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'discoverVertex', request: { u: outEdge.v, g: nrequest.digraph }});
201201
if (innerResponse.error) {
202202
errors.unshift(innerResponse.error);
203203
break;
@@ -208,7 +208,7 @@ module.exports = function (request_) {
208208
break;
209209
}
210210
// treeEdge visitor callback.
211-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'treeEdge', request: { e: outEdge, g: nrequest.digraph }});
211+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'treeEdge', request: { e: outEdge, g: nrequest.digraph }});
212212
if (innerResponse.error) {
213213
errors.unshift(innerResponse.error);
214214
break;
@@ -220,15 +220,15 @@ module.exports = function (request_) {
220220

221221
case colors.gray:
222222
// nonTreeEdge visitor callback.
223-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'nonTreeEdge', request: { e: outEdge, g: nrequest.digraph }});
223+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'nonTreeEdge', request: { e: outEdge, g: nrequest.digraph }});
224224
if (innerResponse.error) {
225225
errors.unshift(innerResponse.error);
226226
break;
227227
}
228228
continueSearch = innerResponse.result;
229229
if (continueSearch) {
230230
// grayTarget visitor callback.
231-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'grayTarget', request: { e: outEdge, g: nrequest.digraph }});
231+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'grayTarget', request: { e: outEdge, g: nrequest.digraph }});
232232
if (innerResponse.error) {
233233
errors.unshift(innerResponse.error);
234234
break;
@@ -239,15 +239,15 @@ module.exports = function (request_) {
239239

240240
case colors.black:
241241
// nonTreeEdge visitor callback.
242-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'nonTreeEdge', request: { e: outEdge, g: nrequest.digraph }});
242+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'nonTreeEdge', request: { e: outEdge, g: nrequest.digraph }});
243243
if (innerResponse.error) {
244244
errors.unshift(innerResponse.error);
245245
break;
246246
}
247247
continueSearch = innerResponse.result;
248248
if (continueSearch) {
249249
// blackTarget visitor callback.
250-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'blackTarget', request: { e: outEdge, g: nrequest.digraph }});
250+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'blackTarget', request: { e: outEdge, g: nrequest.digraph }});
251251
if (innerResponse.error) {
252252
errors.unshift(innerResponse.error);
253253
break;
@@ -273,7 +273,7 @@ module.exports = function (request_) {
273273
}
274274

275275
// finishVertex visitor callback.
276-
innerResponse = visitorCallback({ visitor: nrequest.visitor, method: 'finishVertex', request: { u: vertexId, g: nrequest.digraph }});
276+
innerResponse = visitorCallback({ algorithm: algorithmName, visitor: nrequest.visitor, method: 'finishVertex', request: { u: vertexId, g: nrequest.digraph }});
277277
if (innerResponse.error) {
278278
errors.unshift(innerResponse.error);
279279
break;

src/digraph-algorithm-common-request.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
var helperFunctions = require('./helper-functions');
14-
var createBreadthFirstTraverseContext = require('./digraph-algorithm-common-context');
14+
var TRAVERSE_CONTEXT = require('./digraph-algorithm-common-context');
1515

1616
/*
1717
request = {
@@ -46,7 +46,7 @@ module.exports = function (request_) {
4646
var inBreakScope = false;
4747

4848
var createTraverseContext = function() {
49-
var response = createBreadthFirstTraverseContext({ digraph: nrequest.digraph });
49+
var response = TRAVERSE_CONTEXT({ digraph: nrequest.digraph });
5050
var result = null;
5151
if (response.error) {
5252
errors.unshift(response.error);
@@ -146,7 +146,7 @@ module.exports = function (request_) {
146146

147147
// Ensure that the starting vertex set is not empty (unless allowed).
148148
if (!nrequest.options.startVector.length && !nrequest.options.allowEmptyStartVector) {
149-
errors.unshift("You have specified an empty starting vertex set for this traversal. This is allowed only if you set request.options.allowEmptyStartVector === true.");
149+
errors.unshift("Traversal aborted because we don't know which vertex to start on. Specify a graph that has at least one root vertex, explicity specify the start vertex (or vertices) via `request.options.startVector` array, or suppress this error by setting `request.options.allowEmptyStartVector` to Boolean true.");
150150
break;
151151
}
152152

src/digraph-algorithm-common-visit.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var helperFunctions = require('./helper-functions');
1717
/*
1818
request = {
1919
visitor: interface object reference
20+
algorithm: string name of the algorithm for error messages
2021
method: string name of the visitor method to call
2122
request: request object to pass to the visitor method
2223
},
@@ -37,13 +38,17 @@ module.exports = function (request_) {
3738
var jstype = helperFunctions.JSType(visitorCallback);
3839
// If the visitor function is not defined on the visitor object, return true to continue the search.
3940
if (jstype !== '[object Function]') {
41+
if (jstype !== '[object Undefined]') {
42+
errors.unshift(request_.algorithm + " visitor interface method '" + request_.method + "' is type '" + jstype + "' instead of '[object Function]' as expected.");
43+
break;
44+
}
4045
response.result = true;
4146
break;
4247
}
4348
var continueSearch = visitorCallback(request_.request);
4449
jstype = helperFunctions.JSType(continueSearch);
4550
if (jstype !== '[object Boolean]') {
46-
errors.unshift("BFS visitor." + request_.method + " returned type '" + jstype + "' instead of expected '[object Boolean]'.");
51+
errors.unshift(request_.algorithm + " visitor interface error in callback function '" + request_.method + "'. Function returned type '" + jstype + "' instead of expected '[object Boolean]'.");
4752
break;
4853
}
4954
response.result = continueSearch;

0 commit comments

Comments
 (0)