0.4.0 • Published 7 years ago

samjs v0.4.0

Weekly downloads
34
License
MIT
Repository
github
Last release
7 years ago

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-client

Simple 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

NameDescription
samjs-authadds a configs-based user management, authentification mechanismen and authorization system for configs.
samjs-filesadds a model and interface for file/folder interaction
samjs-files-authadds authorization for samjs-files
samjs-mongoadds a model and interface for mongodb interaction
samjs-mongo-authadds authorization for samjs-mongo
samjs-auth-mongomoves user management to mongodb. Adds groups.
samjs-mongo-isOwnerPlugin 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:

Nametypedefaultdescription
nameString-(required) name of the config object. To access the config object after setup :samjs.configs[name]
accessObject{read:false,write:false}allowes to access this config value by client
testFunction-Function to test values for validity. Must return a promise which resolves on success and rejects on error.
isRequiredBooleanfalseincludes this config object in configuration phase. (see lifecycle)
hooksObject-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.

Nameargumentsdescription
beforeCreateoptionsmanipulate the options object before creation
afterCreateoptionsmanipulate 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_Getdatacalled 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:

Nametypedefaultdescription
nameString-(required) name of the model object. To access the model object after setup :samjs.models[name]
interfacesObject or array-see below.
isRequiredBooleanfalseincludes this model object in installation phase. (see lifecycle)
installInterfaceFunction-a socket.io interface which is used in installation phase if isRequired is true (see installInterface)
testFunction-a function to test if installation requirement is met, if isRequired is true
dbString-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)

NameDescription
beforePluginsemitted before samjs.plugins() is executed
pluginsemitted after samjs.plugins() is executed
beforeOptionsemitted before samjs.options() is executed
optionsemitted after samjs.options() is executed
beforeConfigsemitted before samjs.configs() is executed
configsemitted after samjs.configs() is executed
beforeModelsemitted before samjs.models() is executed
modelsemitted after samjs.models() is executed
beforeStartupemitted before samjs.startup() is executed
startupemitted after samjs.startup() is executed
beforeShutdownemitted before samjs.shutdown() is executed
shutdownemitted after samjs.shutdown() is executed
beforeResetemitted before samjs.reset() is executed
resetemitted 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 interfaces

Events:

NameDescription
beforeConfigureemitted before configuration is set up and ready
configureemitted after configuration is set up and ready
configuredemitted after configuration is done or when no configuration was necessary
beforeInstallemitted before installation is set up and ready
installemitted after installation is set up and ready
installedemitted after installation is done or when no installation was necessary
startedemitted when samjs is properly started up

There are additional state promises which will fullfill once a state is reached:

NameDescription
onceConfigurefullfilled once in configuration mode
onceConfiguredfullfilled once configuration is finished
onceInstallfullfilled once in installation mode
onceInstalledfullfilled once installation is finished
onceStartedfullfilled once samjs is properly started up
onceConfigureOrInstallfullfilled 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