diff --git a/.travis.yml b/.travis.yml
index 2ca91f2895..43be7c3197 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
language: node_js
node_js:
- "0.10"
- - "0.8"
\ No newline at end of file
+ - "0.8"
+ # - "0.11"
\ No newline at end of file
diff --git a/documentation.md b/documentation.md
index e628b3d757..c3b728ca43 100644
--- a/documentation.md
+++ b/documentation.md
@@ -74,6 +74,47 @@ app.configure(feathers.socketio(function(io) {
Once the server has been started with `app.listen()` the SocketIO object is available as `app.io`.
+### Primus
+
+[Primus](https://github.com/primus/primus) is a universal wrapper for real-time frameworks and allows you to transparently use Engine.IO, WebSockets, BrowserChannel, SockJS and Socket.IO. Set it up with `feathers.primus(configuration [, fn])` where `configuration` is the [Primus server configuration](https://github.com/primus/primus#getting-started) and `fn` an optional callback with the Primus server instance that can e.g. be used for setting up [authorization](https://github.com/primus/primus#authorization):
+
+```js
+// Set up Primus with SockJS
+app.configure(feathers.primus({
+ transformer: 'sockjs'
+}, function(primus) {
+ // Set up Primus authorization here
+ primus.authorize(function (req, done) {
+ var auth;
+
+ try { auth = authParser(req.headers['authorization']) }
+ catch (ex) { return done(ex) }
+
+ // Do some async auth check
+ authCheck(auth, done);
+ });
+}));
+```
+
+In the Browser you can connect like this:
+
+```html
+
+
+```
+
## API
### listen
@@ -453,6 +494,8 @@ With that being said there are some amazing frameworks already out there and we
__0.3.0__
+- Added [Primus](https://github.com/primus/primus) provider ([#34](https://github.com/feathersjs/feathers/pull/34))
+- `app.setup(server)` to support HTTPS (and other functionality that requires a custom server) ([#33](https://github.com/feathersjs/feathers/pull/33))
- Removed bad SocketIO configuration ([#19](https://github.com/feathersjs/feathers/issues/19))
- Add .npmignore to not publish .idea folder ([#30](https://github.com/feathersjs/feathers/issues/30))
- Remove middleware: connect.bodyParser() ([#27](https://github.com/feathersjs/feathers/pull/27))
diff --git a/lib/providers/index.js b/lib/providers/index.js
index 278897d473..8b8e267ac7 100644
--- a/lib/providers/index.js
+++ b/lib/providers/index.js
@@ -2,5 +2,6 @@
module.exports = {
rest: require('./rest'),
- socketio: require('./socketio')
+ socketio: require('./socketio'),
+ primus: require('./primus')
};
diff --git a/lib/providers/primus.js b/lib/providers/primus.js
new file mode 100644
index 0000000000..1db4ade9e6
--- /dev/null
+++ b/lib/providers/primus.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var _ = require('underscore');
+var Proto = require('uberproto');
+var Primus = require('primus');
+var Emitter = require('primus-emitter');
+
+module.exports = function(config, configurer) {
+ return function() {
+ var app = this;
+ var services = {};
+
+ app.enable('feathers primus');
+
+ // Monkey patch app.setup(server)
+ Proto.mixin({
+ setup: function(server) {
+ var self = this;
+ var result = this._super.apply(this, arguments);
+
+ if (this.disabled('feathers primus')) {
+ return result;
+ }
+
+ var primus = this.primus = new Primus(server, config);
+ primus.use('emitter', Emitter);
+
+ _.each(services, function(service, path) {
+ // If the service emits events that we want to listen to (Event mixin)
+ if (typeof service.on === 'function' && service._serviceEvents) {
+ _.each(service._serviceEvents, function(ev) {
+ service.on(ev, function(data) {
+ primus.forEach(function (spark) {
+ spark.send(path + ' ' + ev, data);
+ });
+ });
+ });
+ }
+ });
+
+ primus.on('connection', function(spark) {
+ _.each(services, function(service, path) {
+ _.each(self.methods, function(method) {
+ var name = path + '::' + method;
+ if (service[method]) {
+ spark.on(name, service[method].bind(service));
+ }
+ });
+ });
+ });
+
+ if (typeof configurer === 'function') {
+ configurer.call(this, primus);
+ }
+
+ return result;
+ }
+ }, app);
+
+ app.providers.push(function(path, service) {
+ services[path] = service;
+ });
+ };
+};
diff --git a/package.json b/package.json
index 899c4618b9..afab6a3629 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,9 @@
"express": "~ 3.4.0",
"rubberduck": "~1.x",
"underscore": "~1.5.0",
- "socket.io": "~0.9.0"
+ "socket.io": "~0.9.0",
+ "primus-emitter": "~2.0.5",
+ "primus": "~1.5.1"
},
"devDependencies": {
"request": "~2.21.0",
diff --git a/test/providers/primus.test.js b/test/providers/primus.test.js
new file mode 100644
index 0000000000..457656e979
--- /dev/null
+++ b/test/providers/primus.test.js
@@ -0,0 +1,113 @@
+'use strict';
+
+var feathers = require('../../lib/feathers');
+var fixture = require('./service-fixture');
+var todoService = fixture.Service;
+var verify = fixture.verify;
+
+describe('Primus provider', function () {
+ var server, socket;
+
+ before(function () {
+ server = feathers()
+ .configure(feathers.primus({
+ transformer: 'socket.io'
+ }, function(primus) {
+ socket = new primus.Socket('http://localhost:7888');
+ }))
+ .use('todo', todoService)
+ .listen(7888);
+ });
+
+ after(function (done) {
+ socket.socket.disconnect();
+ server.close(done);
+ });
+
+ describe('CRUD', function () {
+ it('::find', function (done) {
+ socket.send('todo::find', {}, function (error, data) {
+ verify.find(data);
+
+ done(error);
+ });
+ });
+
+ it('::get', function (done) {
+ socket.send('todo::get', 'laundry', {}, function (error, data) {
+ verify.get('laundry', data);
+
+ done(error);
+ });
+ });
+
+ it('::create', function (done) {
+ var original = {
+ name: 'creating'
+ };
+
+ socket.send('todo::create', original, {}, function (error, data) {
+ verify.create(original, data);
+
+ done(error);
+ });
+ });
+
+ it('::update', function (done) {
+ var original = {
+ name: 'updating'
+ };
+
+ socket.send('todo::update', 23, original, {}, function (error, data) {
+ verify.update(23, original, data);
+
+ done(error);
+ });
+ });
+
+ it('::remove', function (done) {
+ socket.send('todo::remove', 11, {}, function (error, data) {
+ verify.remove(11, data);
+
+ done(error);
+ });
+ });
+ });
+
+ describe('Events', function () {
+ it('created', function (done) {
+ var original = {
+ name: 'created event'
+ };
+
+ socket.on('todo created', function (data) {
+ verify.create(original, data);
+ done();
+ });
+
+ socket.send('todo::create', original, {}, function () {});
+ });
+
+ it('updated', function (done) {
+ var original = {
+ name: 'updated event'
+ };
+
+ socket.on('todo updated', function (data) {
+ verify.update(10, original, data);
+ done();
+ });
+
+ socket.send('todo::update', 10, original, {}, function () {});
+ });
+
+ it('removed', function (done) {
+ socket.on('todo removed', function (data) {
+ verify.remove(333, data);
+ done();
+ });
+
+ socket.send('todo::remove', 333, {}, function () {});
+ });
+ });
+});