To use this with immutable data structures such as https://github.com/facebook/immutable-js
"Almost all of the methods on Array will be found in similar form on Immutable.Vector, those of Map found on Immutable.Map, and those of Set found on Immutable.Set, including sequence operations like forEach and map."
function asImmutable(data) {
return Immutable.fromJS(data);
}
function dataWrap(data) {
return asImmutable(data);
}
function valueOf(snap) {
return dataWrap(snap.val());
}
syncChanges: function(list, ref) {
ref.on('child_added', function _add(snap, prevChild) {
var data = valueOf(snap);
data.$id = snap.name(); // assumes data is always an object
var pos = positionAfter(list, prevChild);
list.splice(pos, 0, data);
});
ref.on('child_removed', function _remove(snap) {
var i = positionFor(list, snap.name());
if( i > -1 ) {
list.splice(i, 1);
}
});
ref.on('child_changed', function _change(snap) {
var i = positionFor(list, snap.name());
if( i > -1 ) {
list[i] = valueOf(snap);
list[i].$id = snap.name(); // assumes data is always an object
}
});
ref.on('child_moved', function _move(snap, prevChild) {
var curPos = positionFor(list, snap.name());
if( curPos > -1 ) {
var data = list.splice(curPos, 1)[0];
var newPos = positionAfter(list, prevChild);
list.splice(newPos, 0, data);
}
});
The initial data must also be made into immutable data structure
getSynchronizedArray: function(firebaseRef) {
var list = valueOf([]);
syncChanges(list, firebaseRef);
// Todo is class name and is f.ex used to find Validator
wrapLocalCrudOps(list, firebaseRef, new SyncStore('Todo');
return list;
}
Finally, make the wrapCruds more "intelligent"
Validator: function(options) {
this.repo = options.repo || {}
return {
findFor: function(className) {
// find validator in repository using className
return this.repo[className]
},
addFor: function(className, validator) {
this.repo[className] = validator;
}
removeFor: function(className) {
this.repo.delete(className);
}
repo: this.repo
}
}
SyncStore: function(options) {
var options = options || {};
var validatorFor = function(className) {
return Validator.findFor(className);
}
this.className = options.className || 'Base';
this.validator = options.validator || validatorFor(this.className);
this.updateData = function(positionRef, operation, data) {
var isValid = function(data) {
if (!this.validator) { return true; }
return this.validator.validate(data);
}
if (isValid(data)) {
console.log(operation, data);
return positionRef.call(positionRef[operation], data);
}
}
}
wrapLocalCrudOps: function(list, firebaseRef, syncStore) {
// we can hack directly on the array to provide some convenience methods
list.$add = function(data) {
return syncStore.updateData(firebaseRef, 'push', data);
};
list.$remove = function(key) {
firebaseRef.child(key).remove();
};
list.$removeList = function(keys) {
keys.forEach(function(key) {
keys.$remove(key);
});
};
list.$set = function(key, newData) {
// make sure we don't accidentally push our $id prop
if( newData.hasOwnProperty('$id') ) { delete newData.$id; }
var positionRef = firebaseRef.child(key)
return syncStore.updateData(positionRef, 'set', newData);
};
...
}