From 54812d1e02d0277dce8a016b2a74cbf72deee25a Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Tue, 11 Nov 2014 18:22:07 -0300 Subject: [PATCH 1/7] Model as an EventEmitter --- js/node/node_modules/eventemitter2/README.md | 248 ++++++++ js/node/node_modules/eventemitter2/index.js | 1 + .../eventemitter2/lib/eventemitter2.js | 573 ++++++++++++++++++ .../node_modules/eventemitter2/package.json | 82 +++ js/node/package.json | 1 + js/server/modules/org/arangodb/foxx/model.js | 5 + .../tests/shell-foxx-model-events-spec.js | 14 + 7 files changed, 924 insertions(+) create mode 100644 js/node/node_modules/eventemitter2/README.md create mode 100644 js/node/node_modules/eventemitter2/index.js create mode 100644 js/node/node_modules/eventemitter2/lib/eventemitter2.js create mode 100644 js/node/node_modules/eventemitter2/package.json create mode 100644 js/server/tests/shell-foxx-model-events-spec.js diff --git a/js/node/node_modules/eventemitter2/README.md b/js/node/node_modules/eventemitter2/README.md new file mode 100644 index 0000000000..e1f6edd6b6 --- /dev/null +++ b/js/node/node_modules/eventemitter2/README.md @@ -0,0 +1,248 @@ +[![build-status](https://www.codeship.io/projects/3ad58940-4c7d-0131-15d5-5a8cd3f550f8/status)](https://www.codeship.io/projects/11259) + +# SYNOPSIS + +EventEmitter2 is an implementation of the EventEmitter found in Node.js + +# DESCRIPTION + +### FEATURES + - Namespaces/Wildcards. + - Times To Listen (TTL), extends the `once` concept with `many`. + - Browser environment compatibility. + - Demonstrates good performance in benchmarks + +``` +EventEmitterHeatUp x 3,728,965 ops/sec \302\2610.68% (60 runs sampled) +EventEmitter x 2,822,904 ops/sec \302\2610.74% (63 runs sampled) +EventEmitter2 x 7,251,227 ops/sec \302\2610.55% (58 runs sampled) +EventEmitter2 (wild) x 3,220,268 ops/sec \302\2610.44% (65 runs sampled) +Fastest is EventEmitter2 +``` + +### Differences (Non breaking, compatible with existing EventEmitter) + + - The constructor takes a configuration object. + +```javascript + var EventEmitter2 = require('eventemitter2').EventEmitter2; + var server = new EventEmitter2({ + + // + // use wildcards. + // + wildcard: true, + + // + // the delimiter used to segment namespaces, defaults to `.`. + // + delimiter: '::', + + // + // if you want to emit the newListener event set to true. + // + newListener: false, + + // + // max listeners that can be assigned to an event, default 10. + // + maxListeners: 20 + }); +``` + + - Getting the actual event that fired. + +```javascript + server.on('foo.*', function(value1, value2) { + console.log(this.event, value1, value2); + }); +``` + + - Fire an event N times and then remove it, an extension of the `once` concept. + +```javascript + server.many('foo', 4, function() { + console.log('hello'); + }); +``` + + - Pass in a namespaced event as an array rather than a delimited string. + +```javascript + server.many(['foo', 'bar', 'bazz'], function() { + console.log('hello'); + }); +``` + + +# API + +When an `EventEmitter` instance experiences an error, the typical action is +to emit an `error` event. Error events are treated as a special case. +If there is no listener for it, then the default action is to print a stack +trace and exit the program. + +All EventEmitters emit the event `newListener` when new listeners are +added. + + +**Namespaces** with **Wildcards** +To use namespaces/wildcards, pass the `wildcard` option into the EventEmitter +constructor. When namespaces/wildcards are enabled, events can either be +strings (`foo.bar`) separated by a delimiter or arrays (`['foo', 'bar']`). The +delimiter is also configurable as a constructor option. + +An event name passed to any event emitter method can contain a wild card (the +`*` character). If the event name is a string, a wildcard may appear as `foo.*`. +If the event name is an array, the wildcard may appear as `['foo', '*']`. + +If either of the above described events were passed to the `on` method, +subsequent emits such as the following would be observed... + +```javascript + emitter.emit('foo.bazz'); + emitter.emit(['foo', 'bar']); +``` + + +### emitter.addListener(event, listener) +### emitter.on(event, listener) + +Adds a listener to the end of the listeners array for the specified event. + +```javascript + server.on('data', function(value1, value2, value3, ...) { + console.log('The event was raised!'); + }); +``` + +```javascript + server.on('data', function(value) { + console.log('The event was raised!'); + }); +``` + +### emitter.onAny(listener) + +Adds a listener that will be fired when any event is emitted. + +```javascript + server.onAny(function(value) { + console.log('All events trigger this.'); + }); +``` + +### emitter.offAny(listener) + +Removes the listener that will be fired when any event is emitted. + +```javascript + server.offAny(function(value) { + console.log('The event was raised!'); + }); +``` + +#### emitter.once(event, listener) + +Adds a **one time** listener for the event. The listener is invoked +only the first time the event is fired, after which it is removed. + +```javascript + server.once('get', function (value) { + console.log('Ah, we have our first value!'); + }); +``` + +### emitter.many(event, timesToListen, listener) + +Adds a listener that will execute **n times** for the event before being +removed. The listener is invoked only the first **n times** the event is +fired, after which it is removed. + +```javascript + server.many('get', 4, function (value) { + console.log('This event will be listened to exactly four times.'); + }); +``` + + +### emitter.removeListener(event, listener) +### emitter.off(event, listener) + +Remove a listener from the listener array for the specified event. +**Caution**: changes array indices in the listener array behind the listener. + +```javascript + var callback = function(value) { + console.log('someone connected!'); + }; + server.on('get', callback); + // ... + server.removeListener('get', callback); +``` + + +### emitter.removeAllListeners([event]) + +Removes all listeners, or those of the specified event. + + +### emitter.setMaxListeners(n) + +By default EventEmitters will print a warning if more than 10 listeners +are added to it. This is a useful default which helps finding memory leaks. +Obviously not all Emitters should be limited to 10. This function allows +that to be increased. Set to zero for unlimited. + + +### emitter.listeners(event) + +Returns an array of listeners for the specified event. This array can be +manipulated, e.g. to remove listeners. + +```javascript + server.on('get', function(value) { + console.log('someone connected!'); + }); + console.log(server.listeners('get')); // [ [Function] ] +``` + +### emitter.listenersAny() + +Returns an array of listeners that are listening for any event that is +specified. This array can be manipulated, e.g. to remove listeners. + +```javascript + server.onAny(function(value) { + console.log('someone connected!'); + }); + console.log(server.listenersAny()[0]); // [ [Function] ] +``` + +### emitter.emit(event, [arg1], [arg2], [...]) + +Execute each of the listeners that may be listening for the specified event +name in order with the list of arguments. + +# LICENSE + +(The MIT License) + +Copyright (c) 2011 hij1nx + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the 'Software'), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/js/node/node_modules/eventemitter2/index.js b/js/node/node_modules/eventemitter2/index.js new file mode 100644 index 0000000000..6f583b5f6d --- /dev/null +++ b/js/node/node_modules/eventemitter2/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/eventemitter2'); diff --git a/js/node/node_modules/eventemitter2/lib/eventemitter2.js b/js/node/node_modules/eventemitter2/lib/eventemitter2.js new file mode 100644 index 0000000000..bde69e8532 --- /dev/null +++ b/js/node/node_modules/eventemitter2/lib/eventemitter2.js @@ -0,0 +1,573 @@ +/*! + * EventEmitter2 + * https://github.com/hij1nx/EventEmitter2 + * + * Copyright (c) 2013 hij1nx + * Licensed under the MIT license. + */ +;!function(undefined) { + + var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + var defaultMaxListeners = 10; + + function init() { + this._events = {}; + if (this._conf) { + configure.call(this, this._conf); + } + } + + function configure(conf) { + if (conf) { + + this._conf = conf; + + conf.delimiter && (this.delimiter = conf.delimiter); + conf.maxListeners && (this._events.maxListeners = conf.maxListeners); + conf.wildcard && (this.wildcard = conf.wildcard); + conf.newListener && (this.newListener = conf.newListener); + + if (this.wildcard) { + this.listenerTree = {}; + } + } + } + + function EventEmitter(conf) { + this._events = {}; + this.newListener = false; + configure.call(this, conf); + } + + // + // Attention, function return type now is array, always ! + // It has zero elements if no any matches found and one or more + // elements (leafs) if there are matches + // + function searchListenerTree(handlers, type, tree, i) { + if (!tree) { + return []; + } + var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, + typeLength = type.length, currentType = type[i], nextType = type[i+1]; + if (i === typeLength && tree._listeners) { + // + // If at the end of the event(s) list and the tree has listeners + // invoke those listeners. + // + if (typeof tree._listeners === 'function') { + handlers && handlers.push(tree._listeners); + return [tree]; + } else { + for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { + handlers && handlers.push(tree._listeners[leaf]); + } + return [tree]; + } + } + + if ((currentType === '*' || currentType === '**') || tree[currentType]) { + // + // If the event emitted is '*' at this part + // or there is a concrete match at this patch + // + if (currentType === '*') { + for (branch in tree) { + if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); + } + } + return listeners; + } else if(currentType === '**') { + endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); + if(endReached && tree._listeners) { + // The next element has a _listeners, add it to the handlers. + listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); + } + + for (branch in tree) { + if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { + if(branch === '*' || branch === '**') { + if(tree[branch]._listeners && !endReached) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); + } + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); + } else if(branch === nextType) { + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); + } else { + // No match on this one, shift into the tree but not in the type array. + listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); + } + } + } + return listeners; + } + + listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); + } + + xTree = tree['*']; + if (xTree) { + // + // If the listener tree will allow any match for this part, + // then recursively explore all branches of the tree + // + searchListenerTree(handlers, type, xTree, i+1); + } + + xxTree = tree['**']; + if(xxTree) { + if(i < typeLength) { + if(xxTree._listeners) { + // If we have a listener on a '**', it will catch all, so add its handler. + searchListenerTree(handlers, type, xxTree, typeLength); + } + + // Build arrays of matching next branches and others. + for(branch in xxTree) { + if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { + if(branch === nextType) { + // We know the next element will match, so jump twice. + searchListenerTree(handlers, type, xxTree[branch], i+2); + } else if(branch === currentType) { + // Current node matches, move into the tree. + searchListenerTree(handlers, type, xxTree[branch], i+1); + } else { + isolatedBranch = {}; + isolatedBranch[branch] = xxTree[branch]; + searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); + } + } + } + } else if(xxTree._listeners) { + // We have reached the end and still on a '**' + searchListenerTree(handlers, type, xxTree, typeLength); + } else if(xxTree['*'] && xxTree['*']._listeners) { + searchListenerTree(handlers, type, xxTree['*'], typeLength); + } + } + + return listeners; + } + + function growListenerTree(type, listener) { + + type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + + // + // Looks for two consecutive '**', if so, don't add the event at all. + // + for(var i = 0, len = type.length; i+1 < len; i++) { + if(type[i] === '**' && type[i+1] === '**') { + return; + } + } + + var tree = this.listenerTree; + var name = type.shift(); + + while (name) { + + if (!tree[name]) { + tree[name] = {}; + } + + tree = tree[name]; + + if (type.length === 0) { + + if (!tree._listeners) { + tree._listeners = listener; + } + else if(typeof tree._listeners === 'function') { + tree._listeners = [tree._listeners, listener]; + } + else if (isArray(tree._listeners)) { + + tree._listeners.push(listener); + + if (!tree._listeners.warned) { + + var m = defaultMaxListeners; + + if (typeof this._events.maxListeners !== 'undefined') { + m = this._events.maxListeners; + } + + if (m > 0 && tree._listeners.length > m) { + + tree._listeners.warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + tree._listeners.length); + console.trace(); + } + } + } + return true; + } + name = type.shift(); + } + return true; + } + + // By default EventEmitters will print a warning if more than + // 10 listeners are added to it. This is a useful default which + // helps finding memory leaks. + // + // Obviously not all Emitters should be limited to 10. This function allows + // that to be increased. Set to zero for unlimited. + + EventEmitter.prototype.delimiter = '.'; + + EventEmitter.prototype.setMaxListeners = function(n) { + this._events || init.call(this); + this._events.maxListeners = n; + if (!this._conf) this._conf = {}; + this._conf.maxListeners = n; + }; + + EventEmitter.prototype.event = ''; + + EventEmitter.prototype.once = function(event, fn) { + this.many(event, 1, fn); + return this; + }; + + EventEmitter.prototype.many = function(event, ttl, fn) { + var self = this; + + if (typeof fn !== 'function') { + throw new Error('many only accepts instances of Function'); + } + + function listener() { + if (--ttl === 0) { + self.off(event, listener); + } + fn.apply(this, arguments); + } + + listener._origin = fn; + + this.on(event, listener); + + return self; + }; + + EventEmitter.prototype.emit = function() { + + this._events || init.call(this); + + var type = arguments[0]; + + if (type === 'newListener' && !this.newListener) { + if (!this._events.newListener) { return false; } + } + + // Loop through the *_all* functions and invoke them. + if (this._all) { + var l = arguments.length; + var args = new Array(l - 1); + for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; + for (i = 0, l = this._all.length; i < l; i++) { + this.event = type; + this._all[i].apply(this, args); + } + } + + // If there is no 'error' event listener then throw. + if (type === 'error') { + + if (!this._all && + !this._events.error && + !(this.wildcard && this.listenerTree.error)) { + + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + var handler; + + if(this.wildcard) { + handler = []; + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + searchListenerTree.call(this, handler, ns, this.listenerTree, 0); + } + else { + handler = this._events[type]; + } + + if (typeof handler === 'function') { + this.event = type; + if (arguments.length === 1) { + handler.call(this); + } + else if (arguments.length > 1) + switch (arguments.length) { + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + var l = arguments.length; + var args = new Array(l - 1); + for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; + handler.apply(this, args); + } + return true; + } + else if (handler) { + var l = arguments.length; + var args = new Array(l - 1); + for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + this.event = type; + listeners[i].apply(this, args); + } + return (listeners.length > 0) || !!this._all; + } + else { + return !!this._all; + } + + }; + + EventEmitter.prototype.on = function(type, listener) { + + if (typeof type === 'function') { + this.onAny(type); + return this; + } + + if (typeof listener !== 'function') { + throw new Error('on only accepts instances of Function'); + } + this._events || init.call(this); + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + + if(this.wildcard) { + growListenerTree.call(this, type, listener); + return this; + } + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } + else if(typeof this._events[type] === 'function') { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + } + else if (isArray(this._events[type])) { + // If we've already got an array, just append. + this._events[type].push(listener); + + // Check for listener leak + if (!this._events[type].warned) { + + var m = defaultMaxListeners; + + if (typeof this._events.maxListeners !== 'undefined') { + m = this._events.maxListeners; + } + + if (m > 0 && this._events[type].length > m) { + + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + } + return this; + }; + + EventEmitter.prototype.onAny = function(fn) { + + if (typeof fn !== 'function') { + throw new Error('onAny only accepts instances of Function'); + } + + if(!this._all) { + this._all = []; + } + + // Add the function to the event listener collection. + this._all.push(fn); + return this; + }; + + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + EventEmitter.prototype.off = function(type, listener) { + if (typeof listener !== 'function') { + throw new Error('removeListener only takes instances of Function'); + } + + var handlers,leafs=[]; + + if(this.wildcard) { + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); + } + else { + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events[type]) return this; + handlers = this._events[type]; + leafs.push({_listeners:handlers}); + } + + for (var iLeaf=0; iLeaf 0) { + fns = this._all; + for(i = 0, l = fns.length; i < l; i++) { + if(fn === fns[i]) { + fns.splice(i, 1); + return this; + } + } + } else { + this._all = []; + } + return this; + }; + + EventEmitter.prototype.removeListener = EventEmitter.prototype.off; + + EventEmitter.prototype.removeAllListeners = function(type) { + if (arguments.length === 0) { + !this._events || init.call(this); + return this; + } + + if(this.wildcard) { + var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); + var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); + + for (var iLeaf=0; iLeaf= 0.2.2" + }, + "main": "./lib/eventemitter2.js", + "scripts": { + "test": "nodeunit test/simple/ && nodeunit test/wildcardEvents/", + "benchmark": "node test/perf/benchmark.js" + }, + "files": [ + "lib/eventemitter2.js", + "index.js" + ], + "bugs": { + "url": "https://github.com/hij1nx/EventEmitter2/issues" + }, + "homepage": "https://github.com/hij1nx/EventEmitter2", + "_id": "eventemitter2@0.4.14", + "_shasum": "8f61b75cde012b2e9eb284d4545583b5643b61ab", + "_from": "eventemitter2@", + "_npmVersion": "1.4.7", + "_npmUser": { + "name": "jasonkuhrt", + "email": "jasonkuhrt@me.com" + }, + "maintainers": [ + { + "name": "hij1nx", + "email": "hij1nx@me.com" + }, + { + "name": "jasonkuhrt", + "email": "jasonkuhrt@me.com" + } + ], + "dist": { + "shasum": "8f61b75cde012b2e9eb284d4545583b5643b61ab", + "tarball": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" +} diff --git a/js/node/package.json b/js/node/package.json index e0a710ef3b..6c98643f39 100644 --- a/js/node/package.json +++ b/js/node/package.json @@ -6,6 +6,7 @@ "coffee-script": "1.7.1", "decimal": "0.0.2", "docco": "0.6.3", + "eventemitter2": "^0.4.14", "htmlparser2": "3.7.2", "iced-coffee-script": "1.7.1-f", "joi": "4.6.1", diff --git a/js/server/modules/org/arangodb/foxx/model.js b/js/server/modules/org/arangodb/foxx/model.js index 079b398929..cb13f85ecb 100644 --- a/js/server/modules/org/arangodb/foxx/model.js +++ b/js/server/modules/org/arangodb/foxx/model.js @@ -32,6 +32,8 @@ var Model, joi = require("joi"), is = require("org/arangodb/is"), extend = require('org/arangodb/extend').extend, + EventEmitter2 = require('eventemitter2').EventEmitter2, + util = require('util'), excludeExtraAttributes, metadataSchema = { _id: joi.string().optional(), @@ -142,8 +144,11 @@ Model = function (attributes) { } else if (attributes) { instance.attributes = _.clone(attributes); } + EventEmitter2.call(instance); }; +util.inherits(Model, EventEmitter2); + Model.fromClient = function (attributes) { 'use strict'; return new this(excludeExtraAttributes(attributes, this)); diff --git a/js/server/tests/shell-foxx-model-events-spec.js b/js/server/tests/shell-foxx-model-events-spec.js new file mode 100644 index 0000000000..9c5440a2c2 --- /dev/null +++ b/js/server/tests/shell-foxx-model-events-spec.js @@ -0,0 +1,14 @@ +/*global require, describe, expect, it */ + +var Model = require("org/arangodb/foxx/model").Model; + +describe('Model Events', function () { + 'use strict'; + + it('should be possible to subscribe and emit events', function () { + var instance = new Model(); + expect(instance.on).toBeDefined(); + expect(instance.emit).toBeDefined(); + }); + +}); \ No newline at end of file From 298f7dea1e0e11d194596b9a26ece9f58af72080 Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Wed, 12 Nov 2014 16:05:59 -0300 Subject: [PATCH 2/7] Before/After events at Repository.save method --- .../modules/org/arangodb/foxx/repository.js | 4 ++ .../tests/shell-foxx-model-events-spec.js | 51 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/repository.js b/js/server/modules/org/arangodb/foxx/repository.js index ae095a9c70..29d0b21ad2 100644 --- a/js/server/modules/org/arangodb/foxx/repository.js +++ b/js/server/modules/org/arangodb/foxx/repository.js @@ -130,8 +130,12 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// save: function (model) { 'use strict'; + model.emit('beforeCreate'); + model.emit('beforeSave'); var id_and_rev = this.collection.save(model.forDB()); model.set(id_and_rev); + model.emit('afterSave'); + model.emit('afterCreate'); return model; }, diff --git a/js/server/tests/shell-foxx-model-events-spec.js b/js/server/tests/shell-foxx-model-events-spec.js index 9c5440a2c2..cac428d06f 100644 --- a/js/server/tests/shell-foxx-model-events-spec.js +++ b/js/server/tests/shell-foxx-model-events-spec.js @@ -1,14 +1,57 @@ -/*global require, describe, expect, it */ +/*global require, describe, expect, it, beforeEach, createSpyObj */ -var Model = require("org/arangodb/foxx/model").Model; +var FoxxRepository = require("org/arangodb/foxx/repository").Repository, + Model = require("org/arangodb/foxx/model").Model; describe('Model Events', function () { 'use strict'; + var collection, instance; + + beforeEach(function () { + collection = createSpyObj('collection', [ + 'save' + ]); + instance = new Model({ random: '', beforeCalled: false, afterCalled: false }); + }); + it('should be possible to subscribe and emit events', function () { - var instance = new Model(); expect(instance.on).toBeDefined(); expect(instance.emit).toBeDefined(); }); -}); \ No newline at end of file + it('should emit beforeCreate and afterCreate events when creating the model', function () { + var repository = new FoxxRepository(collection, {model: Model}); + addHooks(instance, 'Create'); + expect(repository.save(instance)).toEqual(instance); + expect(instance.get('beforeCalled')).toBe(true); + expect(instance.get('afterCalled')).toBe(true); + }); + + it('should emit beforeSave and afterSave events when creating the model', function () { + var repository = new FoxxRepository(collection, {model: Model}); + addHooks(instance, 'Save'); + expect(repository.save(instance)).toEqual(instance); + expect(instance.get('beforeCalled')).toBe(true); + expect(instance.get('afterCalled')).toBe(true); + }); + +}); + +function addHooks(model, ev) { + 'use strict'; + + var random = String(Math.floor(Math.random() * 1000)); + + model.on('before' + ev, function () { + expect(this).toEqual(model); + this.set('random', random); + this.set('beforeCalled', true); + }); + model.on('after' + ev, function () { + expect(this).toEqual(model); + this.set('afterCalled', true); + expect(this.get('beforeCalled')).toBe(true); + expect(this.get('random')).toEqual(random); + }); +} \ No newline at end of file From 26451ea7c3fb9e85d86c114c0ee3815062a24b8a Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Wed, 12 Nov 2014 16:27:25 -0300 Subject: [PATCH 3/7] Before/After events at Repository.update method --- .../modules/org/arangodb/foxx/repository.js | 4 +++ .../tests/shell-foxx-model-events-spec.js | 34 +++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/repository.js b/js/server/modules/org/arangodb/foxx/repository.js index 29d0b21ad2..6cad3da5d7 100644 --- a/js/server/modules/org/arangodb/foxx/repository.js +++ b/js/server/modules/org/arangodb/foxx/repository.js @@ -402,10 +402,14 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// update: function (model, data) { 'use strict'; + model.emit('beforeUpdate', data); + model.emit('beforeSave', data); var id = model.get("_id") || model.get("_key"), id_and_rev = this.collection.update(id, data); model.set(data); model.set(id_and_rev); + model.emit('afterSave', data); + model.emit('afterUpdate', data); return model; }, diff --git a/js/server/tests/shell-foxx-model-events-spec.js b/js/server/tests/shell-foxx-model-events-spec.js index cac428d06f..345c50e794 100644 --- a/js/server/tests/shell-foxx-model-events-spec.js +++ b/js/server/tests/shell-foxx-model-events-spec.js @@ -6,13 +6,15 @@ var FoxxRepository = require("org/arangodb/foxx/repository").Repository, describe('Model Events', function () { 'use strict'; - var collection, instance; + var collection, instance, repository; beforeEach(function () { collection = createSpyObj('collection', [ + 'update', 'save' ]); instance = new Model({ random: '', beforeCalled: false, afterCalled: false }); + repository = new FoxxRepository(collection, {model: Model}); }); it('should be possible to subscribe and emit events', function () { @@ -21,7 +23,6 @@ describe('Model Events', function () { }); it('should emit beforeCreate and afterCreate events when creating the model', function () { - var repository = new FoxxRepository(collection, {model: Model}); addHooks(instance, 'Create'); expect(repository.save(instance)).toEqual(instance); expect(instance.get('beforeCalled')).toBe(true); @@ -29,27 +30,48 @@ describe('Model Events', function () { }); it('should emit beforeSave and afterSave events when creating the model', function () { - var repository = new FoxxRepository(collection, {model: Model}); addHooks(instance, 'Save'); expect(repository.save(instance)).toEqual(instance); expect(instance.get('beforeCalled')).toBe(true); expect(instance.get('afterCalled')).toBe(true); }); + it('should emit beforeUpdate and afterUpdate events when updating the model', function () { + var newData = { newAttribute: 'test' }; + addHooks(instance, 'Update', newData); + expect(repository.update(instance, newData)).toEqual(instance); + expect(instance.get('beforeCalled')).toBe(true); + expect(instance.get('afterCalled')).toBe(true); + }); + + it('should emit beforeSave and afterSave events when updating the model', function () { + var newData = { newAttribute: 'test' }; + addHooks(instance, 'Save', newData); + expect(repository.update(instance, newData)).toEqual(instance); + expect(instance.get('beforeCalled')).toBe(true); + expect(instance.get('afterCalled')).toBe(true); + }); + }); -function addHooks(model, ev) { +function addHooks(model, ev, dataToReceive) { 'use strict'; var random = String(Math.floor(Math.random() * 1000)); - model.on('before' + ev, function () { + model.on('before' + ev, function (data) { expect(this).toEqual(model); + if (dataToReceive) { + expect(data).toEqual(dataToReceive); + } this.set('random', random); this.set('beforeCalled', true); }); - model.on('after' + ev, function () { + model.on('after' + ev, function (data) { expect(this).toEqual(model); + if (dataToReceive) { + expect(data).toEqual(dataToReceive); + } this.set('afterCalled', true); expect(this.get('beforeCalled')).toBe(true); expect(this.get('random')).toEqual(random); From 619b8cf810821428677919bbd169446b943a8511 Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Thu, 13 Nov 2014 10:55:53 -0300 Subject: [PATCH 4/7] Before/After events at Repository.remove method --- .../modules/org/arangodb/foxx/repository.js | 7 +++++-- .../tests/shell-foxx-model-events-spec.js | 18 +++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx/repository.js b/js/server/modules/org/arangodb/foxx/repository.js index 6cad3da5d7..329f40d742 100644 --- a/js/server/modules/org/arangodb/foxx/repository.js +++ b/js/server/modules/org/arangodb/foxx/repository.js @@ -266,8 +266,11 @@ _.extend(Repository.prototype, { //////////////////////////////////////////////////////////////////////////////// remove: function (model) { 'use strict'; - var id = model.get('_id'); - return this.collection.remove(id); + model.emit('beforeRemove'); + var id = model.get('_id'), + result = this.collection.remove(id); + model.emit('afterRemove'); + return result; }, //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/tests/shell-foxx-model-events-spec.js b/js/server/tests/shell-foxx-model-events-spec.js index 345c50e794..a9df30490b 100644 --- a/js/server/tests/shell-foxx-model-events-spec.js +++ b/js/server/tests/shell-foxx-model-events-spec.js @@ -11,7 +11,8 @@ describe('Model Events', function () { beforeEach(function () { collection = createSpyObj('collection', [ 'update', - 'save' + 'save', + 'remove' ]); instance = new Model({ random: '', beforeCalled: false, afterCalled: false }); repository = new FoxxRepository(collection, {model: Model}); @@ -52,6 +53,13 @@ describe('Model Events', function () { expect(instance.get('afterCalled')).toBe(true); }); + it('should emit beforeRemove and afterRemove events when removing the model', function () { + addHooks(instance, 'Remove'); + repository.remove(instance); + expect(instance.get('beforeCalled')).toBe(true); + expect(instance.get('afterCalled')).toBe(true); + }); + }); function addHooks(model, ev, dataToReceive) { @@ -61,17 +69,13 @@ function addHooks(model, ev, dataToReceive) { model.on('before' + ev, function (data) { expect(this).toEqual(model); - if (dataToReceive) { - expect(data).toEqual(dataToReceive); - } + expect(data).toEqual(dataToReceive); this.set('random', random); this.set('beforeCalled', true); }); model.on('after' + ev, function (data) { expect(this).toEqual(model); - if (dataToReceive) { - expect(data).toEqual(dataToReceive); - } + expect(data).toEqual(dataToReceive); this.set('afterCalled', true); expect(this.get('beforeCalled')).toBe(true); expect(this.get('random')).toEqual(random); From ce66c0ffc660ebb4fbdb226a853feb7eddc6ddec Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Fri, 14 Nov 2014 23:38:39 -0300 Subject: [PATCH 5/7] Removed EventEmitter2. Using core events --- js/node/node_modules/eventemitter2/README.md | 248 -------- js/node/node_modules/eventemitter2/index.js | 1 - .../eventemitter2/lib/eventemitter2.js | 573 ------------------ .../node_modules/eventemitter2/package.json | 82 --- js/node/package.json | 1 - js/server/modules/org/arangodb/foxx/model.js | 6 +- 6 files changed, 3 insertions(+), 908 deletions(-) delete mode 100644 js/node/node_modules/eventemitter2/README.md delete mode 100644 js/node/node_modules/eventemitter2/index.js delete mode 100644 js/node/node_modules/eventemitter2/lib/eventemitter2.js delete mode 100644 js/node/node_modules/eventemitter2/package.json diff --git a/js/node/node_modules/eventemitter2/README.md b/js/node/node_modules/eventemitter2/README.md deleted file mode 100644 index e1f6edd6b6..0000000000 --- a/js/node/node_modules/eventemitter2/README.md +++ /dev/null @@ -1,248 +0,0 @@ -[![build-status](https://www.codeship.io/projects/3ad58940-4c7d-0131-15d5-5a8cd3f550f8/status)](https://www.codeship.io/projects/11259) - -# SYNOPSIS - -EventEmitter2 is an implementation of the EventEmitter found in Node.js - -# DESCRIPTION - -### FEATURES - - Namespaces/Wildcards. - - Times To Listen (TTL), extends the `once` concept with `many`. - - Browser environment compatibility. - - Demonstrates good performance in benchmarks - -``` -EventEmitterHeatUp x 3,728,965 ops/sec \302\2610.68% (60 runs sampled) -EventEmitter x 2,822,904 ops/sec \302\2610.74% (63 runs sampled) -EventEmitter2 x 7,251,227 ops/sec \302\2610.55% (58 runs sampled) -EventEmitter2 (wild) x 3,220,268 ops/sec \302\2610.44% (65 runs sampled) -Fastest is EventEmitter2 -``` - -### Differences (Non breaking, compatible with existing EventEmitter) - - - The constructor takes a configuration object. - -```javascript - var EventEmitter2 = require('eventemitter2').EventEmitter2; - var server = new EventEmitter2({ - - // - // use wildcards. - // - wildcard: true, - - // - // the delimiter used to segment namespaces, defaults to `.`. - // - delimiter: '::', - - // - // if you want to emit the newListener event set to true. - // - newListener: false, - - // - // max listeners that can be assigned to an event, default 10. - // - maxListeners: 20 - }); -``` - - - Getting the actual event that fired. - -```javascript - server.on('foo.*', function(value1, value2) { - console.log(this.event, value1, value2); - }); -``` - - - Fire an event N times and then remove it, an extension of the `once` concept. - -```javascript - server.many('foo', 4, function() { - console.log('hello'); - }); -``` - - - Pass in a namespaced event as an array rather than a delimited string. - -```javascript - server.many(['foo', 'bar', 'bazz'], function() { - console.log('hello'); - }); -``` - - -# API - -When an `EventEmitter` instance experiences an error, the typical action is -to emit an `error` event. Error events are treated as a special case. -If there is no listener for it, then the default action is to print a stack -trace and exit the program. - -All EventEmitters emit the event `newListener` when new listeners are -added. - - -**Namespaces** with **Wildcards** -To use namespaces/wildcards, pass the `wildcard` option into the EventEmitter -constructor. When namespaces/wildcards are enabled, events can either be -strings (`foo.bar`) separated by a delimiter or arrays (`['foo', 'bar']`). The -delimiter is also configurable as a constructor option. - -An event name passed to any event emitter method can contain a wild card (the -`*` character). If the event name is a string, a wildcard may appear as `foo.*`. -If the event name is an array, the wildcard may appear as `['foo', '*']`. - -If either of the above described events were passed to the `on` method, -subsequent emits such as the following would be observed... - -```javascript - emitter.emit('foo.bazz'); - emitter.emit(['foo', 'bar']); -``` - - -### emitter.addListener(event, listener) -### emitter.on(event, listener) - -Adds a listener to the end of the listeners array for the specified event. - -```javascript - server.on('data', function(value1, value2, value3, ...) { - console.log('The event was raised!'); - }); -``` - -```javascript - server.on('data', function(value) { - console.log('The event was raised!'); - }); -``` - -### emitter.onAny(listener) - -Adds a listener that will be fired when any event is emitted. - -```javascript - server.onAny(function(value) { - console.log('All events trigger this.'); - }); -``` - -### emitter.offAny(listener) - -Removes the listener that will be fired when any event is emitted. - -```javascript - server.offAny(function(value) { - console.log('The event was raised!'); - }); -``` - -#### emitter.once(event, listener) - -Adds a **one time** listener for the event. The listener is invoked -only the first time the event is fired, after which it is removed. - -```javascript - server.once('get', function (value) { - console.log('Ah, we have our first value!'); - }); -``` - -### emitter.many(event, timesToListen, listener) - -Adds a listener that will execute **n times** for the event before being -removed. The listener is invoked only the first **n times** the event is -fired, after which it is removed. - -```javascript - server.many('get', 4, function (value) { - console.log('This event will be listened to exactly four times.'); - }); -``` - - -### emitter.removeListener(event, listener) -### emitter.off(event, listener) - -Remove a listener from the listener array for the specified event. -**Caution**: changes array indices in the listener array behind the listener. - -```javascript - var callback = function(value) { - console.log('someone connected!'); - }; - server.on('get', callback); - // ... - server.removeListener('get', callback); -``` - - -### emitter.removeAllListeners([event]) - -Removes all listeners, or those of the specified event. - - -### emitter.setMaxListeners(n) - -By default EventEmitters will print a warning if more than 10 listeners -are added to it. This is a useful default which helps finding memory leaks. -Obviously not all Emitters should be limited to 10. This function allows -that to be increased. Set to zero for unlimited. - - -### emitter.listeners(event) - -Returns an array of listeners for the specified event. This array can be -manipulated, e.g. to remove listeners. - -```javascript - server.on('get', function(value) { - console.log('someone connected!'); - }); - console.log(server.listeners('get')); // [ [Function] ] -``` - -### emitter.listenersAny() - -Returns an array of listeners that are listening for any event that is -specified. This array can be manipulated, e.g. to remove listeners. - -```javascript - server.onAny(function(value) { - console.log('someone connected!'); - }); - console.log(server.listenersAny()[0]); // [ [Function] ] -``` - -### emitter.emit(event, [arg1], [arg2], [...]) - -Execute each of the listeners that may be listening for the specified event -name in order with the list of arguments. - -# LICENSE - -(The MIT License) - -Copyright (c) 2011 hij1nx - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the 'Software'), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/js/node/node_modules/eventemitter2/index.js b/js/node/node_modules/eventemitter2/index.js deleted file mode 100644 index 6f583b5f6d..0000000000 --- a/js/node/node_modules/eventemitter2/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/eventemitter2'); diff --git a/js/node/node_modules/eventemitter2/lib/eventemitter2.js b/js/node/node_modules/eventemitter2/lib/eventemitter2.js deleted file mode 100644 index bde69e8532..0000000000 --- a/js/node/node_modules/eventemitter2/lib/eventemitter2.js +++ /dev/null @@ -1,573 +0,0 @@ -/*! - * EventEmitter2 - * https://github.com/hij1nx/EventEmitter2 - * - * Copyright (c) 2013 hij1nx - * Licensed under the MIT license. - */ -;!function(undefined) { - - var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { - return Object.prototype.toString.call(obj) === "[object Array]"; - }; - var defaultMaxListeners = 10; - - function init() { - this._events = {}; - if (this._conf) { - configure.call(this, this._conf); - } - } - - function configure(conf) { - if (conf) { - - this._conf = conf; - - conf.delimiter && (this.delimiter = conf.delimiter); - conf.maxListeners && (this._events.maxListeners = conf.maxListeners); - conf.wildcard && (this.wildcard = conf.wildcard); - conf.newListener && (this.newListener = conf.newListener); - - if (this.wildcard) { - this.listenerTree = {}; - } - } - } - - function EventEmitter(conf) { - this._events = {}; - this.newListener = false; - configure.call(this, conf); - } - - // - // Attention, function return type now is array, always ! - // It has zero elements if no any matches found and one or more - // elements (leafs) if there are matches - // - function searchListenerTree(handlers, type, tree, i) { - if (!tree) { - return []; - } - var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, - typeLength = type.length, currentType = type[i], nextType = type[i+1]; - if (i === typeLength && tree._listeners) { - // - // If at the end of the event(s) list and the tree has listeners - // invoke those listeners. - // - if (typeof tree._listeners === 'function') { - handlers && handlers.push(tree._listeners); - return [tree]; - } else { - for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { - handlers && handlers.push(tree._listeners[leaf]); - } - return [tree]; - } - } - - if ((currentType === '*' || currentType === '**') || tree[currentType]) { - // - // If the event emitted is '*' at this part - // or there is a concrete match at this patch - // - if (currentType === '*') { - for (branch in tree) { - if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { - listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); - } - } - return listeners; - } else if(currentType === '**') { - endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); - if(endReached && tree._listeners) { - // The next element has a _listeners, add it to the handlers. - listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); - } - - for (branch in tree) { - if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { - if(branch === '*' || branch === '**') { - if(tree[branch]._listeners && !endReached) { - listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); - } - listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); - } else if(branch === nextType) { - listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); - } else { - // No match on this one, shift into the tree but not in the type array. - listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); - } - } - } - return listeners; - } - - listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); - } - - xTree = tree['*']; - if (xTree) { - // - // If the listener tree will allow any match for this part, - // then recursively explore all branches of the tree - // - searchListenerTree(handlers, type, xTree, i+1); - } - - xxTree = tree['**']; - if(xxTree) { - if(i < typeLength) { - if(xxTree._listeners) { - // If we have a listener on a '**', it will catch all, so add its handler. - searchListenerTree(handlers, type, xxTree, typeLength); - } - - // Build arrays of matching next branches and others. - for(branch in xxTree) { - if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { - if(branch === nextType) { - // We know the next element will match, so jump twice. - searchListenerTree(handlers, type, xxTree[branch], i+2); - } else if(branch === currentType) { - // Current node matches, move into the tree. - searchListenerTree(handlers, type, xxTree[branch], i+1); - } else { - isolatedBranch = {}; - isolatedBranch[branch] = xxTree[branch]; - searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); - } - } - } - } else if(xxTree._listeners) { - // We have reached the end and still on a '**' - searchListenerTree(handlers, type, xxTree, typeLength); - } else if(xxTree['*'] && xxTree['*']._listeners) { - searchListenerTree(handlers, type, xxTree['*'], typeLength); - } - } - - return listeners; - } - - function growListenerTree(type, listener) { - - type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); - - // - // Looks for two consecutive '**', if so, don't add the event at all. - // - for(var i = 0, len = type.length; i+1 < len; i++) { - if(type[i] === '**' && type[i+1] === '**') { - return; - } - } - - var tree = this.listenerTree; - var name = type.shift(); - - while (name) { - - if (!tree[name]) { - tree[name] = {}; - } - - tree = tree[name]; - - if (type.length === 0) { - - if (!tree._listeners) { - tree._listeners = listener; - } - else if(typeof tree._listeners === 'function') { - tree._listeners = [tree._listeners, listener]; - } - else if (isArray(tree._listeners)) { - - tree._listeners.push(listener); - - if (!tree._listeners.warned) { - - var m = defaultMaxListeners; - - if (typeof this._events.maxListeners !== 'undefined') { - m = this._events.maxListeners; - } - - if (m > 0 && tree._listeners.length > m) { - - tree._listeners.warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - tree._listeners.length); - console.trace(); - } - } - } - return true; - } - name = type.shift(); - } - return true; - } - - // By default EventEmitters will print a warning if more than - // 10 listeners are added to it. This is a useful default which - // helps finding memory leaks. - // - // Obviously not all Emitters should be limited to 10. This function allows - // that to be increased. Set to zero for unlimited. - - EventEmitter.prototype.delimiter = '.'; - - EventEmitter.prototype.setMaxListeners = function(n) { - this._events || init.call(this); - this._events.maxListeners = n; - if (!this._conf) this._conf = {}; - this._conf.maxListeners = n; - }; - - EventEmitter.prototype.event = ''; - - EventEmitter.prototype.once = function(event, fn) { - this.many(event, 1, fn); - return this; - }; - - EventEmitter.prototype.many = function(event, ttl, fn) { - var self = this; - - if (typeof fn !== 'function') { - throw new Error('many only accepts instances of Function'); - } - - function listener() { - if (--ttl === 0) { - self.off(event, listener); - } - fn.apply(this, arguments); - } - - listener._origin = fn; - - this.on(event, listener); - - return self; - }; - - EventEmitter.prototype.emit = function() { - - this._events || init.call(this); - - var type = arguments[0]; - - if (type === 'newListener' && !this.newListener) { - if (!this._events.newListener) { return false; } - } - - // Loop through the *_all* functions and invoke them. - if (this._all) { - var l = arguments.length; - var args = new Array(l - 1); - for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; - for (i = 0, l = this._all.length; i < l; i++) { - this.event = type; - this._all[i].apply(this, args); - } - } - - // If there is no 'error' event listener then throw. - if (type === 'error') { - - if (!this._all && - !this._events.error && - !(this.wildcard && this.listenerTree.error)) { - - if (arguments[1] instanceof Error) { - throw arguments[1]; // Unhandled 'error' event - } else { - throw new Error("Uncaught, unspecified 'error' event."); - } - return false; - } - } - - var handler; - - if(this.wildcard) { - handler = []; - var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); - searchListenerTree.call(this, handler, ns, this.listenerTree, 0); - } - else { - handler = this._events[type]; - } - - if (typeof handler === 'function') { - this.event = type; - if (arguments.length === 1) { - handler.call(this); - } - else if (arguments.length > 1) - switch (arguments.length) { - case 2: - handler.call(this, arguments[1]); - break; - case 3: - handler.call(this, arguments[1], arguments[2]); - break; - // slower - default: - var l = arguments.length; - var args = new Array(l - 1); - for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; - handler.apply(this, args); - } - return true; - } - else if (handler) { - var l = arguments.length; - var args = new Array(l - 1); - for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; - - var listeners = handler.slice(); - for (var i = 0, l = listeners.length; i < l; i++) { - this.event = type; - listeners[i].apply(this, args); - } - return (listeners.length > 0) || !!this._all; - } - else { - return !!this._all; - } - - }; - - EventEmitter.prototype.on = function(type, listener) { - - if (typeof type === 'function') { - this.onAny(type); - return this; - } - - if (typeof listener !== 'function') { - throw new Error('on only accepts instances of Function'); - } - this._events || init.call(this); - - // To avoid recursion in the case that type == "newListeners"! Before - // adding it to the listeners, first emit "newListeners". - this.emit('newListener', type, listener); - - if(this.wildcard) { - growListenerTree.call(this, type, listener); - return this; - } - - if (!this._events[type]) { - // Optimize the case of one listener. Don't need the extra array object. - this._events[type] = listener; - } - else if(typeof this._events[type] === 'function') { - // Adding the second element, need to change to array. - this._events[type] = [this._events[type], listener]; - } - else if (isArray(this._events[type])) { - // If we've already got an array, just append. - this._events[type].push(listener); - - // Check for listener leak - if (!this._events[type].warned) { - - var m = defaultMaxListeners; - - if (typeof this._events.maxListeners !== 'undefined') { - m = this._events.maxListeners; - } - - if (m > 0 && this._events[type].length > m) { - - this._events[type].warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); - console.trace(); - } - } - } - return this; - }; - - EventEmitter.prototype.onAny = function(fn) { - - if (typeof fn !== 'function') { - throw new Error('onAny only accepts instances of Function'); - } - - if(!this._all) { - this._all = []; - } - - // Add the function to the event listener collection. - this._all.push(fn); - return this; - }; - - EventEmitter.prototype.addListener = EventEmitter.prototype.on; - - EventEmitter.prototype.off = function(type, listener) { - if (typeof listener !== 'function') { - throw new Error('removeListener only takes instances of Function'); - } - - var handlers,leafs=[]; - - if(this.wildcard) { - var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); - leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); - } - else { - // does not use listeners(), so no side effect of creating _events[type] - if (!this._events[type]) return this; - handlers = this._events[type]; - leafs.push({_listeners:handlers}); - } - - for (var iLeaf=0; iLeaf 0) { - fns = this._all; - for(i = 0, l = fns.length; i < l; i++) { - if(fn === fns[i]) { - fns.splice(i, 1); - return this; - } - } - } else { - this._all = []; - } - return this; - }; - - EventEmitter.prototype.removeListener = EventEmitter.prototype.off; - - EventEmitter.prototype.removeAllListeners = function(type) { - if (arguments.length === 0) { - !this._events || init.call(this); - return this; - } - - if(this.wildcard) { - var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); - var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); - - for (var iLeaf=0; iLeaf= 0.2.2" - }, - "main": "./lib/eventemitter2.js", - "scripts": { - "test": "nodeunit test/simple/ && nodeunit test/wildcardEvents/", - "benchmark": "node test/perf/benchmark.js" - }, - "files": [ - "lib/eventemitter2.js", - "index.js" - ], - "bugs": { - "url": "https://github.com/hij1nx/EventEmitter2/issues" - }, - "homepage": "https://github.com/hij1nx/EventEmitter2", - "_id": "eventemitter2@0.4.14", - "_shasum": "8f61b75cde012b2e9eb284d4545583b5643b61ab", - "_from": "eventemitter2@", - "_npmVersion": "1.4.7", - "_npmUser": { - "name": "jasonkuhrt", - "email": "jasonkuhrt@me.com" - }, - "maintainers": [ - { - "name": "hij1nx", - "email": "hij1nx@me.com" - }, - { - "name": "jasonkuhrt", - "email": "jasonkuhrt@me.com" - } - ], - "dist": { - "shasum": "8f61b75cde012b2e9eb284d4545583b5643b61ab", - "tarball": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" - }, - "directories": {}, - "_resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" -} diff --git a/js/node/package.json b/js/node/package.json index 6c98643f39..e0a710ef3b 100644 --- a/js/node/package.json +++ b/js/node/package.json @@ -6,7 +6,6 @@ "coffee-script": "1.7.1", "decimal": "0.0.2", "docco": "0.6.3", - "eventemitter2": "^0.4.14", "htmlparser2": "3.7.2", "iced-coffee-script": "1.7.1-f", "joi": "4.6.1", diff --git a/js/server/modules/org/arangodb/foxx/model.js b/js/server/modules/org/arangodb/foxx/model.js index cb13f85ecb..0dfffc4e78 100644 --- a/js/server/modules/org/arangodb/foxx/model.js +++ b/js/server/modules/org/arangodb/foxx/model.js @@ -32,7 +32,7 @@ var Model, joi = require("joi"), is = require("org/arangodb/is"), extend = require('org/arangodb/extend').extend, - EventEmitter2 = require('eventemitter2').EventEmitter2, + EventEmitter = require('events').EventEmitter, util = require('util'), excludeExtraAttributes, metadataSchema = { @@ -144,10 +144,10 @@ Model = function (attributes) { } else if (attributes) { instance.attributes = _.clone(attributes); } - EventEmitter2.call(instance); + EventEmitter.call(instance); }; -util.inherits(Model, EventEmitter2); +util.inherits(Model, EventEmitter); Model.fromClient = function (attributes) { 'use strict'; From 901f1ed938c66c2e8c2df74c542921b8887a4c0a Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Mon, 17 Nov 2014 13:35:54 -0300 Subject: [PATCH 6/7] Updated documentation --- Documentation/Books/Users/Foxx/FoxxModel.mdpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Documentation/Books/Users/Foxx/FoxxModel.mdpp b/Documentation/Books/Users/Foxx/FoxxModel.mdpp index aa31ed9dab..2dc80707b4 100644 --- a/Documentation/Books/Users/Foxx/FoxxModel.mdpp +++ b/Documentation/Books/Users/Foxx/FoxxModel.mdpp @@ -55,6 +55,46 @@ person.attributes // => { name: "Pete", admin: true, active: true } person.errors // => {admin: [ValidationError: value is not allowed]} ``` +The following events are emitted by a model: + +- beforeCreate +- afterCreate +- beforeSave +- afterSave +- beforeUpdate +- afterUpdate +- beforeRemove +- afterRemove + +Model lifecycle: + +```js +var person = new PersonModel(); +person.on('beforeCreate', function() { + // Do something fancy with the model +}); +var people = new Repository(appContext.collection("people"), { model: PersonModel }); + +people.save(person); +// beforeCreate() +// beforeSave() +// The model is created at db +// afterSave() +// afterCreate() + +people.update(person, data); +// beforeUpdate(data) +// beforeSave(data) +// The model is updated at db +// afterSave(data) +// afterUpdate(data) + +people.remove(person); +// beforeRemove() +// The model is deleted at db +// afterRemove() +``` + !SUBSECTION Extend @startDocuBlock JSF_foxx_model_extend From 94a20843b7c4fa41f8c43592ae4ca4ee9536e28a Mon Sep 17 00:00:00 2001 From: Hugo Sarti Date: Tue, 18 Nov 2014 11:44:00 -0300 Subject: [PATCH 7/7] Doc update --- Documentation/Books/Users/Foxx/FoxxModel.mdpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/Books/Users/Foxx/FoxxModel.mdpp b/Documentation/Books/Users/Foxx/FoxxModel.mdpp index 2dc80707b4..af8bf182ba 100644 --- a/Documentation/Books/Users/Foxx/FoxxModel.mdpp +++ b/Documentation/Books/Users/Foxx/FoxxModel.mdpp @@ -71,7 +71,8 @@ Model lifecycle: ```js var person = new PersonModel(); person.on('beforeCreate', function() { - // Do something fancy with the model + var model = this; + model.fancyMethod(); // Do something fancy with the model }); var people = new Repository(appContext.collection("people"), { model: PersonModel });