Skip to content

Instantly share code, notes, and snippets.

@bs1180
Forked from tlrobinson/firebase-utils.coffee
Last active August 29, 2015 14:27
Show Gist options
  • Save bs1180/32e5ab9ed68e9cf1254f to your computer and use it in GitHub Desktop.
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.
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