graphology
Advanced tools
Comparing version 0.3.0 to 0.4.0
# Changelog | ||
## 0.4.0 | ||
* Adding `#.removeNodeAttribute` & `#.removeEdgeAttribute` that was actually missing from earlier versions. | ||
* Adding graph attributes. | ||
* `nodeUpdated` & `edgeUpdated` changing to `nodeAttributesUpdated` & `edgeAttributesUpdated` respectively. | ||
## 0.3.0 | ||
@@ -4,0 +10,0 @@ |
@@ -179,3 +179,3 @@ 'use strict'; | ||
function attachAttributeSetter(Class, method, key, elementName, checker, finder) { | ||
var eventName = elementName === 'node' ? 'nodeUpdated' : 'edgeUpdated'; | ||
var eventName = elementName === 'node' ? 'nodeAttributesUpdated' : 'edgeAttributesUpdated'; | ||
@@ -247,3 +247,3 @@ /** | ||
function attachAttributeUpdater(Class, method, key, elementName, checker, finder) { | ||
var eventName = elementName === 'node' ? 'nodeUpdated' : 'edgeUpdated'; | ||
var eventName = elementName === 'node' ? 'nodeAttributesUpdated' : 'edgeAttributesUpdated'; | ||
@@ -306,2 +306,65 @@ /** | ||
/** | ||
* Attach an attribute remover method onto the provided class. | ||
* | ||
* @param {function} Class - Target class. | ||
* @param {string} method - Method name. | ||
* @param {string} key - Key of the element's storage on instance. | ||
* @param {string} elementName - Name of target element for messages. | ||
* @param {string} checker - Name of the checker method to use. | ||
* @param {string} [finder] - Name of the finder method to use. | ||
*/ | ||
function attachAttributeRemover(Class, method, key, elementName, checker, finder) { | ||
var eventName = elementName === 'node' ? 'nodeAttributesUpdated' : 'edgeAttributesUpdated'; | ||
/** | ||
* Remove the desired attribute for the given element (node or edge). | ||
* | ||
* Arity 2: | ||
* @param {any} element - Target element. | ||
* @param {string} name - Attribute's name. | ||
* | ||
* Arity 3 (only for edges): | ||
* @param {any} source - Source element. | ||
* @param {any} target - Target element. | ||
* @param {string} name - Attribute's name. | ||
* | ||
* @return {Graph} - Returns itself for chaining. | ||
* | ||
* @throws {Error} - Will throw if too many arguments are provided. | ||
* @throws {Error} - Will throw if any of the elements is not found. | ||
*/ | ||
Class.prototype[method] = function (element, name) { | ||
if (arguments.length > 2) { | ||
if (!finder) throw new _errors.InvalidArgumentsGraphError('Graph.' + method + ': too many arguments provided.'); | ||
var source = element, | ||
target = name; | ||
name = arguments[2]; | ||
if (!this[checker](source, target)) throw new _errors.NotFoundGraphError('Graph.' + method + ': could not find an edge for the given path ("' + source + '" - "' + target + '").'); | ||
element = this[finder](source, target); | ||
} | ||
if (!this[checker](element)) throw new _errors.NotFoundGraphError('Graph.' + method + ': could not find the "' + element + '" ' + elementName + ' in the graph.'); | ||
var data = this[key].get(element); | ||
delete data.attributes[name]; | ||
// Emitting | ||
this.emit(eventName, { | ||
key: element, | ||
type: 'remove', | ||
meta: { | ||
name: name | ||
} | ||
}); | ||
return this; | ||
}; | ||
} | ||
/** | ||
* Attach an attribute replacer method onto the provided class. | ||
@@ -317,3 +380,3 @@ * | ||
function attachAttributesReplacer(Class, method, key, elementName, checker, finder) { | ||
var eventName = elementName === 'node' ? 'nodeUpdated' : 'edgeUpdated'; | ||
var eventName = elementName === 'node' ? 'nodeAttributesUpdated' : 'edgeAttributesUpdated'; | ||
@@ -386,3 +449,3 @@ /** | ||
function attachAttributesMerger(Class, method, key, elementName, checker, finder) { | ||
var eventName = elementName === 'node' ? 'nodeUpdated' : 'edgeUpdated'; | ||
var eventName = elementName === 'node' ? 'nodeAttributesUpdated' : 'edgeAttributesUpdated'; | ||
@@ -471,2 +534,7 @@ /** | ||
name: function name(element) { | ||
return 'remove' + element + 'Attribute'; | ||
}, | ||
attacher: attachAttributeRemover | ||
}, { | ||
name: function name(element) { | ||
return 'replace' + element + 'Attributes'; | ||
@@ -473,0 +541,0 @@ }, |
@@ -250,2 +250,5 @@ 'use strict'; | ||
// Storing whether the id was generated | ||
if (mustGenerateId) data.generatedId = true; | ||
// Adding the edge to the internal register | ||
@@ -366,2 +369,3 @@ graph._edges.set(edge, data); | ||
// Indexes | ||
(0, _utils.privateProperty)(_this, '_attributes', {}); | ||
(0, _utils.privateProperty)(_this, '_nodes', (0, _utils.createInternalMap)()); | ||
@@ -413,2 +417,162 @@ (0, _utils.privateProperty)(_this, '_edges', (0, _utils.createInternalMap)()); | ||
/** | ||
* Method returning the desired graph's attribute. | ||
* | ||
* @param {string} name - Name of the attribute. | ||
* @return {any} | ||
*/ | ||
Graph.prototype.getAttribute = function getAttribute(name) { | ||
return this._attributes[name]; | ||
}; | ||
/** | ||
* Method returning the graph's attributes. | ||
* | ||
* @return {object} | ||
*/ | ||
Graph.prototype.getAttributes = function getAttributes() { | ||
return this._attributes; | ||
}; | ||
/** | ||
* Method returning whether the graph has the desired attribute. | ||
* | ||
* @param {string} name - Name of the attribute. | ||
* @return {boolean} | ||
*/ | ||
Graph.prototype.hasAttribute = function hasAttribute(name) { | ||
return this._attributes.hasOwnProperty(name); | ||
}; | ||
/** | ||
* Method setting a value for the desired graph's attribute. | ||
* | ||
* @param {string} name - Name of the attribute. | ||
* @param {any} value - Value for the attribute. | ||
* @return {Graph} | ||
*/ | ||
Graph.prototype.setAttribute = function setAttribute(name, value) { | ||
this._attributes[name] = value; | ||
// Emitting | ||
this.emit('attributesUpdated', { | ||
type: 'set', | ||
meta: { | ||
name: name, | ||
value: value | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Method using a function to update the desired graph's attribute's value. | ||
* | ||
* @param {string} name - Name of the attribute. | ||
* @param {function} updater - Function use to update the attribute's value. | ||
* @return {Graph} | ||
*/ | ||
Graph.prototype.updateAttribute = function updateAttribute(name, updater) { | ||
this._attributes[name] = updater(this._attributes[name]); | ||
// Emitting | ||
this.emit('attributesUpdated', { | ||
type: 'set', | ||
meta: { | ||
name: name, | ||
value: this._attributes[name] | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Method removing the desired graph's attribute. | ||
* | ||
* @param {string} name - Name of the attribute. | ||
* @return {Graph} | ||
*/ | ||
Graph.prototype.removeAttribute = function removeAttribute(name) { | ||
delete this._attributes[name]; | ||
// Emitting | ||
this.emit('attributesUpdated', { | ||
type: 'remove', | ||
meta: { | ||
name: name | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Method replacing the graph's attributes. | ||
* | ||
* @param {object} attributes - New attributes. | ||
* @return {Graph} | ||
* | ||
* @throws {Error} - Will throw if given attributes are not a plain object. | ||
*/ | ||
Graph.prototype.replaceAttributes = function replaceAttributes(attributes) { | ||
if (!(0, _utils.isPlainObject)(attributes)) throw new _errors.InvalidArgumentsGraphError('Graph.replaceAttributes: provided attributes are not a plain object.'); | ||
var before = this._attributes; | ||
this._attributes = attributes; | ||
// Emitting | ||
this.emit('attributesUpdated', { | ||
type: 'replace', | ||
meta: { | ||
before: before, | ||
after: attributes | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Method merging the graph's attributes. | ||
* | ||
* @param {object} attributes - Attributes to merge. | ||
* @return {Graph} | ||
* | ||
* @throws {Error} - Will throw if given attributes are not a plain object. | ||
*/ | ||
Graph.prototype.mergeAttributes = function mergeAttributes(attributes) { | ||
if (!(0, _utils.isPlainObject)(attributes)) throw new _errors.InvalidArgumentsGraphError('Graph.mergeAttributes: provided attributes are not a plain object.'); | ||
this._attributes = (0, _utils.assign)(this._attributes, attributes); | ||
// Emitting | ||
this.emit('attributesUpdated', { | ||
type: 'merge', | ||
meta: { | ||
data: attributes | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* Method returning whether the given node is found in the graph. | ||
@@ -1504,6 +1668,12 @@ * | ||
// Importing a serialized graph | ||
if (!(0, _utils.isPlainObject)(data) || !data.nodes) throw new _errors.InvalidArgumentsGraphError('Graph.import: invalid argument. Expecting an object with at least a "nodes" property or, alternatively, a Graph instance.'); | ||
if (!(0, _utils.isPlainObject)(data)) throw new _errors.InvalidArgumentsGraphError('Graph.import: invalid argument. Expecting a serialized graph or, alternatively, a Graph instance.'); | ||
this.importNodes(data.nodes, merge); | ||
if (data.attributes) { | ||
if (!(0, _utils.isPlainObject)(data.attributes)) throw new _errors.InvalidArgumentsGraphError('Graph.import: invalid attributes. Expecting a plain object.'); | ||
if (merge) this.mergeAttributes(data.attributes);else this.replaceAttributes(data.attributes); | ||
} | ||
if (data.nodes) this.importNodes(data.nodes, merge); | ||
if (data.edges) this.importEdges(data.edges, merge); | ||
@@ -1636,18 +1806,18 @@ | ||
Graph.prototype.inspect = function inspect() { | ||
var nodes = Object.create(null); | ||
var nodes = {}; | ||
this._nodes.forEach(function (data, key) { | ||
nodes[key] = data.attributes; | ||
}); | ||
this._nodes.forEach(function (value, key) { | ||
var attributes = value.attributes; | ||
var edges = {}; | ||
this._edges.forEach(function (data, key) { | ||
var direction = data.undirected ? '<->' : '->'; | ||
nodes[key] = Object.keys(attributes).length ? attributes : '<empty>'; | ||
}); | ||
var label = ''; | ||
var edges = []; | ||
this._edges.forEach(function (value, key) { | ||
if (!data.generatedId) label += '[' + key + ']: '; | ||
var formatted = [key, value.source, value.undirected ? '<->' : '->', value.target]; | ||
label += '(' + data.source + ')' + direction + '(' + data.target + ')'; | ||
if (Object.keys(value.attributes).length) formatted.push(value.attributes); | ||
edges.push(formatted); | ||
edges[label] = data.attributes; | ||
}); | ||
@@ -1661,2 +1831,3 @@ | ||
dummy.attributes = this._attributes; | ||
dummy.nodes = nodes; | ||
@@ -1663,0 +1834,0 @@ dummy.edges = edges; |
{ | ||
"name": "graphology", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "A robust and multipurpose Graph object for JavaScript.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -24,3 +24,3 @@ 'use strict'; | ||
var METHODS = ['getNodeAttribute', 'getNodeAttributes', 'hasNodeAttribute', 'getEdgeAttribute', 'getEdgeAttributes', 'hasEdgeAttribute', 'setNodeAttribute', 'setEdgeAttribute', 'updateNodeAttribute', 'updateEdgeAttribute', 'replaceNodeAttributes', 'replaceEdgeAttributes', 'mergeNodeAttributes', 'mergeEdgeAttributes']; | ||
var METHODS = ['getNodeAttribute', 'getNodeAttributes', 'hasNodeAttribute', 'getEdgeAttribute', 'getEdgeAttributes', 'hasEdgeAttribute', 'setNodeAttribute', 'setEdgeAttribute', 'updateNodeAttribute', 'updateEdgeAttribute', 'removeNodeAttribute', 'removeEdgeAttribute', 'replaceNodeAttributes', 'replaceEdgeAttributes', 'mergeNodeAttributes', 'mergeEdgeAttributes']; | ||
@@ -61,2 +61,17 @@ function attributes(Graph, checkers) { | ||
return (0, _helpers.deepMerge)(tests, { | ||
'#.getAttribute': { | ||
'it should return the correct value.': function itShouldReturnTheCorrectValue() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
_assert2.default.strictEqual(graph.getAttribute('name'), 'graph'); | ||
}, | ||
'it should return undefined if the attribute does not exist.': function itShouldReturnUndefinedIfTheAttributeDoesNotExist() { | ||
var graph = new Graph(); | ||
_assert2.default.strictEqual(graph.getAttribute('name'), undefined); | ||
} | ||
}, | ||
'#.getNodeAttribute': { | ||
@@ -99,2 +114,18 @@ | ||
'#.getAttributes': { | ||
'it should return the correct value.': function itShouldReturnTheCorrectValue() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
_assert2.default.deepEqual(graph.getAttributes(), { name: 'graph' }); | ||
}, | ||
'it should return an empty object if the node does not have attributes.': function itShouldReturnAnEmptyObjectIfTheNodeDoesNotHaveAttributes() { | ||
var graph = new Graph(); | ||
_assert2.default.deepEqual(graph.getAttributes(), {}); | ||
} | ||
}, | ||
'#.getNodeAttributes': { | ||
@@ -137,2 +168,19 @@ | ||
'#.hasAttribute': { | ||
'it should correctly return whether the attribute is set.': function itShouldCorrectlyReturnWhetherTheAttributeIsSet() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
_assert2.default.strictEqual(graph.hasAttribute('name'), true); | ||
_assert2.default.strictEqual(graph.hasAttribute('info'), false); | ||
}, | ||
'it does not fail with typical prototypal properties.': function itDoesNotFailWithTypicalPrototypalProperties() { | ||
var graph = new Graph(); | ||
_assert2.default.strictEqual(graph.hasAttribute('toString'), false); | ||
} | ||
}, | ||
'#.hasNodeAttribute': { | ||
@@ -176,2 +224,12 @@ | ||
'#.setAttribute': { | ||
'it should correctly set the graph\'s attribute.': function itShouldCorrectlySetTheGraphSAttribute() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
_assert2.default.strictEqual(graph.getAttribute('name'), 'graph'); | ||
} | ||
}, | ||
'#.setNodeAttribute': { | ||
@@ -202,2 +260,27 @@ | ||
'#.updateAttribute': { | ||
'it should correctly set the graph\'s attribute.': function itShouldCorrectlySetTheGraphSAttribute() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
graph.updateAttribute('name', function (name) { | ||
return name + '1'; | ||
}); | ||
_assert2.default.strictEqual(graph.getAttribute('name'), 'graph1'); | ||
}, | ||
'the given value should be undefined if not found.': function theGivenValueShouldBeUndefinedIfNotFound() { | ||
var graph = new Graph(); | ||
var updater = function updater(x) { | ||
_assert2.default.strictEqual(x, undefined); | ||
return 'graph'; | ||
}; | ||
graph.updateAttribute('name', updater); | ||
_assert2.default.strictEqual(graph.getAttribute('name'), 'graph'); | ||
} | ||
}, | ||
'#.updateNodeAttribute': { | ||
@@ -261,2 +344,60 @@ | ||
'#.removeAttribute': { | ||
'it should correctly remove the attribute.': function itShouldCorrectlyRemoveTheAttribute() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
graph.removeAttribute('name'); | ||
_assert2.default.strictEqual(graph.hasAttribute('name'), false); | ||
_assert2.default.deepEqual(graph.getAttributes(), {}); | ||
} | ||
}, | ||
'#.removeNodeAttribute': { | ||
'it should correctly remove the attribute.': function itShouldCorrectlyRemoveTheAttribute() { | ||
var graph = new Graph(); | ||
graph.addNode('Martha', { age: 34 }); | ||
graph.removeNodeAttribute('Martha', 'age'); | ||
_assert2.default.strictEqual(graph.hasNodeAttribute('Martha', 'age'), false); | ||
_assert2.default.deepEqual(graph.getNodeAttributes('Martha'), {}); | ||
} | ||
}, | ||
'#.removeEdgeAttribute': { | ||
'it should correclty remove the attribute.': function itShouldCorrecltyRemoveTheAttribute() { | ||
var graph = new Graph(); | ||
var edge = graph.mergeEdge('John', 'Martha', { weight: 1, size: 3 }); | ||
graph.removeEdgeAttribute('John', 'Martha', 'weight'); | ||
graph.removeEdgeAttribute(edge, 'size'); | ||
_assert2.default.strictEqual(graph.hasEdgeAttribute(edge, 'weight'), false); | ||
_assert2.default.strictEqual(graph.hasEdgeAttribute(edge, 'size'), false); | ||
_assert2.default.deepEqual(graph.getEdgeAttributes(edge), {}); | ||
} | ||
}, | ||
'#.replaceAttribute': { | ||
'it should throw if given attributes are not a plain object.': function itShouldThrowIfGivenAttributesAreNotAPlainObject() { | ||
var graph = new Graph(); | ||
_assert2.default.throws(function () { | ||
graph.replaceAttributes(true); | ||
}, invalid()); | ||
}, | ||
'it should correctly replace attributes.': function itShouldCorrectlyReplaceAttributes() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
graph.replaceAttributes({ name: 'other graph' }); | ||
_assert2.default.deepEqual(graph.getAttributes(), { name: 'other graph' }); | ||
} | ||
}, | ||
'#.replaceNodeAttributes': { | ||
@@ -304,2 +445,21 @@ 'it should throw if given attributes are not a plain object.': function itShouldThrowIfGivenAttributesAreNotAPlainObject() { | ||
'#.mergeAttributes': { | ||
'it should throw if given attributes are not a plain object.': function itShouldThrowIfGivenAttributesAreNotAPlainObject() { | ||
var graph = new Graph(); | ||
_assert2.default.throws(function () { | ||
graph.mergeAttributes(true); | ||
}, invalid()); | ||
}, | ||
'it should correctly merge attributes.': function itShouldCorrectlyMergeAttributes() { | ||
var graph = new Graph(); | ||
graph.setAttribute('name', 'graph'); | ||
graph.mergeAttributes({ color: 'blue' }); | ||
_assert2.default.deepEqual(graph.getAttributes(), { name: 'graph', color: 'blue' }); | ||
} | ||
}, | ||
'#.mergeNodeAttributes': { | ||
@@ -306,0 +466,0 @@ 'it should throw if given attributes are not a plain object.': function itShouldThrowIfGivenAttributesAreNotAPlainObject() { |
@@ -22,3 +22,3 @@ 'use strict'; | ||
*/ | ||
var VALID_TYPES = new Set(['set', 'merge', 'replace']); | ||
var VALID_TYPES = new Set(['set', 'merge', 'replace', 'remove']); | ||
@@ -119,3 +119,3 @@ function events(Graph) { | ||
'nodeUpdated': { | ||
'nodeAttributesUpdated': { | ||
'it should fire when a node is updated.': function itShouldFireWhenANodeIsUpdated() { | ||
@@ -140,2 +140,4 @@ var graph = new Graph(); | ||
_assert2.default.deepEqual(meta.after, { age: 56 }); | ||
} else if (type === 'remove') { | ||
_assert2.default.strictEqual(meta.name, 'eyes'); | ||
} else { | ||
@@ -146,3 +148,3 @@ _assert2.default.deepEqual(meta.data, { eyes: 'blue' }); | ||
graph.on('nodeUpdated', handler); | ||
graph.on('nodeAttributesUpdated', handler); | ||
@@ -153,8 +155,9 @@ graph.addNode('John'); | ||
graph.mergeNodeAttributes('John', { eyes: 'blue' }); | ||
graph.removeNodeAttribute('John', 'eyes'); | ||
_assert2.default.strictEqual(handler.times, 3); | ||
_assert2.default.strictEqual(handler.times, 4); | ||
} | ||
}, | ||
'edgeUpdated': { | ||
'edgeAttributesUpdated': { | ||
'it should fire when a node is updated.': function itShouldFireWhenANodeIsUpdated() { | ||
@@ -179,2 +182,4 @@ var graph = new Graph(); | ||
_assert2.default.deepEqual(meta.after, { weight: 56 }); | ||
} else if (type === 'remove') { | ||
_assert2.default.strictEqual(meta.name, 'type'); | ||
} else { | ||
@@ -185,3 +190,3 @@ _assert2.default.deepEqual(meta.data, { type: 'KNOWS' }); | ||
graph.on('edgeUpdated', handler); | ||
graph.on('edgeAttributesUpdated', handler); | ||
@@ -193,4 +198,5 @@ graph.addNodesFrom(['John', 'Thomas']); | ||
graph.mergeEdgeAttributes('J->T', { type: 'KNOWS' }); | ||
graph.removeEdgeAttribute('J->T', 'type'); | ||
_assert2.default.strictEqual(handler.times, 3); | ||
_assert2.default.strictEqual(handler.times, 4); | ||
} | ||
@@ -197,0 +203,0 @@ } |
@@ -374,6 +374,2 @@ 'use strict'; | ||
}, invalid()); | ||
_assert2.default.throws(function () { | ||
graph.import({ hello: 'world' }); | ||
}, invalid()); | ||
}, | ||
@@ -402,2 +398,26 @@ | ||
_assert2.default.strictEqual(graph.hasEdge('John', 'Thomas'), true); | ||
}, | ||
'it should be possible to import only edges when merging.': function itShouldBePossibleToImportOnlyEdgesWhenMerging() { | ||
var graph = new Graph(); | ||
graph.import({ | ||
edges: [{ source: 'John', target: 'Thomas' }] | ||
}, true); | ||
_assert2.default.deepEqual(graph.nodes(), ['John', 'Thomas']); | ||
_assert2.default.strictEqual(graph.size, 1); | ||
_assert2.default.strictEqual(graph.hasEdge('John', 'Thomas'), true); | ||
}, | ||
'it should be possible to import attributes.': function itShouldBePossibleToImportAttributes() { | ||
var graph = new Graph(); | ||
graph.import({ | ||
attributes: { | ||
name: 'graph' | ||
} | ||
}); | ||
_assert2.default.deepEqual(graph.getAttributes(), { name: 'graph' }); | ||
} | ||
@@ -404,0 +424,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
256474
5670