Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Owen Barnes committed Apr 20, 2013
0 parents commit bd8c8dc
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
node_modules
1 change: 1 addition & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"node": true}
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Realtime Service

**Status: Usable, but a work in progress. Feedback encouraged!**

An open standard describing a mix of client and server-side code designed to communicate over a persistent connection. This will usually be a WebSocket connection, but any [Realtime Transport](https://github.com/socketstream/realtime-transport) module is compatible.

Realtime Services are designed to be very easy to understand, write, test and share on `npm`.

Each service is defined in the simplest possible way: as a plain old JavaScript object. Here's a simple example:

```js
var service = {
client: function(client) {
client.onmessage = function(msg) {
console.log('Message in from server:', msg);
}
},

server: function(server) {
setInterval(function(){
server.broadcast('Hello!');
}, 1000);
}
}
```

## Examples Services

(all alpha quality for now)

[**rts-rpc**](https://github.com/socketstream/rts-rpc)
[**rts-pubsub**](https://github.com/socketstream/rts-pubsub)
[**rts-livereload**](https://github.com/socketstream/rts-livereload)


## Features

* super-simple: services are just JavaScript objects
* client and server code can expose APIs to be called by the app
* easy to write tests
* the [realtime-service-client](https://github.com/socketstream/realtime-service-client) can run in a browser or separate node process
* each service has it's own directory to store files (e.g. model definitions)
* efficiently handles callbacks, even for non-JSON messages
* services can optionally use Sessions (provided by the server)
* server is notified when a client connect/disconnect (allowing cleanup)
* services can send JavaScript assets to the browser
* optionally handles JSON encoding/decoding for you
* provides a standard logging API
* ultra light weight client-side code (for sending to browser)


Realtime Services **do not** care about:

* the underlying transport layer (abstracted away by [Realtime Transport](https://github.com/socketstream/realtime-transport))
* how code is delivered to the browser (left to the framework or app to implement)


## Implementations

Realtime Services are currently implemented in [**Prism**](https://github.com/socketstream/prism), the realtime server component of SocketStream 0.4.

As other frameworks / toolkits implement them, they will be listed here.


## Contents

Realtime Services consist of

1. This - the server-side library
2. The [realtime-service-client](https://github.com/socketstream/realtime-service-client) module
3. The Realtime Server Spec (Coming soon!)


## API

Coming soon!


## History

Realtime Services are the evolution of an idea called "Request Responders" which first appeared in SocketStream 0.3. Despite a horribly clunky API, the idea proved to be popular with third-party modules for Backbone and Angular soon appearing.

Realtime Services will be one of the key features of SocketStream 0.4. However, I hope by ensuring the spec is a simple as possible (with minimal dependencies), other frameworks will support Realtime Services in the future. The ultimate goal is an ecosystem of reusable components using 100% standard `npm` packages.


## Licence

MIT
91 changes: 91 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use strict";

/*!
* Realtime Service
* Copyright(c) 2013 Owen Barnes <[email protected]>
* MIT Licensed
*/


/**
* Load objects to be passed to server() and client()
*/

var Server = require('./server');
var Client = require('realtime-service-client');


function Service (definition, assigned, services) {
if (typeof assigned.id === 'undefined') throw new Error("Service must have an ID");
if (!assigned.name) throw new Error("Service must have a name");

this.assigned = assigned;
this.services = services;

this.use = definition.use || {};

this.serverApi = definition.server;
this.clientApi = definition.client;

this.private = definition.private || false;

this.msgAttrs = [];
if (this.use.callbacks) this.msgAttrs.push('callbackId');
}

Service.prototype.start = function(transport) {
this.server = new Server(this, transport);
return this.serverApi(this.server);
};

Service.prototype.paramsForClient = function() {
return {
id: this.assigned.id,
name: this.assigned.name,
options: this.assigned.options,
use: this.use
};
};

Service.prototype.relativeRoot = function() {
var len = this.services.root.length;
return '.' + this.assigned.root.substr(len);
};

Service.prototype.log = function(args) {
if (this.assigned.log) {
args.unshift(this.assigned.name); // append service name
this.assigned.log.apply(this.assigned.log, args);
}
};

// A mock client used for testing - lot of work and thinking still to do here!
Service.prototype.testClient = function() {
var self = this;

var client = new Client();

var serverTransport = {
sendToSocketId: function(fake, serviceId, msg) {
client.processIncomingMessage(self.assigned.id, msg);
}
};

var clientTransport = {
write: function(serviceId, msg) {
var meta = {transport: 'fakedForTest'};
server.read(msg, meta);
}
};

var server = new Server(this, serverTransport);

var clientApi = client.register(this.paramsForClient(), this.clientApi);
client.connect(clientTransport);
this.serverApi(server);

return clientApi;
};

module.exports = Service;

13 changes: 13 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "realtime-service",
"description": "Realtime Service",
"version": "0.0.1",
"author": "Owen Barnes <[email protected]>",
"engines": { "node": ">= 0.8.0" },
"dependencies": {
},
"devDependencies": {
"mocha": "1.8.x",
"should": "1.2.x"
}
}
77 changes: 77 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use strict";

/*!
* Realtime Service - Server API
* Copyright(c) 2013 Owen Barnes <[email protected]>
* MIT Licensed
*/

function Server(service, transport) {
this.service = service;
this.transport = transport;
this.events = service.assigned.events;
}

Server.prototype.read = function(req) {
var self = this;

// Try to fetch Callback ID
var cbId = Number(req.attributes.callbackId);

if (this.service.use.json) req.message = JSON.parse(req.message);

this.onmessage(req.message, req, function(msg){
self.sendToSocketId(req.socketId, msg, {callbackId: cbId});
});
};

Server.prototype.sendToSocketIds = function(socketIds, msg, attrs) {
msg = this._prepareOutgoingMessage(msg, attrs);
if (typeof socketIds !== 'object') socketIds = [socketIds];
socketIds.forEach(function(socketId) {
this.transport.sendToSocketId(socketId, msg);
}, this);
};

// Alias
Server.prototype.sendToSocketId = Server.prototype.sendToSocketIds;

Server.prototype.broadcast = function(msg) {
msg = this._prepareOutgoingMessage(msg);
this.transport.broadcast(msg);
};

Server.prototype.log = function(){
var args = Array.prototype.slice.call(arguments);
this.service.log.call(this.service, args);
};

Server.prototype.debug = function(){
if (!this.service.assigned.options.debug) return;
var args = Array.prototype.slice.call(arguments);
args.unshift('DEBUG');
this.service.log.call(this.service, args);
};


// Private methods

Server.prototype._prepareOutgoingMessage = function(msg, attrs) {
if (this.service.use.json) msg = JSON.stringify(msg);

var buf = [this.service.assigned.id];

// Encode attributes into message if they exist
if (this.service.msgAttrs.length > 0) {
this.service.msgAttrs.map(function(attrName){
buf.push(attrs && attrs[attrName] || '');
});
}

buf.push(msg);

return buf.join('|');
};


module.exports = Server;

0 comments on commit bd8c8dc

Please sign in to comment.