-
1
Install the needed packages
copynpm install -g moin npm install -g yo generator-moin
-
2
Configure Moin
copyyo moin
-
3
Create a Service
copyyo moin:service
-
4
Start the Server
copyWhich should give you a similar output like this:moin
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
- 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
{
"name":"test-module",
"moin":{
"type":"module",
"moduleDependencies":[
"moin-service-loader"
],
"settings":{
"angry":false
}
}
}
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)
{
"name":"test-service",
"moin":{
"type":"service",
"settings":{
"name":"World"
}
}
}
moin.on({
event: "test"
}, ()=>moin.hello(moin.getSettings().name));
moin.emit("test");
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
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());
Every Service has access to the Moin Event System. You can emit and listen for events and even get results from the eventhandlers.
moin.emit("testEvent", {
x: 3, y: 10
});
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.
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);
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.
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);
}
});
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";
});
When this MoinModule is active, changes made to your service files are recognized by the system and the service is Automatically reloaded