Moin JS, all rights reserved. 2016 ©

Installation & First Application

installation
  • 1

    Install the needed packages

    copy
    npm install -g moin
    npm install -g yo generator-moin
  • 2

    Configure Moin

    copy
    yo moin
  • 3

    Create a Service

    copy
    yo moin:service
    
  • 4

    Start the Server

    copy
    moin
    Which should give you a similar output like this:
    startup

Configuration

Moin and its generator create a config.json file, with all available options for the installed MoinModules. Every module has an active option, which defines if the module should be loaded on startup or not.

Core Settings

setting description
moin.modulePaths Array with the folders, which contain NodeModules
logging.level Defines the minimum level of a log-message to be displayed("debug","info","warning","error" or 0,1,2,3)
logging.disabled Array which filters the displayed log-messages. A log message has its source written in square bracket. By putting this source-string inside the logging.disabled array, these messages are hidden by the system.

Modules & Services

Two types of code are used in Moin: Modules & Services.

Modules

  • Adds functionality to the core and API methods for the services
  • Is loaded on startup and can not be unloaded
  • Settings are read from the config.json file
copy
{
  "name":"test-module",
  "moin":{
    "type":"module",
    "moduleDependencies":[
      "moin-service-loader"
    ],
    "settings":{
      "angry":false
    }
  }
}
copy
module.exports = (moin, settings)=> {
    let logger = moin.getLogger("test-module");

    //Add an API method for every new service
    moin.on("loadService", (handler)=> {
        handler.addAPI("hello", (name)=> {
            if (settings.angry) {
                logger.error(`Hello, ${name}!`);
            } else {
                logger.info(`Hello, ${name}!`);
            }
        });
    });
};

Services

  • Adds functionality for other services over the event system
  • Can be loaded and unloaded dynamically upon filesystem changes
  • Settings are read from a JSON file with the service's name(moin-service-settings module)
copy
{
  "name":"test-service",
  "moin":{
    "type":"service",
    "settings":{
      "name":"World"
    }
  }
}
copy
moin.on({
    event: "test"
}, ()=>moin.hello(moin.getSettings().name));

moin.emit("test");
In addition to the dedicated modules and services folders, both types of packages can be installed in the node_modules folder. This adds the possibility to add a service from the NPM repository on-the-fly (e.g. npm install moin-cron).

Services

Every piece of code you write will be placed inside a Service. A service has exactly one job. Some examples are:

  • Send a Mail
  • Open an HTTP Server
  • Send Events every full hour
  • Listen on Slack Messages
  • Convert an JS Array to a CSV String

Services communicate with each other over the Event System

In order to allow the Hot-Reload Feature of Moin, the services have to be unloueded in a proper Way

  • Timers, Timeouts and Immediate Calls get canceled automatically by the System.
  • Open files, network connections or other io handlers have to be closed by the Programmer.
  • For this purpose you can use moin.registerUnloadHandler
copy
let app = require("express")();
let server = app.listen(3000, function () {
    console.log("Webserver started")
});

//Send out an event for every get Reguest
app.get("*", function (req, res) {
    moin.emit("httpRequest", {
        method: "get",
        path: req.path
    }, 30000).then(({values,errors,stats})=> {
        //See how many Listeners have responded.
        switch(values.length){
            //No handler returned a Value -> send a 404 Error
            case 0:
                res.status(404).send("Not Found");
                break;
            //One or more Handler have returned a value -> send them to the Browser
            default:
                res.send(values.join("<br/>"));
        }
    })
});

//Close the Server when the Service is unloaded
moin.registerUnloadHandler(()=>server.close());

Event System

Every Service has access to the Moin Event System. You can emit and listen for events and even get results from the eventhandlers.

copy
moin.emit("testEvent", {
    x: 3, y: 10
});
copy
moin.on({}, ()=> {
    console.log("I get executed on any event!");
});
moin.on({
    event: "testEvent",
    x: 3
}, ()=> {
    console.log("I get executed on event 'testEvent' and an x value of 3. The y value is ignored");
});
moin.on({
    event: "testEvent",
    x: 3,
    y: 10
}, ()=> {
    console.log("I get executed on event 'testEvent' and an x value of 3 and an y value of 10.");
});
moin.on({
    event: "testEvent",
    x: 3,
    y: 10
}, ()=> {
    console.log("I get executed on event 'testEvent' and an x value of 3 and an y value of 10.");
});
moin.on({
    event: "testEvent",
    x: (x)=>x > 2,
    y: (y)=>y < 20
}, ()=> {
    console.log("I get executed on event 'testEvent' and an x value greater 2 and an y value of less than 20.");
});

Listener can return a value or a Promise. Errors get catched and reported to the Emitter.

copy
moin.emit("testEvent", {
    x: 3, y: 10
}).then((results)=> {
    console.log("Return values", results.values);//[5,2]
    console.log("Errors", results.errors);//["Error"]
    console.log("Handlers for this event:",results.stats.handler);//3
    console.log("Resolved Handler:",results.stats.resolve);//2
    console.log("Rejected Handler:",results.stats.rejected);//1
});

//Or with ES2016 destructuring:

moin.emit("testEvent", {
    x: 3, y: 10
}).then(({values,errors,stats})=> {
    console.log("Return values", values);//[5,2]
    console.log("Errors", errors);//["Error"]
    console.log("Handlers for this event:",stats.handler);//3
});

//You also have an Optional 3. parameter which sets an Timeout for the Handler.

moin.emit("testEvent", {
    x: 3, y: 10
}).then((results)=> {
    console.log("Return values", results.values);//[5]
    console.log("Errors", results.errors);//["Error"]
    console.log("Handlers for this event:",results.stats.handler);//3
    console.log("Resolved Handler:",results.stats.resolve);//1
    console.log("Rejected Handler:",results.stats.rejected);//1
    console.log("Rejected Handler:",results.stats.timeout);//1
},1000);
copy
moin.on({
    event: "testEvent"
}, (event)=> {
    return event.x + 2;
});

moin.on({
    event: "testEvent"
}, ()=> {
    return new Promise((resolve, reject)=> {
        setTimeout(()=>resolve(2), 5000);
    });
});

moin.on({
    event: "testEvent"
}, ()=> {
    throw "Error";
});

If you want only one Listener to get called and return a value, you can use moin.act instead of moin.emit. This only calls the best-fitting handler.

copy
moin.act("sqrt", {
    x: 25
}).then(({error,value})=> {
    if (error) {
        console.error("Error:", error);
    } else {
        console.log("sqrt(25) = " + value);
    }
});
moin.act("sqrt", {
    x: -25
}).then(({error,value})=> {
    if (error) {
        console.error("Error:", error);
    } else {
        console.log("sqrt(25) = " + value);
    }
});
copy
moin.on({
    event: "sqrt"
}, (event)=> {
    return Math.sqrt(event.x);
});

moin.on({
    event: "sqrt",
    x: (x)=>x < 0
}, ()=> {
    throw "Cannot calculate squareroot of a negative number";
});

Filesystem Watcher

When this MoinModule is active, changes made to your service files are recognized by the system and the service is Automatically reloaded