Permalink
Browse files

Got filterable array working.

  • Loading branch information...
1 parent f41fd51 commit bdd33b559b6b50809f5a8e81e19c60439804594f @airportyh committed Oct 1, 2013
Showing with 460 additions and 120 deletions.
  1. +2 −1 .gitignore
  2. +133 −0 basic_tests.js
  3. +106 −0 filtered.js
  4. +145 −0 filtered_tests.js
  5. +9 −0 index.js
  6. +2 −1 package.json
  7. +43 −0 tap_testem.js
  8. +15 −0 testem.yml
  9. +3 −0 tests.html
  10. +2 −118 tests.js
View
@@ -1 +1,2 @@
-/node_modules
+/node_modules
+/__tests.js
View
@@ -0,0 +1,133 @@
+var test = require('tape')
+var Array = require('./index')
+var spy = require('ispy')
+var E = require('emmitt')
+
+test('create', function(t){
+ t.equal(new Array().size(), 0)
+ t.equal(new Array(1).size(), 1)
+ t.end()
+})
+
+test('call constructor w/o new dont matter', function(t){
+ t.equal(Array().size(), 0)
+ t.equal(Array(1).size(), 1)
+ t.assert(Array() instanceof Array, 'is instanceof MantaRay')
+ t.end()
+})
+
+test('access', function(t){
+ var ray = new Array(1)
+ t.equal(ray.get(0), 1)
+ t.end()
+})
+
+test('add', function(t){
+ var ray = new Array()
+ ray.add('hello')
+ t.equal(ray.size(), 1)
+ ray.add('world')
+ t.equal(ray.size(), 2)
+ t.equal(ray.get(0), 'hello')
+ t.equal(ray.get(1), 'world')
+ t.end()
+})
+
+test('insert', function(t){
+ var ray = new Array(1, 3)
+ ray.insert(1, 2)
+ t.equal(ray.size(), 3)
+ t.equal(ray.get(0), 1)
+ t.equal(ray.get(1), 2)
+ t.equal(ray.get(2), 3)
+ t.end()
+})
+
+test('remove', function(t){
+ var ray = new Array(1, 2)
+ ray.remove(1)
+ t.equal(ray.get(0), 2)
+ t.equal(ray.size(), 1)
+ t.end()
+})
+
+test('contains', function(t){
+ var ray = new Array(1, 2)
+ t.assert(ray.contains(1))
+ t.assert(!ray.contains(3))
+ t.end()
+})
+
+test('clear', function(t){
+ var ray = new Array(1, 2, 3)
+ ray.clear()
+ t.equal(ray.size(), 0)
+ t.end()
+})
+
+test('indexOf', function(t){
+ var ray = new Array(1, 2, 3)
+ t.equal(ray.indexOf(2), 1)
+ t.end()
+})
+
+test('toArray', function(t){
+ var ray = new Array(1, 2, 3)
+ t.deepEqual(ray.toArray(), [1, 2, 3])
+ t.end()
+})
+
+test('fires on add', function(t){
+ var ray = new Array(1)
+ var onInsert = spy()
+ E.on(ray, 'insert', onInsert)
+ ray.add(2)
+ t.assert(onInsert.called, 'should have called')
+ t.deepEqual(onInsert.lastCall.args, [1, 2])
+ t.end()
+})
+
+test('fires on insert', function(t){
+ var ray = new Array(1, 2)
+ var onInsert = spy()
+ E.on(ray, 'insert', onInsert)
+ ray.insert(1, 3)
+ t.assert(onInsert.called, 'should have called')
+ t.deepEqual(onInsert.lastCall.args, [1, 3])
+ t.end()
+})
+
+test('fires on remove', function(t){
+ var ray = new Array(1, 2)
+ var onRemove = spy()
+ E.on(ray, 'remove', onRemove)
+ ray.remove(1)
+ t.assert(onRemove.called, 'should have called')
+ t.deepEqual(onRemove.lastCall.args, [0, 1])
+ t.end()
+})
+
+test('fires remove on clear', function(t){
+ var ray = new Array(1, 2)
+ var onRemove = spy()
+ E.on(ray, 'remove', onRemove)
+ ray.clear()
+ t.assert(onRemove.called, 'should have called')
+ t.deepEqual(onRemove.lastCall.args, [[0, 1], [1, 2]])
+ t.end()
+})
+
+
+// ES5 Array Methods
+
+test('forEach', function(t){
+ var ray = new Array(1, 2)
+ var fun = spy()
+ ray.forEach(fun)
+ t.equal(fun.callCount, 2)
+ t.deepEqual(fun.calls[0].args, [1,0,[1,2]])
+ t.deepEqual(fun.calls[1].args, [2,1,[1,2]])
+ t.end()
+})
+
+// TODO: the other ones
View
@@ -0,0 +1,106 @@
+var E = require('emmitt')
+module.exports = FilteredArray
+
+function FilteredArray(arr, fun){
+ var self = this
+ this.fun = fun
+ this.source = arr
+ this.indices = []
+ this.$onItemChange = function(){ self._onItemChange(this) }
+ arr.forEach(function(item, idx){
+ if (fun(item)){
+ self.indices.push(idx)
+ }
+ if (typeof item !== 'object') return
+ E.on(item, 'change', self.$onItemChange)
+ })
+ this.$onInsert = function(idx, item){
+ self._onInsert(idx, item)
+ }
+ E.on(this.source, 'insert', this.$onInsert)
+ this.$onRemove = function(idx, item){
+ self._onRemove(idx, item)
+ }
+ E.on(this.source, 'remove', this.$onRemove)
+}
+
+FilteredArray.prototype = {
+ size: function(){
+ return this.indices.length
+ },
+ get: function(i){
+ return this.source.get(this.indices[i])
+ },
+ indexOf: function(obj){
+ for (var i = 0; i < this.indices.length; i++){
+ if (this.get(i) === obj) return i
+ }
+ return -1
+ },
+ contains: function(obj){
+ if (!this.fun(obj)) return false
+ return this.indexOf(obj) !== -1
+ },
+ add: function(){
+ throw new Error('Cannot add to a filtered array')
+ },
+ remove: function(){
+ throw new Error('Cannot removed from a filtered array')
+ },
+ insert: function(){
+ throw new Error('Cannot insert into a filtered array')
+ },
+ clear: function(){
+ throw new Error('Cannot clean a filtered array')
+ },
+ _onInsert: function(idx, item){
+ if (this.fun(item)){
+ this._insert(idx, item)
+ }
+ },
+ _onRemove: function(idx, item){
+ if (this.fun(item)){
+ this._remove(idx, item)
+ }
+ },
+ _onItemChange: function(item){
+ var idx = this.indexOf(item)
+ var contains = idx !== -1
+ var want = this.fun(item)
+ if (want && !contains){
+ this._insert(this.source.indexOf(item), item)
+ }else if (!want && contains){
+ this._remove(idx, item)
+ }
+ },
+ _insert: function(idx, item){
+ this.indices = this.indices.map(function(i){
+ if (i >= idx) return i + 1
+ return i
+ })
+ this.indices.push(idx)
+ this.indices.sort(function(one, other){
+ return one - other
+ })
+ },
+ _remove: function(idx, item){
+ var ii = this.indices.indexOf(idx)
+ if (ii !== -1){
+ this.indices.splice(ii, 1)
+ }
+ E.off(item, 'change', this.$onItemChange)
+ },
+ toArray: function(){
+ var source = this.source
+ return this.indices.map(function(idx){
+ return source.get(idx)
+ })
+ },
+ destroy: function(){
+ this.source.forEach(function(item){
+ E.off(item, 'change', this.$onItemChange)
+ }, this)
+ E.off(this.source, 'insert', this.$onInsert)
+ E.off(this.source, 'remove', this.$onRemove)
+ }
+}
View
@@ -0,0 +1,145 @@
+var FilteredArray = require('./filtered')
+var test = require('tape')
+var Array = require('./index')
+var set = require('setter')
+
+test('basic', function(t){
+ var arr = new Array(1, 2, 3)
+ var filtered = new FilteredArray(arr, function(n){
+ return n % 2 === 1
+ })
+ t.equal(filtered.size(), 2)
+ t.equal(filtered.get(0), 1)
+ t.equal(filtered.get(1), 3)
+ t.end()
+})
+
+test('indexOf', function(t){
+ var arr = new Array(1, 2, 3)
+ var filtered = new FilteredArray(arr, function(n){
+ return n % 2 === 1
+ })
+ t.equal(filtered.indexOf(2), -1)
+ t.equal(filtered.indexOf(1), 0)
+ t.equal(filtered.indexOf(3), 1)
+ t.end()
+})
+
+test('contains', function(t){
+ var arr = new Array(1, 2, 3)
+ var filtered = new FilteredArray(arr, function(n){
+ return n % 2 === 1
+ })
+ t.assert(filtered.contains(1))
+ t.assert(!filtered.contains(2))
+ t.assert(filtered.contains(3))
+ t.end()
+})
+
+test('source changes, so does this', function(t){
+ var source = new Array(1, 2, 3)
+ var filtered = new FilteredArray(source, function(n){
+ return n % 2 === 1
+ })
+
+ source.add(5)
+ t.equal(filtered.size(), 3)
+ t.deepEqual(filtered.toArray(), [1, 3, 5])
+ t.end()
+})
+
+test('updates when insert into source', function(t){
+ var source = new Array(1, 2, 3)
+ var filtered = new FilteredArray(source, function(n){
+ return n % 2 === 1
+ })
+
+ source.insert(2, 7)
+ t.deepEqual(source.toArray(), [1, 2, 7, 3])
+ t.deepEqual(filtered.toArray(), [1, 7, 3])
+ t.end()
+})
+
+test('updates when removed from source', function(t){
+ var source = new Array(1, 2, 3)
+ var filtered = new FilteredArray(source, function(n){
+ return n % 2 === 1
+ })
+ source.remove(2)
+ t.equal(filtered.size(), 2)
+ source.remove(1)
+ t.equal(filtered.size(), 1)
+ t.end()
+})
+
+test('dont allow add, insert, remove or clear', function(t){
+ var source = new Array(1, 2, 3)
+ var filtered = new FilteredArray(source, function(n){
+ return n % 2 === 1
+ })
+ t.throws(function(){
+ filtered.add(4)
+ })
+ t.throws(function(){
+ filtered.remove(4)
+ })
+ t.throws(function(){
+ filtered.insert(4, 1)
+ })
+ t.throws(function(){
+ filtered.clear()
+ })
+ t.end()
+})
+
+test('items change, also changes', function(t){
+ var bob = { name: 'bob', age: 1 }
+ var mary = { name: 'mary', age: 2 }
+ var source = new Array(bob, mary)
+ var filtered = new FilteredArray(source, function(child){
+ return child.age % 2 === 1
+ })
+ t.equal(filtered.size(), 1)
+ set(mary, 'age', 3)
+ t.equal(filtered.size(), 2)
+ set(bob, 'age', 2)
+ t.equal(filtered.size(), 1)
+ t.end()
+})
+
+test('clean up', function(t){
+ var bob = { name: 'bob', age: 1 }
+ var mary = { name: 'mary', age: 2 }
+ var source = new Array(bob, mary)
+ var filtered = new FilteredArray(source, function(child){
+ return child.age % 2 === 1
+ })
+ filtered.destroy()
+ t.equal(numListeners(source), 0)
+ t.equal(numListeners(bob), 0)
+ t.equal(numListeners(mary), 0)
+ t.end()
+})
+
+test('clean up on remove', function(t){
+ var bob = { name: 'bob', age: 1 }
+ var mary = { name: 'mary', age: 2 }
+ var source = new Array(bob, mary)
+ var filtered = new FilteredArray(source, function(child){
+ return child.age % 2 === 1
+ })
+ source.remove(bob)
+ t.equal(numListeners(bob), 0)
+ t.end()
+})
+
+function numListeners(obj){
+ var handlers = obj.__emmittdata__.handlers
+ var count = 0
+ for (var key in handlers){
+ count += handlers[key] ? handlers[key].length : 0
+ }
+ return count
+}
+
+
Oops, something went wrong.

0 comments on commit bdd33b5

Please sign in to comment.