UNPKG

plugd

Version:

flux pattern based framework on the mbp-fbp derivative

1,211 lines (1,074 loc) 33.7 kB
"use strict"; var stacks = require("stackq"); var AllTrue = stacks.funcs.always(true); var ema = []; var PSMeta = { task: true, reply: true}; var MessagePicker = function(n){ return n['message']; }; var inNext = function(n,e){ return n(); }; var Packets = exports.Packets = stacks.Persisto.extends({ init: function(id,body,uuid){ stacks.Asserted(stacks.valids.exists(id),"id is required (id)"); stacks.Asserted(stacks.valids.exists(body),"body is required (body)"); this.$super(); this.message = id; this.traces = []; this.body = body || {}; this.uuid = uuid || stacks.Util.guid(); this.type = 'packet'; var lock = false, plug, plate; this.$secure('locked',function(){ return !!lock; }); this.$secure('lock',function(){ lock = !lock; }); this.$secure('fromPlug',function(p){ if(!Plug.isInstance(p) || plug){ return; } plug = p; }); this.$secure('fromPlate',function(p){ if(!Plate.isInstance(p) || plate){ return; } plate = p; }); this.$secure('plug',function(){ return plug; }); this.$secure('plate',function(){ return plate; }); }, toString: function(){ return [this.message,this.uuid,this.type].join(':'); } },{ isPacket: function(p){ return Packets.isType(p); }, isTask: function(p){ return TaskPackets.isInstance(p); }, isReply: function(p){ return TaskPackets.isInstance(p); }, }).muxin({}); var TaskPackets = exports.TaskPackets = Packets.extends({ init: function(){ this.$super.apply(this,arguments); this.type ='task'; } },{ from: function(p,b,u){ if(!Packets.isReply(p)){ return;} var pp = TaskPackets.make(p.uuid,b,u); pp.Meta = { m: p.message, b: p.body }; return pp; }, clone: function(p,m,b){ if(!Packets.isTask(p)){ return; } var tc = TaskPackets.make(m,b || p.body,p.uuid); tc.Meta = { m: p.message, b: p.body }; p.link(tc); return tc; }, proxy: function(fx){ return { make: function(){ var f = TaskPackets.make.apply(TaskPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; }, clone: function(){ var f = TaskPackets.clone.apply(TaskPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; }, from: function(){ var f = TaskPackets.from.apply(TaskPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; } }; } }); var ReplyPackets = exports.ReplyPackets = Packets.extends({ init: function(){ this.$super.apply(this,arguments); this.type ='reply'; } },{ from: function(p,b,u){ if(!Packets.isTask(p)){ return;} var pp = ReplyPackets.make(p.uuid,b,u); pp.Meta = { m: p.message, b: p.body }; return pp; }, clone: function(p,m,b){ if(!Packets.isReply(p)){ return; } var tc = ReplyPackets.make(m,b || p.body,p.uuid); tc.Meta = { m: p.message , b: p.body}; p.link(tc); return tc; }, proxy: function(fx){ return { make: function(){ var f = ReplyPackets.make.apply(ReplyPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; }, clone: function(){ var f = ReplyPackets.clone.apply(ReplyPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; }, from: function(){ var f = ReplyPackets.from.apply(ReplyPackets,arguments); if(stacks.valids.Function(fx)) fx.call(f,f); return f; } }; } }); var Store = exports.Store = stacks.FunctionStore.extends({ register: function(){ return this.add.apply(this,arguments); }, unregister: function(){ return this.remove.apply(this,arguments); }, }); var SelectedChannel = exports.SelectedChannel = stacks.FilteredChannel.extends({ init: function(id,picker,fx){ this.$super(id,picker || MessagePicker); this.lockTasks = stacks.Switch(); this.lockproxy = stacks.Proxy(this.$bind(function(f,next,end){ if(!Packets.isPacket(f)) return; if(this.taskLocking()){ if(stacks.valids.isFunction(f.locked) && !!f.locked()) return; } return next(); })); this.mutts.add(function(f,next,end){ if(!Packets.isPacket(f)) return; return next(); }); if(stacks.valids.Function(fx)) fx.call(this); this.mutts.add(this.lockproxy.proxy); var bindings = {}; this.bindOut = this.$bind(function(chan){ if(!SelectedChannel.isType(chan) || stacks.valids.contains(bindings,chan.GUUID)) return; bindings[chan.GUUID] = { out: this.stream(chan), in: { unstream: function(){}} }; }); this.bindIn = this.$bind(function(chan){ if(!SelectedChannel.isType(chan) || stacks.valids.contains(bindings,chan.GUUID)) return; bindings[chan.GUUID] = { in: chan.stream(this), out: { unstream: function(){}} }; }); this.unbind = this.$bind(function(chan){ if(!SelectedChannel.isType(chan) || stacks.valids.not.contains(bindings,chan.GUUID)) return; var p = this.bindings[chan.GUUID]; p.in.unstream(); p.out.unstream(); }); this.unbindAllChannel = this.$bind(function(chan){ stacks.enums.each(bindings,function(e,i,o,fn){ if(chan && i === chan.GUUID) return fn(null); e.in.unstream(); e.out.unstream(); return fn(null); }); }); }, enlock: function(){ this.lockTasks.on(); }, dislock: function(){ this.lockTasks.off(); }, taskLocking: function(){ return this.lockTasks.isOn(); }, mutate: function(fn){ this.mutts.add(fn); }, unmutate: function(fn){ this.mutts.remove(fn); }, }); var TaskChannel = exports.TaskChannel = SelectedChannel.extends({ init: function(id,picker){ this.$super(id,picker || MessagePicker,function(){ this.mutts.add(function(f,next,end){ if(!Packets.isTask(f)) return; return next(); }); }); this.mutts.add(this.bind(function(f,next,end){ f.lock(); return next(); })); } }); var ReplyChannel = exports.ReplyChannel = SelectedChannel.extends({ init: function(id,picker){ this.$super(id,picker || MessagePicker,function(){ this.mutts.add(function(f,next,end){ if(!Packets.isReply(f)) return; return next(); }); }); } }); var Plug = exports.Plug = stacks.Configurable.extends({ init: function(id,gid,fn){ this.$super(); stacks.Asserted(stacks.valids.isString(id),"first argument must be a string"); var bindings = [],network; this.idProxy = stacks.Proxy(this.$bind(function(d,n,e){ if(Packets.isPacket(d)){ d.traces.push(this.GUUID); } return n(); })); this.points = Store.make('points',stacks.funcs.identity); this.internalTasks = Store.make('tasks',stacks.funcs.identity); this.internalReplies = Store.make('replies',stacks.funcs.identity); this.tweakAllTasks = this.$bind(function(fc){ this.internalTasks.each(function(e,i,o,fx){ if(stacks.valids.Function(fc)) fc.call(e,e,i); fx(null); },fc) }); this.tweakAllReplies = this.$bind(function(fc){ this.internalReplies.each(function(e,i,o,fx){ if(stacks.valids.Function(fc)) fc.call(e,e,i); fx(null); },fc) }); this.pauseAllTasks = this.$bind(function(fc){ this.internalTasks.each(function(e,i,o,fx){ if(stacks.valids.Function(e.pause)) e.pause(); fx(null); },fc) }); this.pauseAllReplies = this.$bind(function(fc){ this.internalReplies.each(function(e,i,o,fx){ if(stacks.valids.Function(e.pause)) e.pause(); fx(null); },fc) }); this.resumeAllTasks = this.$bind(function(fc){ this.internalTasks.each(function(e,i,o,fx){ if(stacks.valids.Function(e.pause)) e.resume(); fx(null); },fc) }); this.resumeAllReplies = this.$bind(function(fc){ this.internalReplies.each(function(e,i,o,fx){ if(stacks.valids.Function(e.pause)) e.resume(); fx(null); },fc) }); this.id = id; this.gid = gid; this.makeName = this.$bind(function(sn){ if(stacks.valids.not.String(sn)){ return; } var m; if(stacks.valids.String(this.id)) m = this.id; else m = this.gid; return [m,sn].join('.'); }); this.channel = TaskChannel.make(id); this.replyChannel = ReplyChannel.make(stacks.funcs.always(true)); this.channel.mutts(this.idProxy.proxy); this.replyChannel.mutts(this.idProxy.proxy); // this.channel.pause(); // this.replyChannel.pause(); this.channel.enlock(); this.configs.add('id',id); this.configs.add('gid',gid); var plate = null,bind,bindrs; this.pub('boot'); this.pub('networkAttached'); this.pub('networkDetached'); this.pub('attachPlate'); this.pub('detachPlate'); this.pub('release'); var self = this; this.Reply = ReplyPackets.proxy(function(){ self.emitPacket(this); }); this.Task = TaskPackets.proxy(function(){ self.emitPacket(this); }); this.isAttached = this.$closure(function(){ return plate != null; }); this.hasNetwork = this.$closure(function(){ return network != null; }); this.attachPlate = this.$closure(function(pl){ if(this.isAttached() || !Plate.isInstance(pl)) return; bind = pl.channel.stream(this.channel); bindrs = this.replyChannel.stream(pl.channel); plate = pl; this.emit('attachPlate',pl); // this.boot(); }); this.detachPlate = this.$closure(function(){ this.emit('detachPlate',plate); if(this.isAttached()){ bind.unstream(); bindrs.unstream(); } plate = bind = null; this.destoryBindings(); }); this.$secure('dispatch',function (t) { if(!this.isAttached() || !Packets.isPacket(t)) return; t.fromPlug(t); plate.dispatch(t); }); this.$secure('dispatchReply',function (t) { if(!Packets.isPacket(t)) return; t.fromPlug(t); console.log('sending reply in plug',t.body,this.id); this.replyChannel.emit(t); }); this.afterPlate = this.$closure(function(fn){ if(!this.isAttached()){ return this.afterOnce('attachPlate',this.$bind(function(f){ return fn.call(this,plate); })); } return (stacks.valids.isFunction(fn) ? fn.call(this,plate) : null); }); this.destoryBindings = this.$bind(function(){ stacks.enums.each(bindings,function(e,i,o,fn){ e.unstream(); fn(null); },function(){ bindings.length = 0; }); }); this.newTaskChannel= this.$bind(function(id,tag,picker){ stacks.Asserted(arguments.length > 0,'please supply the id, tag for the channel'); if(arguments.length === 1) stacks.Asserted(stacks.valids.isString(id),'key for the channel must be a string') if(arguments.length == 1 && stacks.valids.isString(id)) tag = id; stacks.Asserted(!this.internalTasks.has(id),'id "'+id+'" is already in use'); var tk = TaskChannel.make(tag,picker); this.internalTasks.add(id,tk); this.afterPlate(function(pl){ var br = pl.channel.stream(tk); bindings.push(br); }); return tk; }); this.newReplyChannel= this.$bind(function(id,tag,picker){ stacks.Asserted(arguments.length > 0,'please supply the id, tag for the channel'); if(arguments.length === 1) stacks.Asserted(stacks.valids.isString(id),'key for the channel must be a string') if(arguments.length == 1 && stacks.valids.isString(id)) tag = id; stacks.Asserted(!this.internalReplies.has(id),'id "'+id+'" is already in use'); var tk = ReplyChannel.make(tag,picker); this.internalReplies.add(id,tk); this.afterPlate(function(pl){ var br = tk.stream(pl.channel) bindings.push(br); }); return tk; }); this.attachNetwork = this.$bind(function(net){ if(!Network.isInstance(net) || this.hasNetwork()) return; network = net; this.emit('networkAttached',net); this.withNetwork(this.tasks(),this.replies()); }); this.detachNetwork = this.$bind(function(){ if(!this.hasNetwork()) return; this.tasks().unbind(network.plate.channel); network = null; this.emit('networkDetached',net); }); this.networkOut = this.$bind(function(chan){ if(!SelectedChannel.isType(chan)) return; this.afterOnce('networkAttached',function(net){ if(network){ network.bindOut(chan); } }); this.afterOnce('networkDetached',function(net){ if(network){ network.unbind(chan); } }); }); this.networkIn = this.$bind(function(chan){ if(!SelectedChannel.isType(chan)) return; this.afterOnce('networkAttached',function(net){ if(network){ network.bindIn(chan); } }); this.afterOnce('networkDetached',function(net){ if(network){ network.unbind(chan); } }); }); this.withNetwork = this.$bind(function(chan,xchan){ this.networkIn(chan); this.networkOut(xchan); }); this.exposeNetwork = this.$bind(function(fx){ if(stacks.valids.Function(fx)) fx.call(network); return network; }); this.$rack(fn); }, changeContract: function(n){ this.channel.changeContract(n); }, replies: function(f){ if(f) return this.internalReplies.get(f); return this.replyChannel; }, tasks: function(f){ if(f) return this.internalTasks.get(f); return this.channel; }, point: function(alias){ return this.points.Q(alias); }, attachPoint: function(fn,filter,alias,k){ if(alias && this.points.has(alias)) return; var pt = PlugPoint(fn,filter,k)(this); if(stacks.valids.notExists(alias)) stacks.Util.pusher(this.points.registry,pt); else this.points.add(alias,pt); return pt; }, detachPoint: function(item){ if(stacks.valids.isString(item) && this.points.has(item)){ var pt = this.points.Q(item); if(stacks.valids.isFunction(pt.close)) pt.close(); this.points.remove(item); return pt; }; if(stacks.valids.isObject(item)){ var self = this; this.points.each(function(f,i,o,fn){ if(f == item){ if(stacks.valids.isFunction(f.close)) f.close(); self.points.remove(i); return fn(true); } }); return item; } }, detachAllPoint: function(){ this.points.each(function(f,i,o,fn){ if(stacks.valids.isFunction(f.close)) f.close(); return fn(true); }); }, release: function(){ this.detachPlate(); this.emit('release',this); }, close: function(){ this.$super(); this.emit('close',this); this.release(); this.detachAllPoint(); }, emitPacket: function (p){ if(Packets.isTask(p)) this.dispatch(p); if(Packets.isReply(p)) this.dispatchReply(p); return p; }, }); var Plate = exports.Plate = stacks.Configurable.extends({ init: function(id) { var self = this; this.$super(); this.id = id; this.configs.add('id',id); this.points = Store.make('points',stacks.funcs.identity); this.proxy = stacks.Proxy(function(){ return true; }); this.channel = SelectedChannel.make(this.proxy.proxy); this.bindIn = stacks.funcs.bind(this.channel.bindIn,this.channel); this.bindOut = stacks.funcs.bind(this.channel.bindOut,this.channel); this.unbind = stacks.funcs.bind(this.channel.unbind,this.channel); this.pub('boot'); this.pub('shutdown'); var self = this; this.Reply = ReplyPackets.proxy(function(){ self.emitPacket(this); }); this.Task = TaskPackets.proxy(function(){ self.emitPacket(this); }); this.makeName = this.$bind(function(sn){ if(stacks.valids.not.String(sn)){ return; } return [this.id,sn].join('.'); }); this.$secure('dispatch',function(f){ if(!Packets.isPacket(f)) return; f.fromPlate(this); this.channel.emit(f); }); }, changeContract: function(n){ this.channel.changeContract(n); }, share: function(plate){ if(!Plate.isInstance(plate)) return; this.channel.bindIn(plate.channel); }, unshare: function(plate){ if(!Plate.isInstance(plate)) return; this.channel.unbind(plate.channel); }, plug: function(id,gid,fn){ return new Plug(id,gid,fn) }, tasks: function(){ return this.channel; }, hookproxy: function(c){ c.watch = stacks.funcs.bind(this.watch,this); c.emitWatch = stacks.funcs.bind(this.emitWatch,this); c.emitPacket = stacks.funcs.bind(this.emitPacket,this); c.makeName = stacks.funcs.bind(this.makeName,this); c.changeContract = stacks.funcs.bind(this.switchFilter,this); c.plugQueue = stacks.funcs.bind(this.plugQueue,this); c.tasks = stacks.funcs.bind(this.tasks,this); c.dispatch = stacks.funcs.bind(this.dispatch,this); c.bindIn = stacks.funcs.bind(this.bindIn,this); c.bindOut = stacks.funcs.bind(this.bindOut,this); c.unbind = stacks.funcs.bind(this.unbind,this); c.share = stacks.funcs.bind(this.share,this); c.unshare = stacks.funcs.bind(this.unshare,this); }, point: function(alias){ return this.points.Q(alias); }, attachPoint: function(fn,filter,alias,k){ if(alias && this.points.has(alias)) return; var pt = PlatePoint(fn,filter,k)(this); if(stacks.valids.notExists(alias)) stacks.Util.pusher(this.points.registry,pt); else this.points.add(alias,pt); return pt; }, detachPoint: function(item){ if(stacks.valids.isString(item) && this.points.has(item)){ var pt = this.points.Q(item); if(stacks.valids.isFunction(pt.close)) pt.close(); this.points.remove(item); return pt; }; if(stacks.valids.isObject(item)){ var self = this; this.points.each(function(f,i,o,fn){ if(f == item){ if(stacks.valids.isFunction(f.close)) f.close(); self.points.remove(i); return fn(true); } }); return item; } }, plugQueue: function(){ return new PlugQueue(this); }, watch: function(uuid,mp){ var channel = new channels.SelectedChannel(uuid,mp); this.channel.subscriber = this.channel.stream(channel); return channel; }, emitPacket: function (p){ this.dispatch(p); return p; }, emitWatch: function(t){ if(!Packets.isTask(t)) return; var mw = this.watch(t.uuid); mw.task = t; this.emit(t); return mw; }, }); var PlugQueue = exports.PlugQueue = stacks.Class({ init: function(pl){ stacks.Asserted(stacks.valids.isInstanceOf(pl,Plate),"argument must be an instance of plate"); this.plate = pl; this.typeList = {}; this.wq = stacks.WorkQueue(); this.active = stacks.Switch(); this.onDone = stacks.funcs.bind(this.wq.done.add,this.wq.done); this.onDoneOnce = stacks.funcs.bind(this.wq.done.addOnce,this.wq.done); this.offDone = stacks.funcs.bind(this.wq.done.add,this.wq.done); }, peek: function(fn){ this.wq.queue(fn); }, queue: function(name,uuid){ var self = this, guid = uuid || [name,stacks.Util.guid()].join(':'), chan = this.plate.watch(guid); chan.__guid = guid; if(this.typeList[guid]) return guid; this.typeList[guid] = ({ 'plug':name, 'uuid': guid, 'index': this.typeList.length, 'watch':chan, 'fn': stacks.funcs.bind(function(f){ return this.plate.dispatchMessage(name,guid,f); },self) }); return chan; }, unqueue: function(pl){ if(!this.has(pl)) return null; this.typeList[pl] = null; }, has: function(n){ return !!this.typeList[n]; }, __pack: function(){ var self = this,e,m; for(m in this.typeList){ e = this.typeList[m]; if(stacks.Valids.notExists(e)) return; var plug = e['plug'],fn = e['fn'], chan = e['watch']; chan.once(function(f){ self.emit(f); }); self.wq.queue(fn); }; self.wq.queue(function(f){ self.active.off(); }); }, emit: function(f){ if(!this.active.isOn()) this.__pack(); this.active.on(); return this.wq.emit(f); } }); var PlugPoint = exports.PlugPoint = function(fx,filter,picker){ if(!stacks.valids.isFunction(fx)) return; return stacks.MutateBy(function(fn,src,dests){ if(!Plug.isType(src)) return; var stm = stacks.Stream.make(); var contract = stacks.Contract(filter || "*",picker || MessagePicker); var handle = this.bind(function(r){ return fn.call(this,r,stm); }); var contractHandle = stacks.funcs.bind(function(f){ return this.interogate(f); },contract); contract.onPass(handle); src.replyChannel.on(contractHandle); stacks.enums.each(dests,function(e,i,o,ff){ if(!Plug.isType(e)) return ff(null); stm.stream(e.channel.packets); return ff(null); }); this.UUID = stacks.Util.guid(); this.Reply = ReplyPackets.proxy(function(){ stm.emit(n); }); this.Task = TaskPackets.proxy(function(){ stm.emit(n); }); this.srcReply = ReplyPackets.proxy(function(){ src.emit(this); }); this.srcTask = TaskPackets.proxy(function(){ src.emit(this); }); this.secure('close',function(){ src.replyChannel.off(contractHandle); return stm.close(); }); this.secureLock('plug',function(plug){ if(!Plug.isType(plug)) return; this.stream(plug.channel.packets); }); this.secureLock('plate',function(plate){ if(!Plate.isType(plate)) return; this.stream(plate.channel.packets); }); this.secure('stream',function(sm){ stm.stream(sm); }); this.secure('mux',function(fn){ stm.transformAsync(fn); }); this.secure('tap',function(fn,name){ if(stacks.valids.isString(name)) return stm.onEvent(name,fn); stm.on(fn); }); this.secure('untap',function(fn,name){ if(stacks.valids.isString(name)) return stm.onEvent(name,fn); stm.on(fn); }); // this.lock(); },fx); }; var PlatePoint = exports.PlatePoint = function(fx,filter,picker){ if(!stacks.valids.isFunction(fx)) return; return stacks.MutateBy(function(fn,src,dests){ if(!Plate.isType(src)) return; var stm = stacks.Stream.make(); var contract = stacks.Contract(filter || "*",picker || MessagePicker); var handle = this.bind(function(r){ return fn.call(this,r,stm); }); var contractHandle = stacks.funcs.bind(function(f){ return this.interogate(f); },contract); contract.onPass(handle); src.channel.on(contractHandle); stacks.enums.each(dests,function(e,i,o,ff){ if(!Plate.isType(e)) return ff(null); stm.stream(e.channel.packets); return ff(null); }); this.UUID = stacks.Util.guid(); this.Reply = ReplyPackets.proxy(function(){ stm.emit(n); }); this.Task = TaskPackets.proxy(function(){ stm.emit(n); }); this.srcReply = ReplyPackets.proxy(function(){ src.emit(this); }); this.srcTask = TaskPackets.proxy(function(){ src.emit(this); }); this.secure('mux',function(fn){ stm.transformAsync(fn); }); this.secure('close',function(){ src.channel.off(contractHandle); return stm.close(); }); this.secureLock('plate',function(plate){ if(!Plate.isType(plate)) return; this.stream(plate.channel.packets); }); this.secure('stream',function(sm){ stm.stream(sm); }); this.secure('tap',function(fn,name){ if(stacks.valids.isString(name)) return stm.onEvent(name,fn); stm.on(fn); }); this.secure('untap',function(fn,name){ if(stacks.valids.isString(name)) return stm.onEvent(name,fn); stm.on(fn); }); // this.lock(); },fx); }; var PlugStore = exports.PlugStore = Store.extends({ init: function(id){ this.$super(id,function(fn,sid,fx){ var rest = stacks.enums.rest(arguments); var plug = Plug.make.apply(Plug,rest); fn.call(plug); return plug; }); } }); var AdapterStore = exports.AdapterStore = Store.extends({ init: function(id){ this.$super(id,function(fn,sid,fx){ var rest = stacks.enums.rest(arguments); var ad = Adapters.apply(null,rest); fn.call(ad); return ad; }); } }); var Adapters = exports.Adapters = function(){ var dist = stacks.Distributors(); var mux = stacks.Middleware(_.funcs.bind(_.funcs.bind(dist.distribute,dist))); var fx = _.funcs.bind(mux.emit,mux); var apt = { mux: mux, rack: dist, }; stacks.funcs.selfReturn(apt,'from',function(){ stacks.Asserted(SelectedChannel.isType(chan),'argument must be a channel instance'); this.channel = chan; this.channel.on(fx); }); stacks.funcs.selfReturn(apt,'detach',function(){ this.channel.off(fx); this.channel = null; }); stacks.funcs.selfReturn(apt,'muxate',function(fx){ mux.add(fx); }); stacks.funcs.selfReturn(apt,'out',function(){ dist.add(fn); }); stacks.funcs.selfReturn(apt,'outOnce',function(){ dist.addOnce(fn); }); stacks.funcs.selfReturn(apt,'unOut',function(){ dist.remove(fn); }); return apt; }; var Mutators = exports.Mutators = function(fx){ var channels = [], freed = []; return { bind: function(chan){ if(this.has(chan)) return; chan.mutate(fx); var free = freed.pop(); if(free) channels[free] = chan; else channels.push(chan); }, unbind: function(chan){ if(!this.has(chan)) return; chan.unmutate(fx); var ind = this.index(chan); channels[ind] = null; freed.push(ind); }, unbindAll: function(exc){ stacks.enums.each(channels,function(e){ if(exc && e === exc) return; this.unbind(e); },null,this); }, has: function(chan){ return this.index(chan) !== -1; }, index: function(chan){ return channels.indexOf(chan); } } }; var RackSpace = exports.RackSpace = stacks.Configurable.extends({ init: function(id){ stacks.Asserted(stacks.valids.isString(id),'an "id" of string type is required '); this.$super(); this.id = id; this.racks = stacks.Storage.make('rackspace'); }, has: function(ns){ return this.racks.has(ns); }, ns: function(ns){ if(!this.has(ns)) return; return this.racks.get(ns); }, new: function(id){ stacks.Asserted(stacks.valids.isString(id),'first args must be a string'); if(this.has(id)) return this.ns(id); return this.racks.add(id,Rack.make(id)); }, rack: function(rack){ stacks.Asserted(Rack.isInstance(rack),'first args must be a Rack instance'); if(this.racks.has(rack.id)) return; return this.racks.add(rack.id,rack); }, unrack: function(rack){ if(Rack.isInstance(rack)){ return this.racks.remove(rack.id) } if(stack.valids.isString(rack)){ return this.racks.remove(id); } return; }, resource: function(addr){ stacks.Asserted(stacks.valids.isString(addr),'first argument must be a string with format: {rack}/{type}/{id}'); var rest = stacks.enums.rest(arguments); var paths = addr.split('/'); stacks.Asserted(paths.length >= 3,'address for type and id is incorrect {rack}/{type}/id!'); var tr = stacks.enums.rest(paths), rack = paths[0]; stacks.Asserted(tr.length >= 2,'sub-address for type and id is incorrect {type}/{id}!'); if(!this.has(rack)) return; var r = this.ns(rack), cr = r.resource.apply(r,tr.concat(rest)); if(cr) cr.track = rest; return cr; }, getResource: function(addr){ stacks.Asserted(stacks.valids.isString(addr),'first argument must be a string with format: {rack}/{type}/{id}'); var rest = stacks.enums.rest(arguments); var paths = addr.split('/'); stacks.Asserted(paths.length >= 3,'address for type and id is incorrect {rack}/{type}/id!'); var tr = stacks.enums.rest(paths), rack = paths[0]; stacks.Asserted(tr.length >= 2,'sub-address for type and id is incorrect {type}/{id}!'); if(!this.has(rack)) return; var r = this.ns(rack), cr = r.getResource.apply(r,tr.concat(rest)); if(cr) cr.track = rest; return cr; } }); var Rack = exports.Rack = stacks.Configurable.extends({ init: function(id){ stacks.Asserted(stacks.valids.isString(id),'an "id" of string type is required '); this.$super(); this.id = id; this.adapters = AdapterStore.make("plugs"); this.plugs = PlugStore.make("plugs"); this.plugPoints = Store.make("plugPoints",stacks.funcs.identity); this.platePoints = Store.make("platePoints",stacks.funcs.identity); this.mutators = Store.make("MutatorStore",stacks.funcs.identity); }, resource: function(){ var res, type = stacks.enums.first(arguments), name = stacks.enums.second(arguments), rest = stacks.enums.nthRest(arguments,2); var args = [name].concat(rest); switch(type){ case 'adapters': res = this.Adapter.apply(this,args); break; case 'plugs': res = this.Plug.apply(this,args); break; case 'plugpoint': res = this.PlugPoint.apply(this,args); break; case 'platepoint': res = this.PlatePoint.apply(this,args); break; case 'mutator': res = this.Mutator.apply(this,args); break; } return res; }, getResource: function(){ var res, type = stacks.enums.first(arguments), name = stacks.enums.second(arguments), rest = stacks.enums.nthRest(arguments,2); var args = [name].concat(rest); switch(type){ case 'adapters': res = this.getAdapter.apply(this,args); break; case 'plugs': res = this.getPlug.apply(this,args); break; case 'plugpoint': res = this.getPlugPoint.apply(this,args); break; case 'platepoint': res = this.getPlatePoint.apply(this,args); break; case 'mutator': res = this.getMutator.apply(this,args); break; } return res; }, hasPlug: function(id){ return this.plugs.has(id); }, hasMutator: function(id){ return this.mutators.has(id); }, hasAdapter: function(id){ return this.adapters.has(id); }, hasPlugPoint: function(id){ return this.plugPoints.has(id); }, hasPlatePoints: function(id){ return this.platePoints.has(id); }, Plug: function(id){ return this.plugs.Q.apply(this.plugs,arguments); }, Adapter: function(id){ return this.adapters.Q.apply(this.plugs,arguments); }, Mutator: function(id){ return this.mutators.Q.apply(this.mutators,arguments); }, PlugPoint: function(id){ return this.plugPoints.Q.apply(this.plugPoints,arguments); }, PlatePoint: function(id){ return this.platePoints.Q.apply(this.platePoints,arguments); }, getPlug: function(id){ return this.plugs.get.apply(this.plugs,arguments); }, getAdapter: function(id){ return this.adapters.get.apply(this.plugs,arguments); }, getMutator: function(id){ return this.mutators.get.apply(this.mutators,arguments); }, getPlugPoint: function(id){ return this.plugPoints.get.apply(this.plugPoints,arguments); }, getPlatePoint: function(id){ return this.platePoints.get.apply(this.platePoints,arguments); }, registerPlug: function(){ return this.plugs.register.apply(this.plugs,arguments); }, registerPlugPoint: function(){ return this.plugPoints.register.apply(this.plugPoints,arguments); }, registerPlatePoint: function(){ return this.platePoints.register.apply(this.platePoints,arguments); }, registerAdapter: function(){ return this.adapters.register.apply(this.adapters,arguments); }, registerMutator: function(id,fn){ return this.mutators.register(id,Mutators(fn)); }, }); var Network = exports.Network = stacks.Configurable.extends({ init: function(id,rs,fn){ if(stacks.valids.exists(rs) && stacks.valids.not.Function(rs)){ stacks.Asserted(RackSpace.isInstance(rs),'supply a rackspace instance as second argument'); } this.$super(); this.id = id; this.rs = rs; this.plate = Plate.make(id); this.plugs = stacks.Storage.make(); var self = this; this.Reply = ReplyPackets.proxy(function(){ self.plate.emitPacket(this); }); this.Task = TaskPackets.proxy(function(){ self.plate.emitPacket(this); }); this.plate.hookproxy(this); this.$rack(rs || fn); }, use: function(plug,gid){ stacks.Asserted(Plug.isInstance(plug),'first argument is required to be a plug instance'); if(!this.plugs.has(gid || plug.GUUID)){ this.plugs.add(gid || plug.GUUID,plug); plug.gid = gid; plug.attachPlate(this.plate); } return this; }, get: function(gid){ stacks.Asserted(stacks.valids.isString(gid),'argument is the unique alias for this plug'); return this.plugs.Q(gid); }, remove: function(gid){ if(!this.has(gid)) return; var pl = this.get(gid); pl.release(); return pl; }, destroy: function(gid){ var f = this.remove(gid); if(f) f.close(); return f; }, has: function(id){ return this.plugs.has(id); }, })