Skip to content

Commit

Permalink
Merge pull request #55 from timblack1/master
Browse files Browse the repository at this point in the history
Support for single global changes feed
  • Loading branch information
janmonschke committed Nov 2, 2012
2 parents 3e1bc00 + 52d81b2 commit 5184c49
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 10 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Demos

Changelog
---------
* 1.3
* Single global changes feed support to stay within the browser's concurrent connections per server limit [#25](https://github.com/janmonschke/backbone-couchdb/pull/25)

* 1.2.2
* Revamped view options [#51](https://github.com/janmonschke/backbone-couchdb/pull/51)

Expand All @@ -37,4 +40,4 @@ Changelog
* Chat example (including tests)
* Backbone 5.1 support
* Various bugfixes
* Started versioning ;)
* Started versioning ;)
73 changes: 64 additions & 9 deletions backbone-couchdb.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
###
(c) 2011 Jan Monschke
v1.2.2
(c) 2012 Jan Monschke
v1.3
backbone-couchdb.js is licensed under the MIT license.
###

Expand All @@ -13,9 +13,16 @@ Backbone.couch_connector = con =
list_name : null
# if true, all Collections will have the _changes feed enabled
global_changes : false
# if true, a single changes feed connection will be used
single_feed : false
# change the databse base_url to be able to fetch from a remote couchdb
base_url : null

# global changes feed for all collections
_global_db_inst: null
_global_changes_handler: null
_global_changes_callbacks: []

# some helper methods for the connector
helpers :
# returns a string representing the collection (needed for the "collection"-field)
Expand All @@ -32,10 +39,22 @@ Backbone.couch_connector = con =
# jquery.couch.js adds the id itself, so we delete the id if it is in the url.
# "collection/:id" -> "collection"
_splitted = _name.split "/"
_name = if _splitted.length > 0 then _splitted[0] else _name
_name = _name.replace "/", ""

# only pop off the last component if it is the id
if (_splitted.length > 0)
if (model.id == _splitted[_splitted.length - 1])
_splitted.pop()
_name = _splitted.join('/')

# remove any leading slash
if (_name.indexOf("/") == 0)
_name = _name.replace("/", "")
_name

# default local filter which selects documents of a given collection
filter_collection : (results, collection_name) ->
entry for entry in results when (entry.deleted == true) || (entry.doc?.collection == collection_name)

# creates a database instance from the
make_db : ->
db = $.couch.db con.config.db_name
Expand Down Expand Up @@ -117,6 +136,27 @@ Backbone.couch_connector = con =
else
@helpers.make_db().view "#{_ddoc}/#{_view}", _opts

# initializes the single global changes handler
init_global_changes_handler : (callback) ->
@_global_db_inst = con.helpers.make_db()
@_global_db_inst.info
"success" : (data) =>
# initialize the global changes handler
opts = _.extend { include_docs : true }, con.config.global_changes_opts
@_global_changes_handler = @_global_db_inst.changes (data.update_seq || 0), opts
# register a callback which delegates to every registered collection
@_global_changes_handler.onChange (changes) =>
cb(changes) for cb in @_global_changes_callbacks
callback()

# registers a collection callback with the global changes feed
register_global_changes_callback : (callback) ->
return unless callback?
if !@_global_db_inst?
@init_global_changes_handler =>
@_global_changes_callbacks.push callback
else
@_global_changes_callbacks.push callback

# Reads a model from the couchdb by it's ID
read_model : (model, opts) ->
Expand Down Expand Up @@ -209,9 +249,14 @@ class Backbone.Collection extends Backbone.Collection
# don't enable changes feed a second time
unless @_db_changes_enabled
@_db_changes_enabled = true
@_db_inst = con.helpers.make_db() unless @_db_inst
@_db_inst.info
"success" : @_db_prepared_for_changes
if con.config.single_feed
# if we are using a single feed, don't set up a separate connection for the collection
# register a callback with the global changes handler
@_db_prepared_for_global_changes()
else
@_db_inst = con.helpers.make_db() unless @_db_inst
@_db_inst.info
"success" : @_db_prepared_for_changes

# Stop listening to real time updates
stop_changes : ->
Expand All @@ -220,6 +265,7 @@ class Backbone.Collection extends Backbone.Collection
@_db_changes_handler.stop()
@_db_changes_handler = null

# sets up a new changes feed for this collection
_db_prepared_for_changes : (data) =>
@_db_update_seq = data.update_seq || 0
opts =
Expand All @@ -230,9 +276,18 @@ class Backbone.Collection extends Backbone.Collection
_.defer =>
@_db_changes_handler = @_db_inst.changes(@_db_update_seq, opts)
@_db_changes_handler.onChange @._db_on_change


# registers this collection's change handler with the global change feed
_db_prepared_for_global_changes : =>
con.register_global_changes_callback(@_db_on_change)

_db_on_change : (changes) =>
for _doc in changes.results
results = changes.results
if @db and @db.local_filter # if a local filter has been defined on the collection, use it
results = @db.local_filter(results)
else if con.config.single_feed # otherwise, if we are using a single feed, use the default global changes collection filter
results = con.helpers.filter_collection(results, con.helpers.extract_collection_name(@))
for _doc in results
obj = @get _doc.id
# test if collection contains the doc, if not, we add it to the collection
if obj?
Expand Down

0 comments on commit 5184c49

Please sign in to comment.