PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/app/scripts/backbone-pubnub.coffee

https://github.com/colincarr/pubnub-backbone
CoffeeScript | 261 lines | 166 code | 50 blank | 45 comment | 14 complexity | 5e9aeb230edf31f9d5412896ab328f05 MD5 | raw file
  1. ################
  2. # Backbone.PubNub
  3. # -----------------
  4. # The Backbone.PubNub object is used as a plugin to make collections real-time
  5. ################
  6. Backbone.PubNub = (ref, name) ->
  7. @name = name
  8. @ref = ref
  9. @uuid = @ref.uuid()
  10. @channel = "backbone-#{@name}"
  11. @records = []
  12. # Subscribe to publish methods and react to what happens on the other side
  13. @ref.subscribe
  14. channel: @channel
  15. callback: (message) =>
  16. # We don't want to cause a loop by listening to our own events
  17. unless message.uuid is @uuid
  18. switch message.method
  19. when "create" then @create message.model
  20. when "update" then @update message.model
  21. when "delete" then @destroy message.model
  22. _.extend Backbone.PubNub.prototype,
  23. # Publishes a change to the pubnub channel
  24. publish: (method, model, options) ->
  25. message =
  26. method: method
  27. model: model
  28. options: options
  29. uuid: @uuid
  30. @ref.publish
  31. channel: @channel
  32. message: message
  33. # Finds a model in the local array
  34. read: (model) ->
  35. unless model.id?
  36. @find model.id
  37. else
  38. @findAll()
  39. # Find an individual record
  40. find: (id) ->
  41. _.find @records, (record) ->
  42. record.id is id
  43. # Return all records from the local array
  44. findAll: () ->
  45. @records
  46. # Create a new record and publish it to other instances
  47. create: (model) ->
  48. unless model.id?
  49. model.id = @ref.uuid()
  50. model.set model.idAttribute, model.id
  51. @records.push model
  52. @publish "create", model
  53. model
  54. # When a record updates, publish the changes to other instances
  55. update: (model) ->
  56. oldModel = @find model.id
  57. @records[@records.indexOf(oldModel)] = model
  58. @publish "update", model
  59. model
  60. # When a record is removed, publish the changes to other instances
  61. destroy: (model) ->
  62. if model.isNew()
  63. return false
  64. @records = _.reject @records, (record) ->
  65. record.id is model.id
  66. @publish "delete", model
  67. model
  68. # Custom sync method for using the plugin version
  69. Backbone.PubNub.sync = (method, model, options) ->
  70. pubnub = model.pubnub ? model.collection.pubnub
  71. console.log method, model, options, pubnub
  72. try
  73. switch method
  74. when "read" then resp = pubnub.read model
  75. when "create" then resp = pubnub.create model
  76. when "update" then resp = pubnub.update model
  77. when "delete" then resp = pubnub.destroy model
  78. catch error
  79. errorMessage = error.message
  80. console.log "Could not sync: #{errorMessage}"
  81. # Save the old sync method for later use
  82. _sync = Backbone.sync
  83. # Override the sync method to use pubnub if it is available
  84. Backbone.sync = (method, model, options) ->
  85. syncMethod = _sync
  86. if model.pubnub or (model.collection and model.collection.pubnub)
  87. syncMethod = Backbone.PubNub.sync
  88. syncMethod.apply this, [method, model, options]
  89. ################
  90. # Backbone.PubNub.Collection
  91. # ----------------------------
  92. # A real-time implementation of a Backbone Collection.
  93. # This works by publishing a transaction of all methods (create, update, delete)
  94. # to other instances.
  95. ################
  96. Backbone.PubNub.Collection = Backbone.Collection.extend
  97. # Publishes a change to the pubnub channel
  98. publish: (method, model, options) ->
  99. message =
  100. method: method
  101. model: model
  102. options: options
  103. uuid: @uuid
  104. @pubnub.publish
  105. channel: @channel
  106. message: message
  107. constructor: (models, options) ->
  108. Backbone.Collection.apply this, arguments
  109. if options and options.pubnub
  110. @pubnub = options.pubnub
  111. @uuid = @pubnub.uuid()
  112. @channel = "backbone-collection-#{@name}"
  113. # Subscribe to this colleciton's channel and listen for changes to
  114. # other client's collections
  115. @pubnub.subscribe
  116. channel: @channel
  117. callback: (message) =>
  118. @off 'change', updateModel, this
  119. unless message.uuid is @uuid
  120. switch message.method
  121. when "create" then @_onAdded message.model, message.options
  122. when "update" then @_onChanged message.model, message.options
  123. when "delete" then @_onRemoved message.model, message.options
  124. @on 'change', updateModel, this
  125. # When the collection changes we post updates to everyone else
  126. updateModel = (model) ->
  127. @publish "update", model
  128. @on 'change', updateModel, this
  129. # Called when another client adds a record
  130. _onAdded: (model, options) ->
  131. Backbone.Collection.prototype.add.apply this, [model, options]
  132. # Called when another client changes a record
  133. _onChanged: (model, options) ->
  134. unless not model.id
  135. record = _.find @models, (record) ->
  136. record.id is model.id
  137. unless record?
  138. console.log "Could not find model with ID: #{model.id}"
  139. return false
  140. # Since there is no native update record we have to find the differences manually
  141. diff = _.difference _.keys(record.attributes), _.keys(model)
  142. _.each diff, (key) ->
  143. record.unset key
  144. record.set model, options
  145. # Called when another client removes a record
  146. _onRemoved: (model, options) ->
  147. Backbone.Collection.prototype.remove.apply this, [model, options]
  148. add: (models, options) ->
  149. models = if _.isArray(models) then models.slice() else [models]
  150. for model in models
  151. # We need to manually create an ID for each record, otherwise
  152. # Backbone will not give us change or remove events
  153. unless model.id?
  154. model.id = @pubnub.uuid()
  155. @publish "create", model, options
  156. Backbone.Collection.prototype.add.apply this, arguments
  157. remove: (models, options) ->
  158. models = if _.isArray(models) then models.slice() else [models]
  159. for model in models
  160. @publish "delete", model, options
  161. Backbone.Collection.prototype.remove.apply this, arguments
  162. ################
  163. # Backbone.PubNub.Model
  164. # ----------------------------
  165. # A real-time implementation of a Backbone Model.
  166. # This will publish all change events to other instances of the same model. It
  167. # will also remove itself if another instance is removed
  168. ################
  169. Backbone.PubNub.Model = Backbone.Model.extend
  170. # Publishes a change to the pubnub channel
  171. publish: (method, model, options) ->
  172. message =
  173. method: method
  174. model: model
  175. options: options
  176. uuid: @uuid
  177. @pubnub.publish
  178. channel: @channel
  179. message: message
  180. constructor: (model, options) ->
  181. Backbone.Model.apply this, arguments
  182. if options and options.pubnub and options.name
  183. @pubnub = options.pubnub
  184. @name = options.name
  185. @uuid = @pubnub.uuid()
  186. @channel = "backbone-model-#{@name}"
  187. @on 'change', @_onChange, this
  188. # Subscribe to updates from other instances of this model
  189. @pubnub.subscribe
  190. channel: @channel
  191. callback: (message) =>
  192. unless message.uuid is @uuid
  193. switch message.method
  194. when "update" then @_onChanged message.model, message.options
  195. when "delete" then @_onRemoved message.options
  196. # Publish changes when this model is changed
  197. _onChange: (model, options) ->
  198. @publish "update", model, options
  199. _onChanged: (model, options) ->
  200. @off 'change', @_onChange, this
  201. # Manually find the difference and update this model
  202. diff = _.difference _.keys(@attributes), _.keys(model)
  203. _.each diff, (key) =>
  204. @unset key
  205. @set model
  206. @on 'change', @_onChange, this
  207. _onRemoved: (options) ->
  208. Backbone.Model.prototype.destroy.apply this, arguments
  209. destroy: (options) ->
  210. @publish "delete", null, options
  211. Backbone.Model.prototype.destroy.apply this, arguments