Created
December 2, 2015 21:00
-
-
Save geek/0617b094c1e297f852c3 to your computer and use it in GitHub Desktop.
attempt to pull out actions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2010-2015 Richard Rodger, MIT License */ | |
'use strict' | |
var _ = require('lodash') | |
var Jsonic = require('jsonic') | |
var Common = require('./common') | |
var internals = {} | |
module.exports = function () { | |
var seneca = this | |
seneca.decorate('add', internals.add) | |
seneca.decorate('find', internals.find) | |
seneca.decorate('has', internals.has) | |
seneca.decorate('list', internals.list) | |
seneca.decorate('actroutes', internals.routes) | |
seneca.decorate('stats', internals.actions.stats) | |
// Legacy | |
seneca.decorate('findact', internals.find) | |
seneca.decorate('hasact', internals.has) | |
// Add builtin actions. | |
seneca.add({role: 'seneca', cmd: 'stats'}, internals.actions.stats) | |
seneca.add({role: 'seneca', cmd: 'close'}, internals.actions.close) | |
seneca.add({role: 'seneca', info: 'ready'}, internals.actions.ready) | |
seneca.add({role: 'seneca', info: 'fatal'}, internals.actions.fatal) | |
seneca.add({role: 'seneca', get: 'options'}, internals.actions.get) | |
// Legacy builtin actions. | |
seneca.add({role: 'seneca', stats: true}, internals.actions.stats) | |
seneca.add({role: 'seneca', ready: true}, internals.actions.ready) | |
seneca.add({role: 'options', cmd: 'get'}, internals.actions.get) | |
} | |
// ### seneca.add | |
// Add an message pattern and action function. | |
// | |
// `seneca.add(pattern, action)` | |
// * _pattern_ `o|s` → pattern definition | |
// * _action_ `f` → pattern action function | |
// | |
// `seneca.add(pattern_string, pattern_object, action)` | |
// * _pattern_string_ `s` → pattern definition as jsonic string | |
// * _pattern_object_ `o` → pattern definition as object | |
// * _action_ `f` → pattern action function | |
// | |
// The pattern is defined by the top level properties of the | |
// _pattern_ parameter. In the case where the pattern is a string, | |
// it is first parsed by | |
// [jsonic](https://github.com/rjrodger/jsonic) | |
// | |
// If the value of a pattern property is a sub-object, this is | |
// interpreted as a | |
// [parambulator](https://github.com/rjrodger/parambulator) | |
// validation check. In this case, the property is not considered | |
// part of the pattern, but rather an argument to validate when | |
// _seneca.act_ is called. | |
internals.add = function () { | |
var self = this | |
var args = Common.parsePattern(self, arguments, 'action:f? actmeta:o?') | |
var raw_pattern = args.pattern | |
var action = args.action || function (msg, done) { | |
done.call(this, null, msg.default$ || null) | |
} | |
var actmeta = args.actmeta || {} | |
// TODO: refactor plugin name, tag and fullname handling. | |
actmeta.plugin_name = actmeta.plugin_name || 'root$' | |
actmeta.plugin_fullname = actmeta.plugin_fullname || | |
actmeta.plugin_name + | |
(('-' === actmeta.plugin_tag ? void 0 : actmeta.plugin_tag) | |
? '/' + actmeta.plugin_tag : '') | |
var add_callpoint = callpoint() | |
if (add_callpoint) { | |
actmeta.callpoint = add_callpoint | |
} | |
actmeta.sub = !!raw_pattern.sub$ | |
actmeta.client = !!raw_pattern.client$ | |
// Deprecate a pattern by providing a string message using deprecate$ key. | |
actmeta.deprecate = raw_pattern.deprecate$ | |
var strict_add = (raw_pattern.strict$ && raw_pattern.strict$.add !== null) | |
? !!raw_pattern.strict$.add : !!so.strict.add | |
var pattern = self.util.clean(raw_pattern) | |
if (0 === _.keys(pattern)) { | |
throw internals.error('add_empty_pattern', {args: Common.clean(args)}) | |
} | |
var pattern_rules = _.clone(action.validate || {}) | |
_.each(pattern, function (v, k) { | |
if (_.isObject(v)) { | |
pattern_rules[k] = v | |
delete pattern[k] | |
} | |
}) | |
if (0 < _.keys(pattern_rules).length) { | |
actmeta.parambulator = Parambulator(pattern_rules, pm_custom_args) | |
} | |
var addroute = true | |
actmeta.args = _.clone(pattern) | |
actmeta.pattern = Common.argpattern(pattern) | |
// deprecated | |
actmeta.argpattern = actmeta.pattern | |
// actmeta.id = self.idgen() | |
actmeta.id = refnid() | |
actmeta.func = action | |
var priormeta = self.find(pattern) | |
// only exact action patterns are overridden | |
// use .wrap for pin-based patterns | |
if (strict_add && priormeta && priormeta.pattern !== actmeta.pattern) { | |
priormeta = null | |
} | |
if (priormeta) { | |
if (_.isFunction(priormeta.handle)) { | |
priormeta.handle( args.pattern, action ) | |
addroute = false | |
} | |
else { | |
actmeta.priormeta = priormeta | |
} | |
actmeta.priorpath = priormeta.id + ';' + priormeta.priorpath | |
} | |
else { | |
actmeta.priorpath = '' | |
} | |
// FIX: need a much better way to support layered actions | |
// this ".handle" hack is just to make seneca.close work | |
if (action && actmeta && _.isFunction(action.handle)) { | |
actmeta.handle = action.handle | |
} | |
var stats = { | |
id: actmeta.id, | |
plugin: { | |
full: actmeta.plugin_fullname, | |
name: actmeta.plugin_name, | |
tag: actmeta.plugin_tag | |
}, | |
prior: actmeta.priorpath, | |
calls: 0, | |
done: 0, | |
fails: 0, | |
time: {} | |
} | |
private$.stats.actmap[actmeta.argpattern] = | |
private$.stats.actmap[actmeta.argpattern] || stats | |
if (addroute) { | |
var addlog = [ actmeta.sub ? 'SUB' : 'ADD', | |
actmeta.id, Common.argpattern(pattern), action.name, | |
callpoint() ] | |
var isplugin = self.context.isplugin | |
var logger = self.log.log || self.log | |
if (!isplugin) { | |
// addlog.unshift('-') | |
// addlog.unshift('-') | |
// addlog.unshift('-') | |
addlog.unshift(actmeta.plugin_tag) | |
addlog.unshift(actmeta.plugin_name) | |
addlog.unshift('plugin') | |
} | |
logger.debug.apply(self, addlog) | |
private$.actrouter.add(pattern, actmeta) | |
} | |
return self | |
} | |
internals.find = function (args) { | |
var seneca = this | |
if (_.isString(args)) { | |
args = Jsonic(args) | |
} | |
var actmeta = seneca.private$.actrouter.find(args) | |
// if we have no destination, we look for | |
// a catch-all pattern and assign this, if | |
// it exists. | |
if (!actmeta) { | |
actmeta = seneca.private$.actrouter.find({}) | |
} | |
return actmeta | |
} | |
internals.has = function (args) { | |
return !!this.private$.actrouter.find(args) | |
} | |
internals.list = function (args) { | |
var seneca = this | |
args = _.isString(args) ? Jsonic(args) : args | |
var found = seneca.private$.actrouter.list(args) | |
found = _.map(found, function (entry) { | |
return entry.match | |
}) | |
return found | |
} | |
internals.routes = function () { | |
return this.private$.actrouter.toString(function (d) { | |
var s = 'F=' | |
if (d.plugin_fullname) { | |
s += d.plugin_fullname + '/' | |
} | |
s += d.id | |
while (d.priormeta) { | |
d = d.priormeta | |
s += ';' | |
if (d.plugin_fullname) { | |
s += d.plugin_fullname + '/' | |
} | |
s += d.id | |
} | |
return s | |
}) | |
} | |
internals.actions = { | |
close: function (args, done) { | |
this.emit('close') | |
done() | |
}, | |
get: function (args, done) { | |
var seneca = this | |
var options = seneca.private$.optioner.get() | |
var base = args.base || null | |
var root = base ? (options[base] || {}) : options | |
var val = args.key ? root[args.key] : root | |
done(null, Common.copydata(val)) | |
}, | |
fatal: function (args, done) { | |
done() | |
}, | |
ready: function (args, done) { | |
this.private$.wait_for_ready = false | |
this.emit('ready') | |
done() | |
}, | |
stats: function (args, done) { | |
var seneca = this | |
var private$ = seneca.private$ | |
var stats | |
args = args || {} | |
if (args.pattern && private$.stats.actmap[args.pattern]) { | |
stats = private$.stats.actmap[args.pattern] | |
stats.time = private$.timestats.calculate(args.pattern) | |
} | |
else { | |
stats = _.clone(private$.stats) | |
stats.now = new Date() | |
stats.uptime = stats.now - stats.start | |
stats.now = new Date(stats.now).toISOString() | |
stats.start = new Date(stats.start).toISOString() | |
var summary = | |
(null == args.summary && false) || | |
(/^false$/i.exec(args.summary) ? false : !!(args.summary)) | |
if (summary) { | |
stats.actmap = void 0 | |
} | |
else { | |
_.each(private$.stats.actmap, function (a, p) { | |
private$.stats.actmap[p].time = private$.timestats.calculate(p) | |
}) | |
} | |
} | |
if (done) { | |
done(null, stats) | |
} | |
return stats | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment