Permalink
Browse files

Allow wiring function to subscribe/unsubscribe cells to trigger execu…

…tion
  • Loading branch information...
vic committed Aug 15, 2012
1 parent fe16d83 commit 8c4da6243a7312adac85e301f87e3f6e062d25df
Showing with 99 additions and 36 deletions.
  1. +1 −2 examples/clock.js
  2. +48 −30 lib/NoR.js
  3. +50 −4 test/NoR_test.js
View
@@ -20,8 +20,7 @@ var counter = NoR(function(clock, count){
}, function(){ }, function(){
// mark the count cell so it doent // mark the count cell so it doent
// trigger counter execution again // trigger counter execution again
// mark it as an outputOnly cell. this.count.unsubscribe(this)
this.count.isOutput = true
}) })
var c = clock(true, false, 600) var c = clock(true, false, 600)
View
@@ -33,27 +33,51 @@
} }
var addSubscriber = function(subscriber) { var addSubscriber = function(subscriber) {
if(subscriber.constructor === Gate) {
subscriber = subscriber.execute
}
if(subscriber === cell) { return } if(subscriber === cell) { return }
var i = subscribers.indexOf(subscriber) var i = subscribers.indexOf(subscriber)
if(i < 0 ) { subscribers.push(subscriber) } if(i < 0 ) { subscribers.push(subscriber) }
} }
var removeSubscriber = function(subscriber) { var removeSubscriber = function(subscriber) {
if(subscriber.constructor === Gate) {
subscriber = subscriber.execute
}
var i = subscribers.indexOf(subscriber) var i = subscribers.indexOf(subscriber)
if(i > -1) { subscribers[i] = null } if(i > -1) { subscribers[i] = null }
// TODO: clean list when too big // TODO: clean list when too big
} }
cell.constructor = Cell var clearSubscribers = function(){
cell.cellName = name subscribers = []
cell.toString = function() { return '{'+name+':'+value+'}' } }
cell.subscribe = addSubscriber
cell.bind = function(other) { var subscriberCount = function(){
subscribers.length
}
var bind = function(other) {
cell.subscribe(other) cell.subscribe(other)
other.subscribe(cell) other.subscribe(cell)
} }
cell.clearSubscribers = function() { subscribers = [] }
cell.unSubscribe = removeSubscriber var unbind = function(other){
cell.unsubscribe(other)
other.unsubscribe(cell)
}
Object.defineProperty(cell, 'constructor', {value: Cell})
Object.defineProperty(cell, 'cellName', {value: name})
Object.defineProperty(cell, 'subscribe', {value: addSubscriber})
Object.defineProperty(cell, 'unsubscribe', {value: removeSubscriber})
Object.defineProperty(cell, 'bind', {value: bind})
Object.defineProperty(cell, 'unbind', {value: unbind})
Object.defineProperty(cell, 'subscriberCount', {get: subscriberCount})
cell.toString = function() { return '{'+name+':'+value+'}' }
cell.shouldPropagate = function(){ cell.shouldPropagate = function(){
return value !== arguments[0] || arguments.length > 1 return value !== arguments[0] || arguments.length > 1
} }
@@ -65,8 +89,6 @@
var rest = Array.prototype.slice.apply(arguments, [4]) var rest = Array.prototype.slice.apply(arguments, [4])
, impl = typeof impl === 'function' ? impl : function(){} , impl = typeof impl === 'function' ? impl : function(){}
, names = {} , names = {}
, cells = argCells(impl, names)
, self = names['self'] = cellWrap(Cell('self', _self))
, timeout , timeout
, execute = function() { , execute = function() {
if(timeout) { clearTimeout(timeout) } if(timeout) { clearTimeout(timeout) }
@@ -94,10 +116,16 @@
}]) }])
return arguments.callee return arguments.callee
} }
, self = names['self'] = Cell('self', _self)
, cells = argCells(impl, names)
if(!create){ return clone } if(!create){ return clone }
invoke.clone = clone
invoke.self = self Object.defineProperty(invoke, 'constructor', {value: Gate})
invoke.length = cells.length Object.defineProperty(invoke, 'execute', {value: execute})
Object.defineProperty(invoke, 'clone', {value: clone})
Object.defineProperty(invoke, 'self', {value: self})
invoke.toString = function(){ invoke.toString = function(){
var buff = [] var buff = []
cells.forEach(function(cell){ cells.forEach(function(cell){
@@ -107,7 +135,10 @@
} }
cells.forEach(function(cell, i){ cells.forEach(function(cell, i){
invoke[cell.cellName] = invoke[i] = cell
Object.defineProperty(invoke, cell.cellName, {value: cell})
Object.defineProperty(invoke, i, {value: cell})
if( i < rest.length ) { if( i < rest.length ) {
if(typeof rest[i] === 'function' && rest[i].constructor === Cell) { if(typeof rest[i] === 'function' && rest[i].constructor === Cell) {
cell( rest[i]() ) cell( rest[i]() )
@@ -118,6 +149,9 @@
} }
}) })
self.subscribe(execute)
cells.forEach(function(cell){cell.subscribe(execute)})
if(wire){ if(wire){
var wireCells = argCells(wire, names) var wireCells = argCells(wire, names)
wireCells.forEach(function(cell, i){ wireCells.forEach(function(cell, i){
@@ -134,35 +168,19 @@
wire.apply(invoke, wireCells) wire.apply(invoke, wireCells)
} }
[self].concat(cells).forEach(function(cell){
if(!cell.isOutput) {
cell.subscribe(execute)
}
})
return invoke return invoke
} }
var ARGS_RE = /^function\s+\(([^\)]+)\)/, SEP_RE = /,?\s+/ var ARGS_RE = /^function\s+\(([^\)]+)\)/, SEP_RE = /,?\s+/
var cellWrap = function(cell){
return cell // TODO wrap
var wrap = function(){
var self = this, args = arguments
return cell.apply(this, arguments)
}
wrap.cellName = cell.cellName
return wrap
}
var argCells = function(impl, store){ var argCells = function(impl, store){
var names = impl.toString().match(ARGS_RE) var names = impl.toString().match(ARGS_RE)
, names = names && names[1].split(SEP_RE) || [] , names = names && names[1].split(SEP_RE) || []
, cells = [] , cells = []
, store = store || {} , store = store || {}
names.forEach(function(name, i){ names.forEach(function(name, i){
if(! store[name]) { if(! store[name]) {
store[name] = cellWrap(Cell(name)) store[name] = Cell(name)
} }
cells[i] = store[name] cells[i] = store[name]
}) })
View
@@ -58,7 +58,7 @@ describe("NoR", function(){
expect(x.length).to.equal(0) expect(x.length).to.equal(0)
}) })
it("is triggered when any of the gate arguments (cells) change", function(){ it("is triggered when any of the gate arguments (cells) change", function(done){
var x = 0 var x = 0
var n = new NoR(function(a, b, c){ var n = new NoR(function(a, b, c){
x += 1 x += 1
@@ -76,6 +76,8 @@ describe("NoR", function(){
n(1, 2, 3) n(1, 2, 3)
setTimeout(function(){ setTimeout(function(){
expect(x).to.equal(3) expect(x).to.equal(3)
done()
}, 1) }, 1)
}, 1) }, 1)
@@ -84,7 +86,7 @@ describe("NoR", function(){
describe("called as function", function(){ describe("called as function", function(){
it("sets its exported gates to the given values", function(){ it("sets its exported gates to the given values", function(done){
var x = 0 var x = 0
var n = new NoR(function(a, b, c){ var n = new NoR(function(a, b, c){
x = a() + b() + c() x = a() + b() + c()
@@ -93,6 +95,8 @@ describe("NoR", function(){
n(1, 2, 3) n(1, 2, 3)
setTimeout(function(){ setTimeout(function(){
expect(x).to.equal(6) expect(x).to.equal(6)
done()
}, 1) }, 1)
}) })
@@ -128,13 +132,15 @@ describe("NoR", function(){
}) })
it("is executed having this set to the gate object", function(){ it("is executed having this set to the gate object", function(done){
var x var x
var n = new NoR(function(a){ x = this }) var n = new NoR(function(a){ x = this })
expect(x).to.be.undefined expect(x).to.be.undefined
n.a(99) n.a(99)
setTimeout(function(){ setTimeout(function(){
expect(x).to.equal(n) expect(x).to.equal(n)
done()
}, 1) }, 1)
}) })
@@ -150,7 +156,7 @@ describe("NoR", function(){
expect(n.self()).to.equal(99) expect(n.self()).to.equal(99)
}) })
it("on change triggers gate execution", function(){ it("on change triggers gate execution", function(done){
var x var x
var n = new NoR(function(){ var n = new NoR(function(){
x = this.self() x = this.self()
@@ -160,6 +166,8 @@ describe("NoR", function(){
n.self(88) n.self(88)
setTimeout(function(){ setTimeout(function(){
expect(x).to.equal(88) expect(x).to.equal(88)
done()
}, 1) }, 1)
}) })
@@ -195,6 +203,44 @@ describe("NoR", function(){
expect(x).to.equal(10) expect(x).to.equal(10)
}) })
it("can bind a cell to execute the gate on change", function(done){
var x
var n = new NoR(function(){
x = this.b()
}, function(b) {
b.subscribe(this)
this.b = b
})
n.b(99)
setTimeout(function(){
expect(x).to.equal(99)
done()
}, 2)
})
it("can set a cell not to execute the gate on change", function(done){
var x = 0, y
var n = new NoR(function(a, b){
x += 1
y = a()
}, function(b) {
b.unsubscribe(this)
})
n.a(99)
setTimeout(function(){
expect(n.a()).to.equal(99)
expect(x).to.equal(1)
expect(y).to.equal(99)
n.b(10)
setTimeout(function(){
expect(x).to.equal(1)
expect(y).to.equal(99)
done()
}, 1)
}, 1)
})
}) })
}) })

0 comments on commit 8c4da62

Please sign in to comment.