samjs v0.4.0
SAM.js
Created to connect Socket.io, AngularJS and MongoDB.
Now it is database and view framework agnostic, leaving a socket.io framework.
Browser client: samjs-client
Features
- only websockets
- plugable
- useful defaults
- multiple databases simultaniously
data modeling
options
source-code-based / kept-in-memory data. Used to set low-level options of the app, can only be changed by restarting. Not accessible due websockets.configs
file-based / kept-in-memory data For structural data specific for your app, should be only changed sparsely by few persons.models
Can be custom in-memory models or plugable database-based models. Should be used for all data which changes on regular basis.
Getting Started
npm install --save samjs
npm install --save-dev samjs-clientSimple example with koa server
samjs = require "samjs"
koa = require("koa")()
server = require("http").createServer(koa.callback())
samjs
.plugins()
.options()
.configs({name:"item"})
.models()
.startup(server)
server.listen(3000)
#to close
#samjs.shutdown()
// in browser with e.g. webpack
samjs = require("samjs-client")()
samjs.config.set("item", "value")
.then(function(){
//success
})
.catch(function(){
//failure
})
// some other client
samjs.config.on("item",function(){
//item changed
samjs.config.get("item")
.then(function(response){
response == "value" // true
})
.catch(function(){
//failure
})
}Available plugins
| Name | Description |
|---|---|
| samjs-auth | adds a configs-based user management, authentification mechanismen and authorization system for configs. |
| samjs-files | adds a model and interface for file/folder interaction |
| samjs-files-auth | adds authorization for samjs-files |
| samjs-mongo | adds a model and interface for mongodb interaction |
| samjs-mongo-auth | adds authorization for samjs-mongo |
| samjs-auth-mongo | moves user management to mongodb. Adds groups. |
| samjs-mongo-isOwner | Plugin for managing user owned documents in samjs-mongo. |
docs
Samjs is configured by several functions which must be called in order. Startup order:
samjs.plugins().options().configs().models().startup().io.listen(8080)Function: plugins
takes one or more plugin objects or an array of plugin objects. Returns samjs.
samjs.plugins(require("samjs-files"),require("samjs-auth"))
// or
samjs.plugins([require("samjs-files"),require("samjs-auth")])See plugin api for detailed information to write your own plugin
Function: options
takes a options object to overwrite the default values. Returns samjs.
// only default in core is samjs.options.config == "config.json"
// to overwrite:
samjs.options({config:"settings.json"})Function: configs
takes one or more config objects or an array of config objects. Returns samjs.
samjs.configs({name:"paths"},{name:"users"})
// or
samjs.configs([{name:"paths"},{name:"users"}])Props of a config object:
| Name | type | default | description |
|---|---|---|---|
| name | String | - | (required) name of the config object. To access the config object after setup :samjs.configs[name] |
| access | Object | {read:false,write:false} | allowes to access this config value by client |
| test | Function | - | Function to test values for validity. Must return a promise which resolves on success and rejects on error. |
| isRequired | Boolean | false | includes this config object in configuration phase. (see lifecycle) |
| hooks | Object | - | functions to get called on specific interactions with this config object. |
actions:
After setup, each config object has 3 available actions: set,get,test. Each has a server-side version: _set,_get,_test, which are not checking for authentification.
hooks:
hooks are functions which are called on specific actions. They always have to return their arguments.
| Name | arguments | description |
|---|---|---|
| beforeCreate | options | manipulate the options object before creation |
| afterCreate | options | manipulate the options object after creation |
| beforeSet | {data, socket} | used to authenticate a set request |
| before_Set | {data, oldData} | used to manipulate a set request |
| afterSet | {data, oldData, socket} | called after a successfull set request from client |
| after_Set | {data, oldData} | called after each successfull set request |
| beforeGet | {socket} | used to authenticate a get request |
| after_Get | data | called after a successfull _get request |
| afterGet | {data, socket} | called after a successfull get request from client |
| beforeTest | {data, socket} | used to authenticate a test request |
| afterTest | {data, socket} | called after a successfull test request from client |
example (how samjs-auth basically works):
samjs.configs({name:"paths", hooks: {
beforeSet: function(obj) {
if (obj.client.auth != null
&& obj.client.auth.user != null
&& obj.client.auth.user.username == "root") {
return obj
}
throw new Error "no permission"
}}
})Function: models
takes one or more model objects or an array of model objects. Returns samjs.
samjs.models(model1,model2)
// or
samjs.models([model1,model2])Props of a model object:
| Name | type | default | description |
|---|---|---|---|
| name | String | - | (required) name of the model object. To access the model object after setup :samjs.models[name] |
| interfaces | Object or array | - | see below. |
| isRequired | Boolean | false | includes this model object in installation phase. (see lifecycle) |
| installInterface | Function | - | a socket.io interface which is used in installation phase if isRequired is true (see installInterface) |
| test | Function | - | a function to test if installation requirement is met, if isRequired is true |
| db | String | - | use a model-structure and interface from a plugin |
interfaces
Interfaces can be either a array or a key-value store
// all interfaces in an array will listen one the "someModel" socket.io namespace
samjs.models({
name: "someModel"
value: "someValue"
interfaces: [
// will be in the "someModel" socket.io namespace
function(socket){
var model = this
// model.name == "someModel" // true
socket.on("get",function(request){
if (request.token != null){
socket.emit("get."+.request.token,{success:true, content:model.value})
}
})
}
]
})
// if you need to use another namespace use an object instead
samjs.models({
name: "someModel"
interfaces: {
// either provide a single interface or a array of interfaces
// will be in the "someOtherNamespace" socket.io namespace
someOtherNamespace: function(socket){ //doSomething }
}
})installInterface
samjs.models({
name: "someModel"
installInterface: function(socket){
var model = this # will be bound to model instance
# will be in the 'install' socket-io namespace, specific listeners are required
socket.on "someModel.set", function(request) =>
if (request.token != null && request.content != null){
# no authentification, will be only accessible in install mode
model.test(request.content)
.then(function(value){model.value = value})
.then(function(value){return {success:true, content: value}}
.catch(function(e){return {success:false, content: e.message}}
.then(function(response){
socket.emit("boilerplate.set."+request.token, response)
if(response.success){
samjs.state.checkInstalled() # will trigger a check if installation is finished
}
}
}
# must return a dispose function
return function(){socket.removeAllListeners("someModel.set")}
}
})Function: startup
takes a optional httpServer, returns samjs.
samjs.startup(someServer)
someServer.listen(8080)
// or
samjs.startup()
samjs.io.listen(8080)Function: shutdown
returns samjs. Shuts samjs and socket.io down.
Function reset
returns samjs. Resets samjs instance. Useful for unit testing.
Lifecycle
samjs emits several events during its lifecycle:
example:
samjs.once("options",function(){console.log("options got called")})Synchronous (configuration / after startup)
| Name | Description |
|---|---|
| beforePlugins | emitted before samjs.plugins() is executed |
| plugins | emitted after samjs.plugins() is executed |
| beforeOptions | emitted before samjs.options() is executed |
| options | emitted after samjs.options() is executed |
| beforeConfigs | emitted before samjs.configs() is executed |
| configs | emitted after samjs.configs() is executed |
| beforeModels | emitted before samjs.models() is executed |
| models | emitted after samjs.models() is executed |
| beforeStartup | emitted before samjs.startup() is executed |
| startup | emitted after samjs.startup() is executed |
| beforeShutdown | emitted before samjs.shutdown() is executed |
| shutdown | emitted after samjs.shutdown() is executed |
| beforeReset | emitted before samjs.reset() is executed |
| reset | emitted after samjs.reset() is executed |
Asynchronous (startup)
samjs.startup works like this:
Is samjs configured? (all configs with `isRequired` are set properly)
- start configuration if not
Execute all `startup` functions of all plugins
Execute all `startup` functions of all models
Is samjs installed? (all models with `isRequired` are set properly)
- start installation if not
expose all socket.io interfacesEvents:
| Name | Description |
|---|---|
| beforeConfigure | emitted before configuration is set up and ready |
| configure | emitted after configuration is set up and ready |
| configured | emitted after configuration is done or when no configuration was necessary |
| beforeInstall | emitted before installation is set up and ready |
| install | emitted after installation is set up and ready |
| installed | emitted after installation is done or when no installation was necessary |
| started | emitted when samjs is properly started up |
There are additional state promises which will fullfill once a state is reached:
| Name | Description |
|---|---|
| onceConfigure | fullfilled once in configuration mode |
| onceConfigured | fullfilled once configuration is finished |
| onceInstall | fullfilled once in installation mode |
| onceInstalled | fullfilled once installation is finished |
| onceStarted | fullfilled once samjs is properly started up |
| onceConfigureOrInstall | fullfilled once in configuration or installation mode |
example
samjs.state.onceStarted.then(function(){
// do something
})Plugin api
see source code of samjs-plugin-boilerplate for a complete plugin api