Skip to content

Commit 31e3a1e

Browse files
committed
Major project refactor.
1 parent f05a994 commit 31e3a1e

18 files changed

+1068
-11051
lines changed

README.md

-5
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,3 @@ that we will be using for data transmition.
5454

5555
Thats it! The point of the module is to override Backbone.sync and provide a seamless
5656
interface to data persistance.
57-
58-
59-
***
60-
61-
Beau Sorensen

browser/backbone.redis.js

+336
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
// backbone-redis
2+
// (c) 2011 Beau Sorensen
3+
// backbone-redis may be freely distributed under the MIT license.
4+
// For all details and documentation:
5+
// https://github.com/sorensen/backbone-redis
6+
7+
(function() {
8+
9+
// Save a reference to the global object.
10+
var root = this;
11+
12+
// The top-level namespace. All public classes and modules will
13+
// be attached to this.
14+
var core;
15+
16+
// Remote server socket connection reference
17+
var socket;
18+
19+
// Default socket event listener
20+
var listener = 'backbone';
21+
22+
// Storage container for subscribed models, allowing the returning method
23+
// calls from the server know where and how to find the model in question
24+
var Store = root.Store || (root.Store = {});
25+
26+
// Require Underscore, if we're on the server, and it's not already present.
27+
var _ = root._;
28+
if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
29+
30+
// Require Backbone, if we're on the server, and it's not already present.
31+
var Backbone = root.Backbone;
32+
if (!Backbone && (typeof require !== 'undefined')) Backbone = require('backbone');
33+
34+
_.mixin({
35+
36+
// ###getUrl
37+
// Helper function to get a URL from a Model or Collection as a property
38+
// or as a function.
39+
getUrl : function(object) {
40+
if (!(object && object.url)) return null;
41+
return _.isFunction(object.url) ? object.url() : object.url;
42+
}
43+
});
44+
45+
core = {
46+
47+
//###config
48+
config : function(options, next) {
49+
options.io && (socket = options.io);
50+
options.listener && (listener = options.listener);
51+
52+
socket.on(listener, function (packet) {
53+
core.process(packet);
54+
});
55+
next && next();
56+
},
57+
58+
//###process
59+
process : function(packet) {
60+
var model = packet.model,
61+
options = packet.options;
62+
63+
if (!options || !options.method) {
64+
return;
65+
}
66+
switch(options.method) {
67+
case 'published' : core.published(packet); break;
68+
case 'subscribed' : core.subscribed(packet); break;
69+
case 'unsubscribed' : core.unsubscribed(packet); break;
70+
case 'created' : core.created(packet); break;
71+
case 'read' : core.read(packet); break;
72+
case 'updated' : core.updated(packet); break;
73+
case 'destroyed' : core.destroyed(packet); break;
74+
}
75+
},
76+
77+
// Pubsub routines
78+
//----------------
79+
80+
//###subscribed
81+
// Someone has subscribed to a channel
82+
// Note: This method is not required to run the
83+
// application, it may prove as a useful way to
84+
// update clients, and it may prove to be an added
85+
// security risk, when private channels are involved
86+
subscribed : function(packet) {
87+
var options = packet.options;
88+
options.finished && options.finished(packet);
89+
},
90+
91+
//###unsubscribed
92+
// Someone has unsubscribed from a channel, see the
93+
// note above, as it applies to this method as well
94+
unsubscribed : function(packet) {
95+
var options = packet.options;
96+
options.finished && options.finished(packet);
97+
},
98+
99+
//###published
100+
// Data has been published by another client, this serves
101+
// as the main entry point for server to client communication.
102+
// Events are delegated based on the original method passed,
103+
// and are sent to 'crud.dnode.js' for completion
104+
published : function(packet) {
105+
var options = packet.options;
106+
if (!options.method) {
107+
return;
108+
}
109+
switch (options.method) {
110+
case 'create' : core.created(packet); break;
111+
case 'read' : core.read(packet); break;
112+
case 'update' : core.updated(packet); break;
113+
case 'delete' : core.destroyed(packet); break;
114+
};
115+
},
116+
117+
// CRUD routines
118+
//--------------
119+
120+
//###created
121+
// A model has been created on the server,
122+
// get the model or collection based on channel
123+
// name or url to set or add the new data
124+
created : function(packet) {
125+
var data = packet.model,
126+
options = packet.options,
127+
model = Store[options.channel];
128+
129+
// Model processing
130+
if (model instanceof Backbone.Model) {
131+
model.set(model.parse(data));
132+
// Collection processing
133+
} else if (model instanceof Backbone.Collection) {
134+
if (!model.get(data.id)) model.add(model.parse(data));
135+
}
136+
options.finished && options.finished(data);
137+
},
138+
139+
//###read
140+
// The server has responded with data from a
141+
// model or collection read event, set or add
142+
// the data to the model based on channel
143+
read : function(packet) {
144+
var data = packet.model,
145+
options = packet.options,
146+
model = Store[options.channel];
147+
148+
// Model Processing
149+
if (model instanceof Backbone.Model) {
150+
model.set(model.parse(data));
151+
// Collection processing
152+
} else if (model instanceof Backbone.Collection) {
153+
if (_.isArray(data)) {
154+
model.reset(model.parse(data));
155+
} else if (!model.get(data.id)) {
156+
model.add(model.parse(data));
157+
}
158+
}
159+
options.finished && options.finished(data);
160+
},
161+
162+
//###updated
163+
// A model has been updated with new data from the
164+
// server, set the appropriate model or collection
165+
updated : function(packet) {
166+
var data = packet.model,
167+
options = packet.options,
168+
model = Store[options.channel];
169+
170+
// Collection processing
171+
if (model.get(data.id)) {
172+
model.get(data.id).set(model.parse(data));
173+
// Model processing
174+
} else {
175+
model.set(model.parse(data));
176+
}
177+
options.finished && options.finished(data);
178+
},
179+
180+
//###destroyed
181+
// A model has been destroyed
182+
destroyed : function(packet) {
183+
var data = packet.model,
184+
options = packet.options,
185+
model = Store[options.channel];
186+
187+
Store[options.channel].remove(data) || delete Store[options.channel];
188+
options.finished && options.finished(data);
189+
}
190+
};
191+
192+
// Extend default Backbone functionality
193+
_.extend(Backbone.Model.prototype, {
194+
195+
//###url
196+
// This should probably be overriden with the underscore mixins
197+
// from the helpers.js methods
198+
url : function() {
199+
var base = _.getUrl(this.collection) || this.urlRoot || '';
200+
if (this.isNew()) return base;
201+
return base + (base.charAt(base.length - 1) == ':' ? '' : ':') + encodeURIComponent(this.id);
202+
},
203+
204+
//###publish
205+
// Publish model data to the server for processing, this serves as
206+
// the main entry point for client to server communications. If no
207+
// method is provided, it defaults to an 'update', which is the least
208+
// conflicting method when returned to the client for processing
209+
publish : function(options, next) {
210+
if (!socket) return (options.error && options.error(503, model, options));
211+
var model = this;
212+
options || (options = {});
213+
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
214+
options.method = 'publish';
215+
216+
var packet = {
217+
model : model.toJSON(),
218+
options : options
219+
};
220+
socket.emit(listener, packet, function(response){
221+
if (!options.silent) model.trigger('publish', model, options);
222+
next && next(response);
223+
});
224+
return this;
225+
}
226+
});
227+
228+
// Common extention object for both models and collections
229+
var common = {
230+
231+
//###connection
232+
// Setting a reference to the DNode/socket connection to allow direct
233+
// server communication without the need of a global object
234+
connection : socket,
235+
236+
//###subscribe
237+
// Subscribe to the 'Server' for model changes, if 'override' is set to true
238+
// in the options, this model will replace any other models in the local
239+
// 'Store' which holds the reference for future updates. Uses Backbone 'url'
240+
// for subscriptions, relabeled to 'channel' for clarity
241+
subscribe : function(options, next) {
242+
if (!socket) return (options.error && options.error(503, model, options));
243+
var model = this;
244+
options || (options = {});
245+
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
246+
options.method = 'subscribe';
247+
248+
var packet = {
249+
model : model.toJSON(),
250+
options : options
251+
};
252+
253+
// Add the model to a local object container so that other methods
254+
// called from the 'Server' have access to it
255+
if (!Store[options.channel] || options.override) {
256+
Store[options.channel] = model;
257+
socket.emit(listener, packet, function(response) {
258+
if (!options.silent) model.trigger('subscribe', model, options);
259+
next && next(response);
260+
});
261+
} else {
262+
if (!options.silent) model.trigger('subscribe', model, options);
263+
next && next(response);
264+
}
265+
return this;
266+
},
267+
268+
//###unsubscribe
269+
// Stop listening for published model data, removing the reference in the local
270+
// subscription 'Store', will trigger an unsubscribe event unless 'silent'
271+
// is passed in the options
272+
unsubscribe : function(options, next) {
273+
if (!socket) return (options.error && options.error(503, model, options));
274+
var model = this;
275+
options || (options = {});
276+
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
277+
options.method = 'unsubscribe';
278+
279+
var packet = {
280+
model : {},
281+
options : options
282+
}
283+
socket.emit(listener, packet, function(response) {
284+
if (!options.silent) model.trigger('unsubscribe', model, options);
285+
next && next(response);
286+
});
287+
288+
// The object must be deleted, or a new subscription with the same
289+
// channel name will not be correctly 'synced', unless a 'override'
290+
// option is sent upon subscription
291+
delete Store[options.channel];
292+
return this;
293+
}
294+
};
295+
296+
// Add to underscore utility functions to allow optional usage
297+
// This will allow other storage options easier to manage, such as
298+
// 'localStorage'. This must be set on the model and collection to
299+
// be used on directly. Defaults to 'Backbone.sync' otherwise.
300+
_.mixin({
301+
302+
//###sync
303+
// Set the model or collection's sync method to communicate through DNode
304+
sync : function(method, model, options) {
305+
if (!socket) return (options.error && options.error(503, model, options));
306+
307+
// Remove the Backbone id from the model as not to conflict with
308+
// Mongoose schemas, it will be re-assigned when the model returns
309+
// to the client side
310+
if (model.attributes && model.attributes._id) delete model.attributes.id;
311+
312+
// Set the RPC options for model interaction
313+
options.type || (options.type = model.type || model.collection.type);
314+
options.url || (options.url = _.getUrl(model));
315+
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
316+
options.method || (options.method = method);
317+
318+
// Create the packet to send over the wire
319+
var packet = {
320+
model : model.toJSON(),
321+
options : options
322+
}
323+
if (method === 'read') packet.model = {};
324+
socket.emit(listener, packet);
325+
}
326+
});
327+
328+
// Extend both model and collection with the pub/sub mechanics
329+
_.extend(Backbone.Model.prototype, common);
330+
_.extend(Backbone.Collection.prototype, common);
331+
332+
// Exported for both CommonJS and the browser.
333+
if (typeof exports !== 'undefined') module.exports = core;
334+
else root.core = core;
335+
336+
}).call(this)

browser/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Backbone-Redis
2+
// (c) 2011 Beau Sorensen
3+
// Backbone-Redis may be freely distributed under the MIT license.
4+
// For all details and documentation:
5+
// https://github.com/sorensen/backbone-redis
6+
7+
module.exports = require('./backbone-redis');

0 commit comments

Comments
 (0)