-
-
Save bs1180/32e5ab9ed68e9cf1254f to your computer and use it in GitHub Desktop.
Higher-level classes for synchronizing a nested Firebase ref, FirebaseEventEmitter and FirebaseImmutable. Work in progress.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Firebase = require "firebase" | |
Immutable = require "immutable" | |
{ EventEmitter } = require "events" | |
# Recursively listens to Firebase ref for modifications and emits "add", | |
# "change", and "remove" events with a path and value (except for "remove") | |
class FirebaseEventEmitter extends EventEmitter | |
constructor: (url) -> | |
EventEmitter.call(@) | |
@root = new Firebase(url) | |
@_recursiveSubscribe(@root, []) | |
_recursiveSubscribe: (ref, path) => | |
ref.on "child_added", (snapshot, prevChildName) => | |
if typeof snapshot.val() is "object" | |
childPath = path.concat(snapshot.key()) | |
@emit("add", childPath, snapshot.val()) | |
@_recursiveSubscribe(ref.child(snapshot.key()), childPath) | |
ref.on "child_changed", (snapshot, prevChildName) => | |
if typeof snapshot.val() isnt "object" | |
childPath = path.concat(snapshot.key()) | |
@emit("change", childPath, snapshot.val()) | |
ref.on "child_moved", (snapshot, prevChildName) => | |
console.warn "children ordering not yet implemented" | |
ref.on "child_removed", (snapshot) => | |
childPath = path.concat(snapshot.key()) | |
@emit("remove", childPath) | |
# Recursively synchronizes a Firebase ref with Immutable.js objects, emitting "change" | |
# events when it is changed. Could facilitate efficient updates of React components, etc. | |
class FirebaseImmutable extends EventEmitter | |
BATCH_PERIOD: 10 | |
constructor: (url) -> | |
@events = new FirebaseEventEmitter(url) | |
@data = Immutable.Map() | |
@events.on "add", (path, value) => | |
@_setData(@data.setIn(path, Immutable.Map(value))) | |
@events.on "change", (path, value) => | |
@_setData(@data.setIn(path, value)) | |
@events.on "remove", (path) => | |
@_setData(@data.removeIn(path)) | |
_setData: (data) -> | |
if data isnt @data | |
oldData = @data | |
@data = data | |
if @BATCH_PERIOD | |
unless @_batchTimeout | |
@_batchTimeout = setTimeout => | |
@emit "change", @data, oldData | |
delete @_batchTimeout | |
, @BATCH_PERIOD | |
else | |
@emit "change", @data, oldData | |
# Prints a nice diff of two Immutable.js Maps, recursively | |
colors = require 'colors' | |
diffImmutable = (a, b, name="", pad="") -> | |
aStr = if typeof a is "object" then "[Object]" else a | |
bStr = if typeof b is "object" then "[Object]" else b | |
if a is b | |
console.log "#{pad}#{name} (#{aStr})" | |
else | |
if a is undefined and b isnt undefined | |
console.log "#{pad}#{name.green} (#{aStr} -> #{bStr})" | |
else if a isnt undefined and b is undefined | |
console.log "#{pad}#{name.red} (#{aStr} -> #{bStr})" | |
else | |
console.log "#{pad}#{name.yellow} (#{aStr} -> #{bStr})" | |
keys = {} | |
a.forEach((v,k) -> keys[k] = true) if a instanceof Immutable.Map | |
b.forEach((v,k) -> keys[k] = true) if b instanceof Immutable.Map | |
for key of keys | |
diffImmutable(a?.get?(key), b?.get?(key), key, pad + " ") | |
# Print a diff when any changes occur | |
if require.main is module | |
fire = new FirebaseImmutable(process.argv[2]) | |
fire.on "change", (newData, oldData) -> | |
diffImmutable(oldData, newData, "graph") | |
module.exports = FirebaseImmutable |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment