UNPKG

allex_libcloudexposurelib

Version:

AllexJS lib that generates the necessary glue code for exposing a lib via the Apartment/Hotel/Service chain

246 lines (241 loc) 9.77 kB
function createServiceMixin (execlib, templateslib, lockingjoblib, mylib) { 'use strict'; var lib = execlib.lib; var q = lib.q; var qlib = lib.qlib; var execSuite = execlib.execSuite; var taskRegistry = execlib.execSuite.taskRegistry; function produceServerMixinCtor (desc) { var ret; eval('ret = '+templateslib.process({ template: [ 'function FUNCTIONNAME () {', '}' ].join('\n'), replacements: { FUNCTIONNAME: desc.name+'ServiceMixin' } })); return ret; } function produceServerMixinDtor (ctor, desc) { ctor.prototype.destroy = Function (); } function thiser (thingy) { return 'this.'+thingy; } function jobInvoker (qdesc) { if (!qdesc) { return 'job.go()'; } if (lib.isString(qdesc) && qdesc) { return "this.jobs.run('"+qdesc+"', job)"; } if (lib.isString(qdesc.q) && qdesc.q && qdesc.locked) { return "lockingjoblib.runLockedOn(this.jobs, '"+qdesc.q+"', job)"; } throw new Error('QUEUE_DESCRIPTOR_STRUCTURE_NOT_SUPPORTED'); } function monitorVarer (res, state, index) { if (!state) { return res; } switch (state.type) { case 'historyBuilder': res.push('historybuilder'+index); break; case 'reportChange': res.push('reportchanger'+index); res.push('reportfailchanger'+index); break; } return res; } function reportChangerParamer (param, index, arry) { if (index >= arry.length-1) { return 'res'; } return param; } function reportFailChangerParamer (param, index, arry) { if (index >= arry.length-1) { return 'reason'; } return param; } function monitorFunctioner (methodparams, debugjob, res, state, index) { if (!state) { return res; } switch (state.type) { case 'historyBuilder': res.push( '\tif (!lib.isFunction(thelib.jobs.JOBCLASS.createHistoryBuilder)) {', "\t\tthrow new lib.Error('NO_HISTORYBUILDERCREATOR', 'Job JOBCLASS must have a static method named createHistoryBuilder');", '\t}', '\thistorybuilder'+index+' = thelib.jobs.JOBCLASS.createHistoryBuilder(mylib.jobs);' ); break; case 'reportChange': res.push( '\tif (!lib.isFunction(thelib.jobs.JOBCLASS.calculateChange)) {', "\t\tthrow new lib.Error('NO_CHANGECALCULATOR', 'Job JOBCLASS must have a static method named calculateChange');", '\t}', '\treportchanger'+index+' = function ('+methodparams.map(reportChangerParamer).join(', ')+') {', '\t\ttry{', "\t\t\tvar args = Array.prototype.slice.call(arguments).concat([this.state.get('"+state.name+"')]);", '\t\t\tvar change = thelib.jobs.JOBCLASS.calculateChange.apply(thelib.jobs.JOBCLASS, args);', "\t\t\tif (lib.isVal(change)) { "+(debugjob ? "console.log('setting change', change, 'on state name \""+state.name+"\"'); " : '')+"this.set('"+state.name+"', change); }", '\t\t} catch (e) {console.error(e);}', '\t};', '\treportfailchanger'+index+' = lib.isFunction(thelib.jobs.JOBCLASS.calculateChangeOnFailure) ? function ('+methodparams.map(reportFailChangerParamer).join(', ')+') {', '\t\ttry{', "\t\t\tvar args = Array.prototype.slice.call(arguments).concat([this.state.get('"+state.name+"')]);", '\t\t\tvar change = thelib.jobs.JOBCLASS.calculateChangeOnFailure.apply(thelib.jobs.JOBCLASS, args);', "\t\t\tif (lib.isVal(change)) { "+(debugjob ? "console.log('setting change on failure', change, 'on state name \""+state.name+"\"'); " : '')+"this.set('"+state.name+"', change); }", '\t\t} catch (e) {console.error(e);}', '\t\tthrow args[args.length-2]', '\t} : null;' ); break; } return res; } function reportChangerBinder (res, param, index, arry) { if (index >= arry.length-1) { return res; } res.push(param); return res; } //static on service function historyBuilderProgresser (statename) { console.log('historyBuild progress on', statename, require('util').inspect(arguments[1], {depth:11})); this.set.apply(this, Array.prototype.slice.call(arguments)); console.log('after set,', statename, 'on state', require('util').inspect(this.state.data.get(statename), {depth:11})); } //endof static on service function monitorInvoker (methodparams, debugjob, res, state, index) { if (!state) { return res; } switch (state.type) { case 'historyBuilder': if (debugjob) { res.push('new historybuilder'+index+"(promise).go().then(null, null, historyBuilderProgresser.bind(this, '"+state.name+"'));"); } else { res.push('new historybuilder'+index+"(promise).go().then(null, null, this.set.bind(this, '"+state.name+"'));"); } break; case 'reportChange': res.push( (state.blockoriginalresult ? 'promiseresult = ': '')+'promise.then(', 'reportchanger'+index+'.bind('+methodparams.reduce(reportChangerBinder,['this']).join(', ')+'),', 'reportfailchanger'+index+' ? reportfailchanger'+index+'.bind('+methodparams.reduce(reportChangerBinder,['this']).join(', ')+') : null', ');' ); break; } return res; } function promiseMonitors (states, methodparams, debugjob) { if (!lib.isArray(states)) { return ''; } var varlines, funclines, ret; varlines = states.reduce(monitorVarer, []).join(', '); funclines = states.reduce(monitorFunctioner.bind(null, methodparams, debugjob), []).join('\n'); methodparams = null; debugjob = null; ret = []; if (varlines && funclines) { varlines = '\tvar '+varlines+';' ret.push(varlines); ret.push(funclines); } return ret.join('\n'); } function promiseMonitorInvocation (states, methodparams, debugjob) { if (!lib.isArray(states)) { return ''; } var invoclines = states.reduce(monitorInvoker.bind(null, methodparams, debugjob), []).join('\n'); methodparams = null; debugjob = null; return invoclines; } function promise2Defer (debugged, methodname) { return 'qlib.promise2defer('+(debugged?'qlib.promise2console(':'')+'promiseresult||promise'+(debugged?')':'')+', defer);'; } function produceServerMixinMethod (mixin, servicename, thelib, methoddescs, methodname) { var methoddescarry = mylib.utils.makeUpDescriptors(methoddescs); var funcparams = methoddescarry.map(mylib.utils.parameterProducer); var methodparams = funcparams.concat(['defer']); var svcinvoc = mylib.utils.serviceInvocationFromDescriptors(methoddescs); var jobparams = svcinvoc ? (svcinvoc.servicefieldspre||[]).map(thiser).concat(funcparams) : funcparams; var jobinvocationbody, funcbody, totalbody; var debugjob = lib.isArray(methoddescarry) && methoddescarry.length>0 && methoddescarry[0].debugonservice; if (!(svcinvoc && svcinvoc.jobclass)) { throw new lib.JSONizingError('NO_JOBCLASS_IN_METHODDESCRIPTOR', methoddescs, 'must have a jobclass'); } try { jobinvocationbody = jobInvoker(svcinvoc.q); } catch (e) { throw new lib.JSONizingError(e.message, methoddescs, 'Invalid q descriptor'); } funcbody = templateslib.process({ template: [ "MONITORLINES", '\tfunction METHODNAME (METHODPARAMS) {', '\t\tvar job = new thelib.jobs.JOBCLASS(JOBPARAMS);', "\t\tPROMISEPRODUCTIONLINE", "\t\tPROMISERETURNERPRODUCTIONLINE", "\t\tPROMISEINVOCATIONLINES", "\t\tLINKINGLINE", "\t};" ].join('\n'), replacements: { MONITORLINES: promiseMonitors(svcinvoc.states, methodparams, debugjob), METHODNAME: methodname, METHODPARAMS: methodparams.join(', '), JOBCLASS: svcinvoc.jobclass, JOBPARAMS: jobparams.join(', '), PROMISEPRODUCTIONLINE: 'var promise = '+jobinvocationbody+';', PROMISERETURNERPRODUCTIONLINE: 'var promiseresult;', PROMISEINVOCATIONLINES: promiseMonitorInvocation(svcinvoc.states, methodparams, debugjob), LINKINGLINE: promise2Defer(debugjob, methodname) } }); totalbody = 'mixin.prototype[methodname] = (function (lib, qlib, lockingjoblib, thelib) {\n'+funcbody+'\n\treturn '+methodname+';\n})(lib, qlib, lockingjoblib, thelib);'; try { eval(totalbody); } catch (e) { console.error(totalbody); throw e; } } function produceServerMixinMethodForService (targetservicename, thelib, mixin, servicedesc) { var servicename = servicedesc.service; if (targetservicename != servicename) { return mixin; } var _mixin = mixin; lib.traverseShallow(servicedesc.desc, produceServerMixinMethod.bind(null, _mixin, servicename, thelib)); _mixin = null; servicename = null; thelib = null; return mixin; } function produceServiceMixin (desc, targetservicename) { var mixin = produceServerMixinCtor(desc); var thelib = desc.thelib; produceServerMixinDtor(mixin, desc); (desc.services||[]).reduce(produceServerMixinMethodForService.bind(null, targetservicename, thelib), mixin); mylib.utils.produceAddMethods(mixin, desc, targetservicename); targetservicename = null; thelib = null; return mixin; } return produceServiceMixin; } module.exports = createServiceMixin;