README
xcraft-core-goblin
Xcraft uService API
Welcome on board!
Goblins are small, green (or yellow-green) creatures with pointy features and high intelligence (though often little common sense). Goblins speak Goblin, Orcish, and Common. Goblins know myriad languages in order to trade with as many races as possible.
With goblin you can craft some redux uService on top of the Orcish Xcraft toolchain infrastructure.
This package provide a thin API and conventions for building your first goblin.
Goblins
When you implement a goblin, you must think like a goblin.
Goblin instances
By default, the goblins are instantiated by a create command:
const extractor = yield quest.create ('gold-extractor', payload)
When an instance is created this way in a goblin quest, you own the created instance, and some rules apply:
When your gobelin (the owner) is deleted, sub-creations is also automagically deleted with a call to the delete quest.
quest.create (namespace, args)
command
The Under the hood, quest.create
sends aquest.cmd ('gold-extractor.create', {...payload})
and returns an object containing id
and all wrapped public quests.
Variants
The createFor variant quest.createFor (owner-namespace, id, namespace, args)
,
allow you to define the owner manually, so when the specified owner is deleted,
this creation will be too.
Single instance
Some goblins can be created as singleton. In this case, quest.create
will
not work. You must send a command to the goblin directly with quest.cmd
.
quest lifetime scope
We use quest.defer ()
for registering a function to be run when the current
quest is finished.
// Example of use for defering when we leave the quest
goblin.registerQuest ('test', (quest) => {
const extractor = yield quest.create ('gold-extractor');
// We defer the delete quest, after this quest
quest.defer (extractor.delete);
const gold = extractor.extract ('http://mineofgold.com');
/* ... */
});
goblin lifetime scope
We use quest.goblin.defer ()
for registering a function to be run after the
quest deletion of our goblin instance.
// Example of use for defering when we leave the delete of this instance
goblin.registerQuest ('create', (quest) => {
const extractor = yield quest.create ('gold-extractor');
// We defer the delete quest, after our goblin delete quest run
quest.goblin.defer (extractor.delete);
const gold = extractor.extract ('http://mineofgold.com');
/* ... */
});
Quests
A quest is a powerfull running context for dealing with other goblins. You can create goblins, running dedicated commands, sending events for signaling some status, waiting for other goblins events and dispatch actions for modifing your state.
From another point of view, quests are providing lifetime handlers for a goblin
instance, the creation of a goblin instance is always handled by the create
quest and the delete
quest provides the deletion implementation of an
instance.
Other quests, are like methods of an instance, add
, remove
, open
,
close
etc...
create and delete quests
When you configure a goblin, you must register a create
and a delete
quest.
This is not true for a single instance (singleton) goblin.
Widget example
We create a widget goblin named panel
, we register and implement:
create
(required!)delete
(required!)toggle
set-title
usage for this example:
const panel = yield quest.create ('panel');
quest.goblin.defer (panel.delete);
panel.toggle ();
panel.setTitle ({title: 'Hello World'});
Single instance service example
We create a single instance goblin named window-manager
, we register and
implement:
init
win.create
(required!)win.delete
(required!)win.show
usage for this example:
const win = yield quest.create ('wm');
quest.goblin.defer ( quest.release(win.id));
win.show ();
Quest handlers
// Example of a `create` quest
goblin.registerQuest ('create', (quest, somedata) => {
quest.dispatch ('mutate', {somedata});
// dispatch 'create' with automatic payload:
quest.do ();
// logging
quest.log.info ('done!');
// cmd sending
yield quest.cmd ('somegoblin.somequest', {
someparam: 'value',
});
// event sending
// the final topic is prefixed with your goblin name
quest.evt ('bim.bam.boom', {some: payload});
// (sub|unsub) scribe to events
// full topic name is required
const unsub = quest.sub ('somegoblin.topic', handler => yo);
unsub ();
// wait on an event
yield quest.sub.wait ('somegoblin.topic');
// Create widget via goblins
const panel = yield quest.create ('panel');
quest.goblin.defer (panel.delete);
panel.toggle ();
panel.setTitle ({title: 'Hello World'});
});
Goblin state persistence "feat. Ellen Ripley"
const ripleyConfig = {
DISPATCH_TYPENAME_TO_REPLAY: {
mode: 'all',
},
ANOTHER_TYPENAME_TO_REPLAY: {
mode: 'last',
},
YA_TYPE_BYKEY: {
mode: 'allbykeys',
keys: ['key1'],
},
};
// Give the ripley config at last argument
const goblin = new Goblin(goblinName, logicState, logicHandlers, ripleyConfig);
Goblin Shredder
Mutate your state with de Super Reaper 6000 mega shredder!
const logicState = new Goblin.Shredder({
gold: 0,
});
const logicHandlers = {
cashin: (state, action) => {
state = state.set('collection.key', {bid: ['ule', 'oche']});
if (state.includes('collection.key[0]', 10)) {
const key = state.get(`collection.key.bid[0]`, 2);
state = state.set('collection.lol', key);
state = state.del('collection.lol');
}
return state;
},
};
const goblin = new Goblin(goblinName, logicState, logicHandlers);
Your first goblin
Part 1: providing quest
Create a folder named goblin-treasure
with a treasure.js
file for
registering your namespace and quests on the Xcraft server:
'use strict';
/**
* Retrieve the list of available commands.
*
* @returns {Object} The list and definitions of commands.
*/
exports.xcraftCommands = function () {
return require(`./widgets/${require('path').basename(
__filename,
'.js'
)}/service.js`);
};
You must now implement the quest in ./widgets/treasure/service.js
.
Part 2: quest implementation with goblin
Create a file in a ./widgets/treasure/
subfolder named service.js
.
Extract the namespace and require
the Goblin:
'use strict';
const path = require('path');
const goblinName = path.basename(module.parent.filename, '.js');
const Goblin = require('xcraft-core-goblin');
Define the initial state of the goblin:
// Define initial logic values
const logicState = {
gold: 0,
};
Define the logic behind the cashin
quest:
// Define logic handlers according rc.json
const logicHandlers = {
cashin: (state, action) => {
if (!isNaN(Number(action.meta.amount))) {
state.gold += Number(action.meta.amount);
state.valid = true;
} else {
state.valid = false;
}
return state;
},
};
And finally create a goblin:
// Create a Goblin with initial state and handlers
const goblin = new Goblin(goblinName, logicState, logicHandlers);
// Register quest's according rc.json
goblin.registerQuest('cashin', function* (quest, msg) {
quest.do();
});
// We must export the quests
module.exports = goblin.quests;