Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Feathers universal #193

Merged
merged 2 commits into from
Jan 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/client');
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,10 @@
"q": "^1.0.1",
"request": "^2.x",
"socket.io-client": "^1.0.0"
},
"browser": {
"express": "./lib/client/express",
"./lib/feathers": "./lib/client/index",
"babel-polyfill": false
}
}
15 changes: 10 additions & 5 deletions src/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ export default {
});
},

service(location, service, options) {
service(location, service, options = {}) {
location = stripSlashes(location);

if(!service) {
return this.services[location];
const current = this.services[location];

if(typeof current === 'undefined' && typeof this.defaultService === 'function') {
return this.service(location, this.defaultService(location), options);
}

return current;
}

let protoService = Proto.extend(service);
Expand All @@ -40,16 +46,15 @@ export default {

// Run the provider functions to register the service
this.providers.forEach(provider =>
provider.call(this, location, protoService, options || {}));
provider.call(this, location, protoService, options));

// If we ran setup already, set this service up explicitly
if (this._isSetup && typeof protoService.setup === 'function') {
debug(`Setting up service for \`${location}\``);
protoService.setup(this, location);
}

this.services[location] = protoService;
return protoService;
return (this.services[location] = protoService);
},

use(location) {
Expand Down
47 changes: 47 additions & 0 deletions src/client/express.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { EventEmitter } from 'events';
import Proto from 'uberproto';

export default function() {
const app = {
settings: {},

get(name) {
return this.settings[name];
},

set(name, value) {
this.settings[name] = value;
return this;
},

disable(name) {
this.settings[name] = false;
return this;
},

disabled(name) {
return !this.settings[name];
},

enable(name) {
this.settings[name] = true;
return this;
},

enabled(name) {
return !!this.settings[name];
},

use() {
throw new Error('Middleware functions can not be used in the Feathers client');
},

listen() {
return {};
}
};

Proto.mixin(EventEmitter.prototype, app);

return app;
}
6 changes: 6 additions & 0 deletions src/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import feathers from '../feathers';
import express from './express';

export default function() {
return feathers(express());
}
3 changes: 1 addition & 2 deletions src/feathers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import Application from './application';
* @return {Function}
* @api public
*/
export default function createApplication() {
const app = express();
export default function createApplication(app = express()) {
Proto.mixin(Application, app);
app.init();
return app;
Expand Down
31 changes: 28 additions & 3 deletions test/application.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,32 @@ describe('Feathers application', () => {
});
});

it('Registers a service, wraps it, runs service.setup(), and adds the event mixin', done => {
it('uses .defaultService if available', done => {
const app = feathers();

assert.ok(!app.service('/todos/'));

app.defaultService = function(path) {
assert.equal(path, 'todos');
return {
get(id) {
return Promise.resolve({
id, description: `You have to do ${id}!`
});
}
};
};

app.service('/todos/').get('dishes').then(data => {
assert.deepEqual(data, {
id: 'dishes',
description: 'You have to do dishes!'
});
done();
});
});

it('Registers a service, wraps it, runs service.setup(), and adds the event and Promise mixin', done => {
const dummyService = {
setup(app, path){
this.path = path;
Expand Down Expand Up @@ -84,7 +109,7 @@ describe('Feathers application', () => {
get(name, params, callback) {
callback(null, {
id: name,
description: 'You have to do ' + name + '!'
description: `You have to do ${name}!`
});
}
};
Expand Down Expand Up @@ -151,7 +176,7 @@ describe('Feathers application', () => {
});

it('REST and SocketIO with SSL server (#25)', done => {
// For more info on Reqest HTTPS settings see https://github.com/mikeal/request/issues/418
// For more info on Request HTTPS settings see https://github.com/mikeal/request/issues/418
// This needs to be set so that the SocektIO client can connect
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

Expand Down
126 changes: 126 additions & 0 deletions test/client.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import assert from 'assert';
import Proto from 'uberproto';
import feathers from '../src/client';

describe('Feathers universal client', () => {
it('is not an Express application', () => {
const app = feathers();
// There may be some other better ways to verify this but it works for now
assert.ok(typeof app.render !== 'function');
});

it('is an event emitter', done => {
const app = feathers();
const original = { hello: 'world' };
app.on('test', data => {
assert.deepEqual(original, data);
done();
});

app.emit('test', original);
});

it('Registers a service, wraps it, runs service.setup(), and adds the event and Promise mixin', done => {
const dummyService = {
setup(app, path){
this.path = path;
},

create(data) {
return Promise.resolve(data);
}
};

const app = feathers().use('/dummy', dummyService);
const wrappedService = app.service('dummy');

assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object');
assert.ok(typeof wrappedService.on === 'function', 'Wrapped service is an event emitter');

wrappedService.on('created', function (data) {
assert.equal(data.message, 'Test message', 'Got created event with test message');
done();
});

wrappedService.create({
message: 'Test message'
}).then(data =>
assert.equal(data.message, 'Test message', 'Got created event with test message'));
});

// Copied from the Express tests (without special cases)
describe('Express app options compatibility', function () {
describe('.set()', () => {
it('should set a value', () => {
var app = feathers();
app.set('foo', 'bar');
assert.equal(app.get('foo'), 'bar');
});

it('should return the app', () => {
var app = feathers();
assert.equal(app.set('foo', 'bar'), app);
});

it('should return the app when undefined', () => {
var app = feathers();
assert.equal(app.set('foo', undefined), app);
});
});

describe('.get()', () => {
it('should return undefined when unset', () => {
var app = feathers();
assert.strictEqual(app.get('foo'), undefined);
});

it('should otherwise return the value', () => {
var app = feathers();
app.set('foo', 'bar');
assert.equal(app.get('foo'), 'bar');
});
});

describe('.enable()', () => {
it('should set the value to true', () => {
var app = feathers();
assert.equal(app.enable('tobi'), app);
assert.strictEqual(app.get('tobi'), true);
});
});

describe('.disable()', () => {
it('should set the value to false', () => {
var app = feathers();
assert.equal(app.disable('tobi'), app);
assert.strictEqual(app.get('tobi'), false);
});
});

describe('.enabled()', () => {
it('should default to false', () => {
var app = feathers();
assert.strictEqual(app.enabled('foo'), false);
});

it('should return true when set', () => {
var app = feathers();
app.set('foo', 'bar');
assert.strictEqual(app.enabled('foo'), true);
});
});

describe('.disabled()', () => {
it('should default to true', () => {
var app = feathers();
assert.strictEqual(app.disabled('foo'), true);
});

it('should return false when set', () => {
var app = feathers();
app.set('foo', 'bar');
assert.strictEqual(app.disabled('foo'), false);
});
});
});
});
2 changes: 1 addition & 1 deletion test/mixins/event.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'assert';
import Proto from 'uberproto';
import { EventEmitter } from 'events';
import mixinEvent from '../../lib/mixins/event';
import mixinEvent from '../../src/mixins/event';

const create = Proto.create;

Expand Down
4 changes: 2 additions & 2 deletions test/mixins/normalizer.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'assert';
import Proto from 'uberproto';
import normalizer from '../../lib/mixins/normalizer';
import mixins from '../../lib/mixins';
import normalizer from '../../src/mixins/normalizer';
import mixins from '../../src/mixins';

describe('Argument normalizer mixin', () => {
it('normalizer mixin is always the last to run', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/mixins/promise.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'assert';
import Proto from 'uberproto';
import mixin from '../../lib/mixins/promise';
import mixin from '../../src/mixins/promise';

const create = Proto.create;

Expand Down