├── .gitignore
├── test
├── .gitignore
├── server.js
├── package.json
├── index.html
└── test.js
├── .project
├── lib
├── plugins
│ └── marilyn.watch.js
├── marilyn-min.js
└── marilyn.js
├── bower.json
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .DS_Store?
3 | ._*
4 | .Spotlight-V100
5 | .Trashes
6 | Icon?
7 | ehthumbs.db
8 | Thumbs.db
9 | bower_components/
10 | node_modules/
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .DS_Store?
3 | ._*
4 | .Spotlight-V100
5 | .Trashes
6 | Icon?
7 | ehthumbs.db
8 | Thumbs.db
9 | bower_components/
10 | node_modules/
--------------------------------------------------------------------------------
/test/server.js:
--------------------------------------------------------------------------------
1 | var io = require('socket.io').listen(3002);
2 |
3 | io.sockets.on('connection', function(socket) {
4 |
5 | socket.emit('response', {
6 | 'working': true
7 | });
8 |
9 | socket.on('request', function(data) {
10 | console.log(data);
11 |
12 | socket.emit('response', {
13 | 'working': true
14 | });
15 |
16 | });
17 |
18 | });
--------------------------------------------------------------------------------
/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "marilyn-test",
3 | "authors": [
4 | {
5 | "name": "Alan James",
6 | "email": "alanjames1987@gmail.com"
7 | }
8 | ],
9 | "license": "MIT",
10 | "ignore": [
11 | "**/.*",
12 | "node_modules",
13 | "bower_components",
14 | "test",
15 | "tests"
16 | ],
17 | "dependencies": {
18 | "socket.io": "0.9.16"
19 | }
20 | }
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | Marilyn is a client side, WebSocket driven, Pub/Sub, model layer with a query system similar to Mongoose.
9 |
10 | Angular, Backbone, Ember, and many other libraries provide model layers which are AJAX driven. While variations on these models exist they are usually only useful if you're using the entire framework they are built to work with.
11 |
12 | Marilyn can work with any framework, or by itself if you just need more data abstraction.
13 |
14 | ## Installation
15 |
16 | Install the module with bower:
17 |
18 | ```bash
19 | $ bower install marilyn
20 | ```
21 |
22 | Or [download it from GitHub](https://github.com/alanjames1987/marilyn/archive/master.zip), extract it, and copy the files into your application.
23 |
24 | ### Intalling with Script Tags
25 |
26 | Include a script for the `marilyn.js` or `marilyn-min.js` file after a script for it's dependency, `underscore.js`.
27 |
28 | Upon including the `marilyn.js` file a global `marilyn` object will be available.
29 |
30 | ### Intalling with AMD
31 |
32 | Marilyn can also be loaded using any AMD compliant module loader such as [RequireJS](http://www.requirejs.org/).
33 |
34 | Marilyn's only dependency is `underscore`.
35 |
36 | When using Marilyn with AMD you MUST load the models before using them inside controllers. This can be done as follows.
37 |
38 | ```js
39 |
40 | require(['model/myModel'], function(myModel) {
41 |
42 | // configure Marilyn and other core libraries
43 |
44 | require(['controller/myController'], function(myController) {
45 |
46 | // you can now use the model in your controller
47 |
48 | });
49 |
50 | });
51 |
52 | ```
53 |
54 | ## Usage
55 |
56 | ### Configure Socket.IO
57 |
58 | Before using Marilyn with Socket.IO you have to configure Marilyn to use Socket.IO's socket connection. Lets use the client side example connection from the Socket.IO website to demonstrate this.
59 |
60 | ```html
61 |
62 |
65 | ```
66 |
67 | This creates a global variable called `socket`. This variable should be passed to Marilyn so Marilyn can use the Socket.IO `on` and `emit` methods.
68 |
69 | ```html
70 |
73 | ```
74 |
75 | After this Marilyn has `on` and `emit` methods that we should call instead of the Socket.IO methods. This allows us to centralize all data querying and data fetching methods to Marilyn.
76 |
77 | ### Creating Model
78 |
79 | Like Mongoose, Marilyn creates models using the `model` method.
80 |
81 | ```js
82 | var MyModel = marilyn.model('someModelName');
83 | ```
84 |
85 | `MyModel` is now a Marilyn model, containing query and event methods.
86 |
87 | You can also create a model by passing the `marilyn.model` methods a second parameter, a callback function. Within this callback function `this` represents the model that has been created.
88 |
89 | ```js
90 | var MyModel = marilyn.model('someModelName', function(){
91 |
92 | // "this" is the same as MyModel
93 |
94 | });
95 | ```
96 |
97 | Like Mongoose, the Marilyn model created, called `someModelName`, can now be accessed from the global `marilyn` object.
98 |
99 | This allows you to use self executing functions to create a model and not pollute the global scope.
100 |
101 | ```js
102 | // myModel.js
103 |
104 | (function(){
105 |
106 | var NonPollutingModel = marilyn.model('someModelName', function(){
107 |
108 | this.on('someSocketEvent', function(data){
109 | // do something with data in model
110 | this.inform('someBrowserEvent', data);
111 | });
112 |
113 | });
114 |
115 | NonPollutingModel.on('someOtherSocketEvent', function(data){
116 | // do something else
117 | });
118 |
119 | })();
120 | ```
121 |
122 | ```js
123 | // myController.js
124 |
125 | (function(){
126 |
127 | var MyModel = marilyn.model('someModelName');
128 |
129 | MyModel.receive('someBrowserEvent', function(){
130 | // do something with data in controller
131 | });
132 |
133 | })();
134 | ```
135 |
136 | ### Adding Data to Models
137 |
138 | All models have a `_collection` variable.
139 |
140 | **Setting the `_collection` variable directly without the CRUD methods or the collection setter will not create this `__id` property and Marilyn will not function properly.**
141 |
142 | This variable is an array of all the objects you have stored in your frontend model.
143 |
144 | To populate this variable you can use the built in CRUD methods listed below, or the `collection` setter.
145 |
146 | If you use the CRUD methods various built in callbacks will be run. If you use the `collection` setter these callback functions won't be called.
147 |
148 | ```js
149 | marilyn.model('someModelName', function(){
150 |
151 | this.on('someSocketEvent', function(data){
152 |
153 | // sets the _collection array
154 | // this won't trigger any callbacks
155 | this.collection(data);
156 |
157 | });
158 |
159 | this.on('someOtherServerEvent', function(data){
160 |
161 | // pushes a new object into the _collection array and performs many other tasks
162 | // this will trigger all the "create" callbacks
163 | this.create(data);
164 |
165 | });
166 |
167 | });
168 | ```
169 |
170 | When new objects are added to the `_collection` variable a property of `__id` is added to them so Marilyn can internally track them.
171 |
172 | ### Event Handlers
173 |
174 | Marilyn has four types of event handlers, socket events, browser events, befores, and afters.
175 |
176 | Socket events are for communicating from your model to a socket server, or from a socket server to your model.
177 |
178 | Browser events are for communicating between your model and controller, or client side logic layer.
179 |
180 | Befores run before a query method is executed.
181 |
182 | Afters run after a query method is executed.
183 |
184 | Socket events and browser events have two methods, an event listener and an event dispatcher.
185 |
186 | #### Socket Events
187 |
188 | The socket event methods behave the same events as Socket.IO.
189 |
190 | They are `on` and `emit`.
191 |
192 | Refere to [Socket.IO documentation](http://socket.io/) to understand how these work.
193 |
194 | #### Browser Events
195 |
196 | Browser event methods are `receive` and `inform`. They act very similarly to Socket.IO's `on` and `emit`.
197 |
198 | They can send data and receive data with callback functions.
199 |
200 | ```js
201 | // myModel.js
202 |
203 | marilyn.model('someModelName', function(){
204 |
205 | this.inform('modelReady', {
206 | 'someKey':'someValue'
207 | });
208 |
209 | });
210 | ```
211 |
212 | ```js
213 | // myController.js
214 |
215 | var MyModel = marilyn.model('someModelName');
216 |
217 | MyModel.receive('modelReady', function(data){
218 | // do something here
219 | // data is the object passed from the inform method
220 | });
221 | ```
222 |
223 | All query events inform receivers after completion. This is best shown in the next example.
224 |
225 | #### Befores and Afters
226 |
227 | Befores and afters are similar to Mongoose's `pre` and `post` events. Befores are triggered before all querys, and afters are after the query.
228 |
229 | Befores and afters are triggered from `save`, `create`, `read`, `readOne`, `update`, and `delete`.
230 |
231 | Sometimes multiple befores and afters can be triggered by one CRUD method being invoked, for example the `save` method can trigger befores and afters for `create` and `update` in addition to befores and afters for `save` events.
232 |
233 | To create a before or after you use the `before` or `after` method passing it two parameters
234 |
235 | The first parameter is either a string of a single event name (listed above) or an array of strings of event names.
236 |
237 | The second paramter is a callback function.
238 |
239 | All callbacks of befores and afters are passed data that they can manipulate and a next method, which must be called in order to progress the flow control. Callbacks are passed different sets of data depending on the event being listened for, which are listed below.
240 |
241 | ##### Save / Create
242 |
243 | **before callback**
244 | ```js
245 | data:Object, next:Function
246 | ```
247 | `data` is the object being created. It is useful for validating data or adding default fields to objects.
248 |
249 | **after callback**
250 | ```js
251 | data:Object, next:Function
252 | ```
253 | `data` is the object that has been created. It is useful for altering data before it's returned to the callback or syncing the data with a server.
254 |
255 | ##### Read
256 |
257 | **before callback**
258 | ```js
259 | query:Object, next:Function
260 | ```
261 | `query` is what is being used to search for records. It is useful for restricting access to records.
262 |
263 | **after callback**
264 | ```js
265 | data:Array, next:Function
266 | ```
267 | `data` is an array of objects found from the query. It is useful for adding custom fields or methods.
268 |
269 | ##### Read One
270 |
271 | **before callback**
272 | ```js
273 | query:Object, next:Function
274 | ```
275 | `query` is the query being used to read the one object. It is useful for restricting access to records.
276 |
277 | **after callback**
278 | ```js
279 | data:Object, next:Function
280 | ```
281 | `data` is an the single object found from the query. It is useful for adding custom fields and methods.
282 |
283 | ##### Update
284 |
285 | **before callback**
286 | ```js
287 | searchQuery:Object, updateQuery:Object, next:Function
288 | ```
289 | `searchQuery` is the query being used to read the objects. It is useful for restricting access to records.
290 | `updateQuery` is the changes that will be made to all objects found. It is useful for restricting access to certain field updates.
291 |
292 | **after callback**
293 | ```js
294 | data:Array, next:Function
295 | ```
296 | `data` is an array of objects that have been updated. It is useful for adding custom fields and methods or syncing with a server.
297 |
298 | ##### Delete
299 |
300 | **before callback**
301 | ```js
302 | searchQuery:Object, next:Function
303 | ```
304 | `searchQuery` is the query being used to read the objects. It is useful for restricting access to records.
305 |
306 | **after callback**
307 | ```js
308 | data:Array, next:Function
309 | ```
310 | `data` is an array of objects that have been deleted. It is useful for syncing with a server.
311 |
312 | ### Querying Data with CRUD Methods
313 |
314 | Each Marilyn model has a private variable called `_collection`, which can be populated with an array of data. All query methods query this variable.
315 |
316 | There are ten query methods, `create`, `createSilent`, `read`, `readSilent`, `readOne`, `readOneSilent`, `update`, `updateSilent`, `del`, and `delSilent`.
317 |
318 | All silent query methods don't trigger befores or afters.
319 |
320 | #### Create
321 |
322 | ```js
323 | var myModel = new MyModel();
324 | myModel.title = 'Star Wars';
325 | myModel.director = 'George Lucas';
326 |
327 | // calling the save method will trigger create befores and afters as well as save befores and afters
328 | myModel.save(function(err, result){
329 | // result is the object created
330 | });
331 |
332 | // OR
333 |
334 | var myModel = new MyModel({
335 | 'title':'Star Wars',
336 | 'director':'George Lucas'
337 | });
338 |
339 | // calling the save method will trigger create befores and afters as well as save befores and afters
340 | myModel.save(function(err, result){
341 | // result is the object created
342 | });
343 |
344 | // OR
345 |
346 | // calling the create method will trigger create befores and afters, but not saves
347 | MyModel.create({
348 | 'title':'Star Wars',
349 | 'director':'George Lucas'
350 | }, function(err, result){
351 | // result is the object created
352 | });
353 | ```
354 |
355 | #### Read
356 |
357 | ```js
358 | // calling the read method will trigger read befores and afters
359 | // calling read without passing a query will read all
360 | MyModel.read(function(err, results){
361 | // results is an array of all the objects in the collection
362 | });
363 |
364 | // calling the read method will trigger read befores and afters
365 | MyModel.read({
366 | 'director':'George Lucas'
367 | }, function(err, results){
368 | // results is an array of all the objects found
369 | });
370 |
371 | // calling this method will trigger read befores and afters
372 | MyModel.read({
373 | 'director':'George Lucas'
374 | }, function(err, results){
375 | // results is an array of all the objects found
376 | });
377 |
378 | // calling this method will trigger readOne befores and afters
379 | MyModel.readOne({
380 | 'id':1138
381 | }, function(err, result){
382 | // result is the single object found
383 | });
384 | ```
385 |
386 | #### Update
387 |
388 | ```js
389 | // updates using readOne
390 | MyModel.readOne({
391 | 'id':1138
392 | }, function(err, result){
393 |
394 | result.director = 'George Lucas';
395 |
396 | // calling the save method will trigger update befores and afters as well as save befores and afters
397 | result.save(function(err, result){
398 | // result is the updated object
399 | });
400 |
401 | });
402 |
403 | // calling this method will trigger update befores and afters
404 | MyModel.update({
405 | 'id':1138
406 | }, {
407 | 'propertyToUpdate':'someValue'
408 | }, function(err, results){
409 | // results is an array of all the objects updated
410 | });
411 | ```
412 |
413 | #### Delete
414 |
415 | ```js
416 | // calling this method will trigger delete befores and afters
417 | MyModel.del({
418 | 'id':1138
419 | }, function(err results){
420 | // results is an array of all the objects deleted
421 | });
422 | ```
423 |
424 | `err` is always populated if nothing matches the query.
425 |
426 | Query methods don't directly call the server, you must call the server manually with `emit` either before or after query methods are invoked. This makes `before` and `after` very useful for server integration.
427 |
428 | Dependencies
429 | ---
430 |
431 | Marilyn requires Underscore >= 1.5.0. Get it from: [http://underscorejs.org/](http://underscorejs.org/)
432 |
433 | Author
434 | ---
435 |
436 | Alan James: [alanjames1987@gmail.com](mailto:alanjames1987@gmail.com)
437 |
438 | License
439 | ---
440 |
441 | Licensed under [MIT](https://github.com/alanjames1987/marilyn/blob/master/LICENSE).
442 |
--------------------------------------------------------------------------------
/lib/marilyn.js:
--------------------------------------------------------------------------------
1 | (function(factory) {
2 |
3 | // define marilyn using AMD
4 | if (typeof define === 'function' && define.amd) {
5 | define(['underscore'], factory);
6 | }
7 |
8 | // define marilyn by adding it to the window object
9 | else {
10 | window.Marilyn = window.marilyn = factory(_);
11 | }
12 |
13 | })(function(_) {
14 |
15 | // _socketConnection should be shared between all the models
16 | // it's a single connection to the server
17 | var _socketConnection;
18 |
19 | // the socket.io on function isn't avalible until Marilyn gets the socketConnection
20 | // after it gets this connection all the on events need to get attached to socket.io
21 | var _onEventBuffer = {};
22 |
23 | // the socket.io emit function isn't avalible until Marilyn gets the socketConnection
24 | // after it gets this connection all the emit events need to get sent to socket.io
25 | var _emitEventBuffer = [];
26 |
27 | // the is where all the models will be stored so getters can be used to retrieve them
28 | var _models = {};
29 |
30 | // get a Model from the global Marilyn object
31 | var _modelGet = function(modelName) {
32 | return _models[modelName];
33 | };
34 |
35 | // create a Model and assign it to the global Marilyn object
36 | var _modelSet = function(modelName, init) {
37 |
38 | // this is for saving changes to the result generated by read and readOne
39 | var _resultSave = function(callback) {
40 |
41 | // check if the callback is a valid function
42 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
43 |
44 | // saveType is used to determine which receiver to fire
45 | var saveType;
46 |
47 | var runCallback = function(err, result) {
48 |
49 | // this is needed to send an object
50 | if (saveType === 'create') {
51 | Model.inform('create', result);
52 | }
53 |
54 | // this is needed to send an array
55 | else if (saveType === 'update') {
56 | Model.inform('update', [result]);
57 | }
58 |
59 | Model.inform('save', result);
60 |
61 | // call the callback that was passed into the save
62 | callback.call(Model, err, result);
63 |
64 | };
65 |
66 | var runSaves = function(err, result) {
67 |
68 | // check if there are any afters assigned to the save
69 | if (Model._afters.hasOwnProperty('save')) {
70 |
71 | // call the after save callback
72 | Model._afters.save.call(Model, result, function() {
73 | runCallback(err, result);
74 | });
75 |
76 | } else {
77 | runCallback(err, result);
78 | }
79 |
80 | };
81 |
82 | var runUpdateComplete = function(object) {
83 |
84 | Model.updateSilent({
85 | '__id': object.__id
86 | }, object, function(err, results) {
87 |
88 | var result = results[0];
89 |
90 | // check if there are any afters assigned to the update
91 | if (Model._afters.hasOwnProperty('update')) {
92 |
93 | // call the adfter update callback
94 | Model._afters.update.call(Model, results, function() {
95 | runSaves(err, result);
96 | });
97 |
98 | } else {
99 | runSaves(err, result);
100 | }
101 |
102 | });
103 |
104 | };
105 |
106 | var runCreateComplete = function(object) {
107 |
108 | Model.createSilent(object, function(err, result) {
109 |
110 | // check if there are any afters assigned to the create
111 | if (Model._afters.hasOwnProperty('create')) {
112 |
113 | // call the after create callback
114 | Model._afters.create.call(Model, object, function() {
115 | runSaves(err, result);
116 | });
117 |
118 | } else {
119 | runSaves(err, result);
120 | }
121 |
122 | });
123 |
124 | };
125 |
126 | // run the correct CRUD method
127 | var runCRUD = function(object) {
128 |
129 | // CREATE
130 | // this object is being newly created
131 | if (!object.hasOwnProperty('__id')) {
132 |
133 | saveType = 'create';
134 |
135 | // check if there are any befores assigned to the create
136 | if (Model._befores.hasOwnProperty('create')) {
137 |
138 | // call the before create callback
139 | Model._befores.create.call(Model, object, function() {
140 | runCreateComplete(object);
141 | });
142 |
143 | } else {
144 | runCreateComplete(object);
145 | }
146 |
147 | }
148 |
149 | // UPDATE
150 | // check if the object has come from a read and needs to be updated
151 | else {
152 |
153 | saveType = 'update';
154 |
155 | // check if there are any befores assigned to the update
156 | if (Model._befores.hasOwnProperty('update')) {
157 |
158 | // call the before update callback
159 | Model._befores.update.call(Model, {
160 | '__id': object.__id
161 | }, object, function() {
162 | runUpdateComplete(object);
163 | });
164 |
165 | } else {
166 | runUpdateComplete(object);
167 | }
168 |
169 | }
170 |
171 | };
172 |
173 | // "this" is the result or a new object being created
174 | var _this = this || {};
175 |
176 | // the save can save a new or existing object using the create or update methods
177 | // this should trigger the correct befores and afters
178 | // it should always trigger all the before saves and after saves regardless of it being a create or update
179 |
180 | // check if there are any befores assigned to the save
181 | if (Model._befores.hasOwnProperty('save')) {
182 |
183 | // call the before save callback
184 | Model._befores.save.call(Model, _this, function() {
185 | runCRUD(_this);
186 | });
187 |
188 | } else {
189 | runCRUD(_this);
190 | }
191 |
192 | };
193 |
194 | var _resultDelete = function(callback) {
195 |
196 | // check if the callback is a valid function
197 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
198 |
199 | // "this" will always reference the item being deleted
200 | Model.del(this, callback);
201 |
202 | };
203 |
204 | var _resultDeleteSilent = function(callback) {
205 |
206 | // check if the callback is a valid function
207 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
208 |
209 | // "this" will always reference the item
210 | Model.delSilent(this, callback);
211 |
212 | };
213 |
214 | // this Model function will be what contains all the CRUD functions
215 | // it is an function so we can use the "new" keyword to create one
216 | var Model = _models[modelName] = function(object) {
217 |
218 | object = object || {};
219 |
220 | // add additional methods to the object
221 | object.save = _resultSave;
222 |
223 | // we don't need to add the delete methods
224 | // they get added when the save method is called
225 |
226 | return object;
227 |
228 | };
229 |
230 | // PRIVATE PROPERTIES
231 |
232 | // setup the private variables of the Model
233 | // collection will store all the data in the Model
234 | Model._collection = [];
235 | Model._id = 0;
236 |
237 | Model._befores = {};
238 | Model._afters = {};
239 |
240 | Model._receivers = {};
241 |
242 | Model._name = modelName;
243 |
244 | // PRIVATE METHODS
245 |
246 | Model._nextId = function() {
247 | Model._id++;
248 | return Model._id;
249 | };
250 |
251 | // PUBLIC METHODS
252 |
253 | // Socket.IO events
254 |
255 | Model.on = function(eventType, callback) {
256 |
257 | // if the socketConnection is not setup the ons need to get buffered and applied later
258 | if (!_socketConnection) {
259 |
260 | // set the Model name property if not already set
261 | if (!_onEventBuffer.hasOwnProperty(Model._name)) {
262 | _onEventBuffer[Model._name] = {};
263 | }
264 |
265 | // set the callback into the onBuffer object
266 | _onEventBuffer[Model._name][eventType] = callback;
267 |
268 | } else {
269 |
270 | _socketConnection.on(eventType, function(data) {
271 | callback.call(Model, data);
272 | });
273 |
274 | }
275 |
276 | };
277 |
278 | Model.emit = function(eventType, data, data2) {
279 |
280 | // if the socketConnection is not setup the emits need to get buffered and sent later
281 | if (!_socketConnection) {
282 |
283 | // push the event and data into the emitBuffer object
284 | _emitEventBuffer.push([eventType, data, data2]);
285 |
286 | } else {
287 |
288 | _socketConnection.emit(eventType, data, data2);
289 |
290 | }
291 |
292 | };
293 |
294 | // internal events
295 |
296 | Model.use = function(pluginCallback) {
297 | // call the plugin callback
298 | pluginCallback.call(Model);
299 | };
300 |
301 | // listen for a before event
302 | Model.before = function(eventType, callback) {
303 |
304 | // the event was a single event
305 | // no need to setup multiple
306 | if (_.isString(eventType)) {
307 | Model._befores[eventType] = callback;
308 | }
309 |
310 | // multiple events were passed in
311 | else if (_.isArray(eventType)) {
312 | _.each(eventType, function(key) {
313 | Model._befores[key] = callback;
314 | });
315 | }
316 |
317 | };
318 |
319 | // remove a before event listener
320 | Model.beforeRemove = function(eventType) {
321 |
322 | // the event was a single event
323 | // no need to remove multiple
324 | if (_.isString(eventType)) {
325 | Model._befores[eventType] = function() {};
326 | }
327 |
328 | // multiple events were passed in
329 | else if (_.isArray(eventType)) {
330 | _.each(eventType, function(key) {
331 | Model._befores[key] = function() {};
332 | });
333 | }
334 |
335 | };
336 |
337 | // listen for an after event
338 | Model.after = function(eventType, callback) {
339 |
340 | // the event was a single event
341 | // no need to setup multiple
342 | if (_.isString(eventType)) {
343 | Model._afters[eventType] = callback;
344 | }
345 |
346 | // multiple events were passed in
347 | else if (_.isArray(eventType)) {
348 | _.each(eventType, function(key) {
349 | Model._afters[key] = callback;
350 | });
351 | }
352 |
353 | };
354 |
355 | // remove an after event listener
356 | Model.afterRemove = function(eventType) {
357 |
358 | // the event was a single event
359 | // no need to remove multiple
360 | if (_.isString(eventType)) {
361 | Model._afters[eventType] = function() {};
362 | }
363 |
364 | // multiple events were passed in
365 | else if (_.isArray(eventType)) {
366 | _.each(eventType, function(key) {
367 | Model._afters[key] = function() {};
368 | });
369 | }
370 |
371 | };
372 |
373 | // trigger a receive
374 | Model.inform = function(eventType, data) {
375 | if (Model._receivers[eventType]) {
376 | // call the receiver callback
377 | Model._receivers[eventType].call(Model, data);
378 | }
379 | };
380 |
381 | // listen for an inform
382 | Model.receive = function(eventType, callback) {
383 |
384 | // the event was a single event
385 | // no need to setup multiple
386 | if (_.isString(eventType)) {
387 | Model._receivers[eventType] = callback;
388 | }
389 |
390 | // multiple events were passed in
391 | else if (_.isArray(eventType)) {
392 | _.each(eventType, function(key) {
393 | Model._receivers[key] = callback;
394 | });
395 | }
396 |
397 | };
398 |
399 | // remove a receive event listener
400 | Model.receiveRemove = function(eventType) {
401 |
402 | // remove all receivers if the event type is not specified
403 | if (_.isEmpty(eventType)) {
404 |
405 | Model._receivers = {};
406 |
407 | }
408 |
409 | // remove specific receivers
410 | else {
411 |
412 | // the event was a single event
413 | // no need to remove multiple
414 | if (_.isString(eventType)) {
415 | Model._receivers[eventType] = function() {};
416 | }
417 |
418 | // multiple events were passed in
419 | else if (_.isArray(eventType)) {
420 | _.each(eventType, function(key) {
421 | Model._receivers[key] = function() {};
422 | });
423 | }
424 |
425 | }
426 |
427 | };
428 |
429 | // query methods
430 |
431 | // replaces the internal collection with a new array of objects
432 | // this will add an __id property
433 | // it will also change the collection in a data binding friendly way
434 | Model.collection = function(collection) {
435 |
436 | // check if the collection is an array
437 | if (!_.isArray(collection)) {
438 | // TODO throw error
439 | return;
440 | }
441 |
442 | // replace the collection like this for data binding
443 | while (Model._collection.length > 0) {
444 | Model._collection.pop();
445 | }
446 |
447 | for (var i = 0, j = collection.length; i < j; i++) {
448 | collection[i].__id = Model._nextId();
449 | Model._collection.push(collection[i]);
450 | }
451 |
452 | };
453 |
454 | // CREATE
455 |
456 | Model.create = function(element, callback) {
457 |
458 | // store a clone of the query for later reference
459 | var originalQuery = _.clone(element);
460 |
461 | // check if the callback is a valid function
462 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
463 |
464 | var runCallback = function(err, result) {
465 |
466 | Model.inform('create', result);
467 |
468 | // call the callback that was passed into the create
469 | callback.call(Model, err, result);
470 |
471 | };
472 |
473 | var runComplete = function() {
474 |
475 | Model.createSilent(element, function(err, result) {
476 |
477 | // check if there are any afters assigned to the create
478 | if (!Model._afters.hasOwnProperty('create')) {
479 | runCallback(err, result);
480 | return;
481 | }
482 |
483 | // if the function is expecting 3 arguments pass the query, result, and callback
484 | if (Model._afters.create.length == 3) {
485 | Model._afters.create.call(Model, originalQuery, result, function() {
486 | runCallback(err, result);
487 | });
488 | return;
489 | }
490 |
491 | // if the function is expecting 2 arguments only pass the result and callback
492 | Model._afters.create.call(Model, result, function() {
493 | runCallback(err, result);
494 | });
495 |
496 | });
497 |
498 | };
499 |
500 | // check if there are any befores assigned to the create
501 | if (!Model._befores.hasOwnProperty('create')) {
502 | runComplete();
503 | return;
504 | }
505 |
506 | // call the before create callback
507 | Model._befores.create.call(Model, element, function() {
508 | runComplete();
509 | });
510 |
511 | };
512 |
513 | Model.createSilent = function(element, callback) {
514 |
515 | // check if the callback is a valid function
516 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
517 |
518 | var err = null;
519 |
520 | // set the internal id using the Model's next id
521 | element.__id = Model._nextId();
522 |
523 | // allow the returned object to be saved in the future
524 | element.save = _resultSave;
525 |
526 | element.del = function(callback) {
527 | _resultDelete.call(element, callback);
528 | };
529 |
530 | element.delSilent = function(callback) {
531 | _resultDeleteSilent.call(element, callback);
532 | };
533 |
534 | element['delete'] = element.del;
535 | element.deleteSilent = element.delSilent;
536 |
537 | Model._collection.push(element);
538 |
539 | // call the callback that was passed into the createSilent
540 | callback.call(Model, err, element);
541 |
542 | };
543 |
544 | // READ
545 |
546 | Model.read = function(query, callback) {
547 |
548 | // store a clone of the query for later reference
549 | var originalQuery = _.clone(query);
550 |
551 | // check if the callback is a valid function
552 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
553 |
554 | var runCallback = function(err, results) {
555 |
556 | Model.inform('read', results);
557 |
558 | // call the callback that was passed into the read
559 | callback.call(Model, err, results);
560 |
561 | };
562 |
563 | var runComplete = function() {
564 |
565 | Model.readSilent(query, function(err, results) {
566 |
567 | // check if there are any afters assigned to the read
568 | if (!Model._afters.hasOwnProperty('read')) {
569 | runCallback(err, results);
570 | return;
571 | }
572 |
573 | // if the function is expecting 3 arguments pass the query, results, and callback
574 | if (Model._afters.read.length == 3) {
575 | Model._afters.read.call(Model, originalQuery, results, function() {
576 | runCallback(err, results);
577 | });
578 | return;
579 | }
580 |
581 | // if the after function is expecting anything else send back the expected result and a callback
582 | Model._afters.read.call(Model, results, function() {
583 | runCallback(err, results);
584 | });
585 |
586 | });
587 |
588 | };
589 |
590 | // check if there are any befores assigned to the read
591 | if (!Model._befores.hasOwnProperty('read')) {
592 | runComplete();
593 | return;
594 | }
595 |
596 | // call the before read callback
597 | Model._befores.read.call(Model, query, function() {
598 | runComplete();
599 | });
600 |
601 | };
602 |
603 | Model.readSilent = function(query, callback) {
604 |
605 | // check if the callback is a valid function
606 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
607 |
608 | var err = null;
609 |
610 | var readAll = false;
611 |
612 | // if no query was passed
613 | if (typeof query === 'function') {
614 | callback = query;
615 | readAll = true;
616 | }
617 |
618 | // or if the query object was empty
619 | else if (_.isEmpty(query)) {
620 | readAll = true;
621 | }
622 |
623 | var results = [];
624 |
625 | if (readAll) {
626 | results = Model._collection;
627 | } else {
628 | results = _.where(Model._collection, query);
629 | }
630 |
631 | // add additional methods to the results
632 | _.each(results, function(result) {
633 |
634 | result.save = _resultSave;
635 |
636 | result.del = function(callback) {
637 | _resultDelete.call(result, callback);
638 | };
639 |
640 | result.delSilent = function(callback) {
641 | _resultDeleteSilent.call(result, callback);
642 | };
643 |
644 | result['delete'] = result.del;
645 | result.deleteSilent = result.delSilent;
646 |
647 | });
648 |
649 | // call the callback that was passed into the readSilent
650 | callback.call(Model, err, results);
651 |
652 | };
653 |
654 | Model.readOne = function(query, callback) {
655 |
656 | // store a clone of the query for later reference
657 | var originalQuery = _.clone(query);
658 |
659 | // check if the callback is a valid function
660 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
661 |
662 | var runCallback = function(err, result) {
663 |
664 | Model.inform('readOne', result);
665 |
666 | // call the callback that was passed into the readOne
667 | callback.call(Model, err, result);
668 |
669 | };
670 |
671 | var runComplete = function() {
672 |
673 | Model.readOneSilent(query, function(err, result) {
674 |
675 | // check if there are any afters assigned to the readOne
676 | if (!Model._afters.hasOwnProperty('readOne')) {
677 | runCallback(err, result);
678 | return;
679 | }
680 |
681 | // if the function is expecting 3 arguments pass the query, result, and callback
682 | if (Model._afters.readOne.length == 3) {
683 | Model._afters.readOne.call(Model, originalQuery, result, function() {
684 | runCallback(err, result);
685 | });
686 | return;
687 | }
688 |
689 | Model._afters.readOne.call(Model, result, function() {
690 | runCallback(err, result);
691 | });
692 |
693 | });
694 |
695 | };
696 |
697 | // check if there are any befores assigned to the readOne
698 | if (Model._befores.hasOwnProperty('readOne')) {
699 |
700 | // call the before readOne callback
701 | Model._befores.readOne.call(Model, query, function() {
702 | runComplete();
703 | });
704 |
705 | } else {
706 | runComplete();
707 | }
708 |
709 | };
710 |
711 | Model.readOneSilent = function(query, callback) {
712 |
713 | // check if the callback is a valid function
714 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
715 |
716 | var err = null;
717 |
718 | var results = _.where(Model._collection, query);
719 |
720 | var result = null;
721 |
722 | if (results[0]) {
723 |
724 | result = results[0];
725 |
726 | // add additonal methods to the result
727 | result.save = _resultSave;
728 |
729 | result.del = function(callback) {
730 | _resultDelete.call(result, callback);
731 | };
732 |
733 | result.delSilent = function(callback) {
734 | _resultDeleteSilent.call(result, callback);
735 | };
736 |
737 | result['delete'] = result.del;
738 | result.deleteSilent = result.delSilent;
739 |
740 | }
741 |
742 | // call the callback that was passed into the readOneSilent
743 | callback.call(Model, err, result);
744 |
745 | };
746 |
747 | // UPDATE
748 |
749 | Model.update = function(query, changes, callback) {
750 |
751 | // store a clone of the query for later reference
752 | var originalQuery = _.clone(query);
753 | var originalChanges = _.clone(changes);
754 |
755 | // check if the callback is a valid function
756 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
757 |
758 | var runCallback = function(err, results) {
759 |
760 | Model.inform('update', results);
761 |
762 | // call the callback that was passed into the update
763 | callback.call(Model, err, results);
764 |
765 | };
766 |
767 | var runComplete = function() {
768 |
769 | Model.updateSilent(query, changes, function(err, results) {
770 |
771 | // check if there are any afters assigned to the update
772 | if (!Model._afters.hasOwnProperty('update')) {
773 | runCallback(err, results);
774 | return;
775 | }
776 |
777 | // if the function is expecting 4 arguments pass the err, query, results, and callback
778 | if (Model._afters.update.length == 4) {
779 | Model._afters.update.call(Model, originalQuery, originalChanges, results, function() {
780 | runCallback(err, results);
781 | });
782 | return;
783 | }
784 |
785 | // if the after function is expecting anything else send back the expected result and a callback
786 | Model._afters.update.call(Model, results, function() {
787 | runCallback(err, results);
788 | });
789 |
790 | });
791 |
792 | };
793 |
794 | // check if there are any befores assigned to the update
795 | if (!Model._befores.hasOwnProperty('update')) {
796 | runComplete();
797 | return;
798 | }
799 |
800 | // call the before update callback
801 | Model._befores.update.call(Model, query, changes, function() {
802 | runComplete();
803 | });
804 |
805 | };
806 |
807 | Model.updateSilent = function(query, changes, callback) {
808 |
809 | // check if the callback is a valid function
810 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
811 |
812 | var err = null;
813 |
814 | var results = _.where(Model._collection, query);
815 |
816 | if (results.length > 0) {
817 |
818 | _.each(results, function(element) {
819 |
820 | _.each(changes, function(value, key) {
821 | element[key] = value;
822 | });
823 |
824 | });
825 |
826 | }
827 |
828 | // call the callback that was passed into the updateSilent
829 | callback.call(Model, err, results);
830 |
831 | };
832 |
833 | // DELETE
834 |
835 | Model.del = function(query, callback) {
836 |
837 | // store a clone of the query for later reference
838 | var originalQuery = _.clone(query);
839 |
840 | // check if the callback is a valid function
841 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
842 |
843 | var runCallback = function(err, results) {
844 |
845 | Model.inform('delete', results);
846 |
847 | // call the callback that was passed into the del
848 | callback.call(Model, err, results);
849 |
850 | };
851 |
852 | var runComplete = function() {
853 |
854 | Model.deleteSilent(query, function(err, results) {
855 |
856 | // check if there are any afters assigned to the delete
857 | if (!Model._afters.hasOwnProperty('delete')) {
858 | runCallback(err, results);
859 | return;
860 | }
861 |
862 | // if the function is expecting 3 arguments pass the query, results, and callback
863 | if (Model._afters['delete'].length == 3) {
864 | Model._afters['delete'].call(Model, originalQuery, results, function() {
865 | runCallback(err, results);
866 | });
867 | return;
868 | }
869 |
870 | Model._afters['delete'].call(Model, results, function() {
871 | runCallback(err, results);
872 | });
873 |
874 |
875 | });
876 |
877 | };
878 |
879 | // check if there are any befores assigned to the delete
880 | if (!Model._befores.hasOwnProperty('delete')) {
881 | runComplete();
882 | return;
883 | }
884 |
885 | // call the before delete callback
886 | Model._befores['delete'].call(Model, query, function() {
887 | runComplete();
888 | });
889 |
890 | };
891 |
892 | Model.delSilent = function(query, callback) {
893 |
894 | // check if the callback is a valid function
895 | callback = (callback && _.isFunction(callback)) ? callback : function() {};
896 |
897 | var err = null;
898 |
899 | var results = _.where(Model._collection, query);
900 |
901 | if (results.length > 0) {
902 |
903 | _.each(results, function(element) {
904 |
905 | var index = _.indexOf(Model._collection, element);
906 | Model._collection.splice(index, 1);
907 |
908 | });
909 |
910 | }
911 |
912 | // call the callback that was passed into the delSilent
913 | callback.call(Model, err, results);
914 |
915 | };
916 |
917 | // aliases
918 | Model.find = Model.read;
919 | Model.findSilent = Model.readSilent;
920 | Model.findOne = Model.readOne;
921 | Model.findOneSilent = Model.readOneSilent;
922 |
923 | Model['delete'] = Model.del;
924 | Model.deleteSilent = Model.delSilent;
925 |
926 | // run the callback init function if it was passed passing the Model as the "this"
927 | init = (init && _.isFunction(init)) ? init : function() {};
928 | init.call(Model);
929 |
930 | return Model;
931 |
932 | };
933 |
934 | // create the marilyn object
935 | var marilyn = {};
936 |
937 | marilyn.VERSION = '0.17.1';
938 |
939 | marilyn.config = function(socketConnection) {
940 |
941 | _socketConnection = socketConnection;
942 |
943 | // setup the ons
944 | for (var model in _onEventBuffer) {
945 |
946 | for (var eventType in _onEventBuffer[model]) {
947 |
948 | // this has to be a self executing function to retain scope through dependency injection
949 | (function(model, eventType, callback) {
950 |
951 | _socketConnection.on(eventType, function(data) {
952 | callback.call(model, data);
953 | });
954 |
955 | })(_models[model], eventType, _onEventBuffer[model][eventType]);
956 |
957 | }
958 |
959 | }
960 |
961 | // send the emits
962 | for (var i = 0, j = _emitEventBuffer.length; i < j; i++) {
963 | _socketConnection.emit(_emitEventBuffer[i][0], _emitEventBuffer[i][1], _emitEventBuffer[i][2]);
964 | }
965 |
966 | _onEventBuffer = {};
967 |
968 | };
969 |
970 | marilyn.model = function(modelName, init) {
971 |
972 | if (_models[modelName] && !init) {
973 | return _modelGet(modelName);
974 | }
975 |
976 | return _modelSet(modelName, init);
977 |
978 | };
979 |
980 | marilyn.modelRemove = function(modelName) {
981 | _models[modelName] = null;
982 | };
983 |
984 | marilyn.receiveRemove = function(modelName) {
985 | for (var model in _models) {
986 | _models[model].receiveRemove();
987 | }
988 | };
989 |
990 | return marilyn;
991 |
992 | });
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | /** CREATE */
2 |
3 | QUnit.test('CREATE - before, after, receive, and callback events are given the correct context', function(assert) {
4 |
5 | var context = {};
6 | context.before = false;
7 | context.after = false;
8 | context.receive = false;
9 | context.callback = false;
10 |
11 | var name = uuid.v4();
12 |
13 | var contextCheck = uuid.v4();
14 |
15 | var Model = marilyn.model(name, function() {
16 |
17 | this.contextCheck = contextCheck;
18 |
19 | this.before('create', function(data, next) {
20 | context.before = (this.contextCheck === contextCheck);
21 | next();
22 | });
23 |
24 | this.after('create', function(data, next) {
25 | context.after = (this.contextCheck === contextCheck);
26 | next();
27 | });
28 |
29 | this.receive('create', function(data) {
30 | context.receive = (this.contextCheck === contextCheck);
31 | });
32 |
33 | });
34 |
35 | Model.create({
36 | 'contextCheck': contextCheck,
37 | }, function(err, data) {
38 | context.callback = (this.contextCheck === contextCheck);
39 | });
40 |
41 | assert.ok(context.before, 'create before context');
42 | assert.ok(context.after, 'create after context');
43 | assert.ok(context.receive, 'create receive context');
44 | assert.ok(context.callback, 'create callback context');
45 |
46 | });
47 |
48 | QUnit.test('CREATE - before, after, receive, and callback events are given the correct context when saving a new instance', function(assert) {
49 |
50 | var context = {};
51 | context.createBefore = false;
52 | context.createAfter = false;
53 | context.createReceive = false;
54 | context.saveBefore = false;
55 | context.saveAfter = false;
56 | context.saveReceive = false;
57 | context.callback = false;
58 |
59 | var name = uuid.v4();
60 |
61 | var contextCheck = uuid.v4();
62 |
63 | var Model = marilyn.model(name, function() {
64 |
65 | this.contextCheck = contextCheck;
66 |
67 | this.before('create', function(data, next) {
68 | context.createBefore = (this.contextCheck === contextCheck);
69 | next();
70 | });
71 |
72 | this.after('create', function(data, next) {
73 | context.createAfter = (this.contextCheck === contextCheck);
74 | next();
75 | });
76 |
77 | this.receive('create', function(data) {
78 | context.createReceive = (this.contextCheck === contextCheck);
79 | });
80 |
81 | this.before('save', function(data, next) {
82 | context.saveBefore = (this.contextCheck === contextCheck);
83 | next();
84 | });
85 |
86 | this.after('save', function(data, next) {
87 | context.saveAfter = (this.contextCheck === contextCheck);
88 | next();
89 | });
90 |
91 | this.receive('save', function(data) {
92 | context.saveReceive = (this.contextCheck === contextCheck);
93 | });
94 |
95 | });
96 |
97 | var item = new Model();
98 | item.someProperty = 'someValue';
99 |
100 | item.save(function(err, data) {
101 | context.callback = (this.contextCheck === contextCheck);
102 | });
103 |
104 | assert.ok(context.createBefore, 'new create before context');
105 | assert.ok(context.createAfter, 'new create after context');
106 | assert.ok(context.createReceive, 'new create receive context');
107 | assert.ok(context.saveBefore, 'new create before context');
108 | assert.ok(context.saveAfter, 'new create after context');
109 | assert.ok(context.saveReceive, 'new create receive context');
110 | assert.ok(context.callback, 'new create callback context');
111 |
112 | });
113 |
114 | QUnit.test('CREATE - before, after, receive, and callback events run when creating and have valid data types', function(assert) {
115 |
116 | var ran = {};
117 | ran.before = 0;
118 | ran.after = 0;
119 | ran.receive = 0;
120 | ran.callback = 0;
121 |
122 | var typeValid = {};
123 | typeValid.before = false;
124 | typeValid.after = false;
125 | typeValid.receive = false;
126 | typeValid.callback = false;
127 |
128 | var name = uuid.v4();
129 |
130 | var Model = marilyn.model(name, function() {
131 |
132 | this.before('create', function(data, next) {
133 | ran.before = 1;
134 | typeValid.before = _.isObject(data);
135 | next();
136 | });
137 |
138 | this.after('create', function(data, next) {
139 | ran.after = 2;
140 | typeValid.after = _.isObject(data);
141 | next();
142 | });
143 |
144 | this.receive('create', function(data) {
145 | ran.receive = 3;
146 | typeValid.receive = _.isObject(data);
147 | });
148 |
149 | });
150 |
151 | Model.create({
152 | 'someProperty': 'someValue',
153 | }, function(err, data) {
154 | ran.callback = 4;
155 | typeValid.callback = _.isObject(data);
156 | });
157 |
158 | assert.equal(ran.before, 1, 'create before ran');
159 | assert.equal(ran.after, 2, 'create after ran');
160 | assert.equal(ran.receive, 3, 'create receive ran');
161 | assert.equal(ran.callback, 4, 'create callback ran');
162 |
163 | assert.ok(typeValid.before, 'create before has valid data type');
164 | assert.ok(typeValid.after, 'create after has valid data type');
165 | assert.ok(typeValid.receive, 'create receive has valid data type');
166 |
167 | });
168 |
169 | QUnit.test('CREATE - before, after, receive, and callback events run when saving a new instance and have valid data types', function(assert) {
170 |
171 | var ran = {};
172 | ran.createBefore = 0;
173 | ran.createAfter = 0;
174 | ran.createReceive = 0;
175 | ran.saveBefore = 0;
176 | ran.saveAfter = 0;
177 | ran.saveReceive = 0;
178 | ran.callback = 0;
179 |
180 | var typeValid = {};
181 | typeValid.createBefore = false;
182 | typeValid.createAfter = false;
183 | typeValid.createReceive = false;
184 | typeValid.saveBefore = false;
185 | typeValid.saveAfter = false;
186 | typeValid.saveReceive = false;
187 | typeValid.callback = false;
188 |
189 | var name = uuid.v4();
190 |
191 | var Model = marilyn.model(name, function() {
192 |
193 | this.before('save', function(data, next) {
194 | ran.saveBefore = 1;
195 | typeValid.saveBefore = _.isObject(data);
196 | next();
197 | });
198 |
199 | this.before('create', function(data, next) {
200 | ran.createBefore = 2;
201 | typeValid.createBefore = _.isObject(data);
202 | next();
203 | });
204 |
205 | this.after('create', function(data, next) {
206 | ran.createAfter = 3;
207 | typeValid.createAfter = _.isObject(data);
208 | next();
209 | });
210 |
211 | this.after('save', function(data, next) {
212 | ran.saveAfter = 4;
213 | typeValid.saveAfter = _.isObject(data);
214 | next();
215 | });
216 |
217 | this.receive('create', function(data) {
218 | ran.createReceive = 5;
219 | typeValid.createReceive = _.isObject(data);
220 | });
221 |
222 | this.receive('save', function(data) {
223 | ran.saveReceive = 6;
224 | typeValid.saveReceive = _.isObject(data);
225 | });
226 |
227 | });
228 |
229 | var item = new Model();
230 | item.someProperty = 'someValue';
231 |
232 | item.save(function(err, data) {
233 | ran.callback = 7;
234 | typeValid.callback = _.isObject(data);
235 | });
236 |
237 | assert.equal(ran.createBefore, 2, 'new create before ran');
238 | assert.equal(ran.createAfter, 3, 'new create after ran');
239 | assert.equal(ran.createReceive, 5, 'new create receive ran');
240 | assert.equal(ran.saveBefore, 1, 'new create before save ran');
241 | assert.equal(ran.saveAfter, 4, 'new create after save ran');
242 | assert.equal(ran.saveReceive, 6, 'new create receive save ran');
243 | assert.equal(ran.callback, 7, 'new create callback ran');
244 |
245 | assert.ok(typeValid.createBefore, 'new create before has valid data type');
246 | assert.ok(typeValid.createAfter, 'new create after has valid data type');
247 | assert.ok(typeValid.createReceive, 'new create receive has valid data type');
248 | assert.ok(typeValid.saveBefore, 'new create before save has valid data type');
249 | assert.ok(typeValid.saveAfter, 'new create after save has valid data type');
250 | assert.ok(typeValid.saveReceive, 'new create receive save has valid data type');
251 | assert.ok(typeValid.callback, 'new create calback has valid data type');
252 |
253 | });
254 |
255 | /** READ */
256 |
257 | QUnit.test('READ - before, after, receive, and callback events are given the correct context', function(assert) {
258 |
259 | var context = {};
260 | context.before = false;
261 | context.after = false;
262 | context.receive = false;
263 | context.callback = false;
264 |
265 | var name = uuid.v4();
266 |
267 | var contextCheck = uuid.v4();
268 |
269 | var Model = marilyn.model(name, function() {
270 |
271 | this.contextCheck = contextCheck;
272 |
273 | this.before('read', function(data, next) {
274 | context.before = (this.contextCheck === contextCheck);
275 | next();
276 | });
277 |
278 | this.after('read', function(data, next) {
279 | context.after = (this.contextCheck === contextCheck);
280 | next();
281 | });
282 |
283 | this.receive('read', function(data) {
284 | context.receive = (this.contextCheck === contextCheck);
285 | });
286 |
287 | });
288 |
289 | Model.read({
290 | 'someProperty': 'someValue',
291 | }, function(err, data) {
292 | context.callback = (this.contextCheck === contextCheck);
293 | });
294 |
295 | assert.ok(context.before, 'read before context');
296 | assert.ok(context.after, 'read after context');
297 | assert.ok(context.receive, 'read receive context');
298 | assert.ok(context.callback, 'read callback context');
299 |
300 | });
301 |
302 | QUnit.test('READ - before receive, after, and callback events run when reading all and have valid data types', function(assert) {
303 |
304 | var ran = {};
305 | ran.before = 0;
306 | ran.receive = 0;
307 | ran.after = 0;
308 | ran.callback = 0;
309 |
310 | var typeValid = {};
311 | typeValid.before = false;
312 | typeValid.after = false;
313 | typeValid.receive = false;
314 | typeValid.callback = false;
315 |
316 | var name = uuid.v4();
317 |
318 | var Model = marilyn.model(name, function() {
319 |
320 | this.before('read', function(query, next) {
321 | ran.before = 1;
322 | typeValid.before = _.isObject(query);
323 | next();
324 | });
325 |
326 | this.after('read', function(data, next) {
327 | ran.after = 2;
328 | typeValid.after = _.isArray(data);
329 | next();
330 | });
331 |
332 | this.receive('read', function(data) {
333 | ran.receive = 3;
334 | typeValid.receive = _.isArray(data);
335 | });
336 |
337 | });
338 |
339 | Model.read({
340 | 'someProperty': 'someValue',
341 | }, function(err, data) {
342 | ran.callback = 4;
343 | typeValid.callback = _.isArray(data);
344 | });
345 |
346 | assert.equal(ran.before, 1, 'read before ran');
347 | assert.equal(ran.after, 2, 'read after ran');
348 | assert.equal(ran.receive, 3, 'read receive ran');
349 | assert.equal(ran.callback, 4, 'read callback ran');
350 |
351 | assert.ok(typeValid.before, 'read before has valid data type');
352 | assert.ok(typeValid.after, 'read after has valid data type');
353 | assert.ok(typeValid.receive, 'read receive has valid data type');
354 | assert.ok(typeValid.callback, 'read callback has valid data type');
355 |
356 | });
357 |
358 | QUnit.test('READONE - before, after, receive, and callback events are given the correct context', function(assert) {
359 |
360 | var context = {};
361 | context.before = false;
362 | context.after = false;
363 | context.receive = false;
364 | context.callback = false;
365 |
366 | var name = uuid.v4();
367 |
368 | var contextCheck = uuid.v4();
369 |
370 | var Model = marilyn.model(name, function() {
371 |
372 | this.contextCheck = contextCheck;
373 |
374 | this.before('readOne', function(data, next) {
375 | context.before = (this.contextCheck === contextCheck);
376 | next();
377 | });
378 |
379 | this.after('readOne', function(data, next) {
380 | context.after = (this.contextCheck === contextCheck);
381 | next();
382 | });
383 |
384 | this.receive('readOne', function(data) {
385 | context.receive = (this.contextCheck === contextCheck);
386 | });
387 |
388 | });
389 |
390 | Model.create({
391 | 'id': 1,
392 | 'someProperty': 'someValue',
393 | }, function(err, data) {
394 |
395 | Model.readOne({
396 | 'id': 1
397 | }, function(err, data) {
398 | context.callback = (this.contextCheck === contextCheck);
399 | });
400 |
401 | });
402 |
403 | assert.ok(context.before, 'readOne before context');
404 | assert.ok(context.after, 'readOne after context');
405 | assert.ok(context.receive, 'readOne receive context');
406 | assert.ok(context.callback, 'readOne callback context');
407 |
408 | });
409 |
410 | QUnit.test('READONE - before receive, after, and callback events run when reading all and have valid data types', function(assert) {
411 |
412 | var ran = {};
413 | ran.before = 0;
414 | ran.receive = 0;
415 | ran.after = 0;
416 | ran.callback = 0;
417 |
418 | var typeValid = {};
419 | typeValid.before = false;
420 | typeValid.after = false;
421 | typeValid.receive = false;
422 | typeValid.callback = false;
423 |
424 | var name = uuid.v4();
425 |
426 | var Model = marilyn.model(name, function() {
427 |
428 | this.before('readOne', function(query, next) {
429 | ran.before = 1;
430 | typeValid.before = _.isObject(query);
431 | next();
432 | });
433 |
434 | this.after('readOne', function(data, next) {
435 | ran.after = 2;
436 | typeValid.after = _.isObject(data);
437 | next();
438 | });
439 |
440 | this.receive('readOne', function(data) {
441 | ran.receive = 3;
442 | typeValid.receive = _.isObject(data);
443 | });
444 |
445 | });
446 |
447 | Model.create({
448 | 'id': 1,
449 | 'someProperty': 'someValue',
450 | }, function(err, data) {
451 |
452 | Model.readOne({
453 | 'id': 1
454 | }, function(err, data) {
455 | ran.callback = 4;
456 | typeValid.callback = _.isObject(data);
457 | });
458 |
459 | });
460 |
461 | assert.equal(ran.before, 1, 'readOne before ran');
462 | assert.equal(ran.after, 2, 'readOne after ran');
463 | assert.equal(ran.receive, 3, 'readOne receive ran');
464 | assert.equal(ran.callback, 4, 'readOne callback ran');
465 |
466 | assert.ok(typeValid.before, 'readOne before has valid data type');
467 | assert.ok(typeValid.after, 'readOne after has valid data type');
468 | assert.ok(typeValid.receive, 'readOne receive has valid data type');
469 | assert.ok(typeValid.callback, 'readOne callback has valid data type');
470 |
471 | });
472 |
473 | /** UPDATE */
474 |
475 | QUnit.test('UPDATE - before, after, receive, and callback events are given the correct context', function(assert) {
476 |
477 | var context = {};
478 | context.before = false;
479 | context.after = false;
480 | context.receive = false;
481 | context.callback = false;
482 |
483 | var name = uuid.v4();
484 |
485 | var contextCheck = uuid.v4();
486 |
487 | var Model = marilyn.model(name, function() {
488 |
489 | this.contextCheck = contextCheck;
490 |
491 | this.before('update', function(searchQuery, updateQuery, next) {
492 | context.before = (this.contextCheck === contextCheck);
493 | next();
494 | });
495 |
496 | this.after('update', function(data, next) {
497 | context.after = (this.contextCheck === contextCheck);
498 | next();
499 | });
500 |
501 | this.receive('update', function(data) {
502 | context.receive = (this.contextCheck === contextCheck);
503 | });
504 |
505 | });
506 |
507 | Model.create({
508 | 'id': 1,
509 | 'someProperty': 'someValue',
510 | }, function(err, data) {
511 |
512 | Model.update({
513 | 'id': 1,
514 | }, {
515 | 'title': 'Something',
516 | }, function(err, data) {
517 | context.callback = (this.contextCheck === contextCheck);
518 | });
519 |
520 | });
521 |
522 | assert.ok(context.before, 'update before context');
523 | assert.ok(context.after, 'update after context');
524 | assert.ok(context.receive, 'update receive context');
525 | assert.ok(context.callback, 'update callback context');
526 |
527 | });
528 |
529 | QUnit.test('CREATE - before, after, receive, and callback events are given the correct context', function(assert) {
530 |
531 | var context = {};
532 | context.before = false;
533 | context.after = false;
534 | context.receive = false;
535 | context.callback = false;
536 |
537 | var name = uuid.v4();
538 |
539 | var contextCheck = uuid.v4();
540 |
541 | var Model = marilyn.model(name, function() {
542 |
543 | this.contextCheck = contextCheck;
544 |
545 | this.before('create', function(data, next) {
546 | context.before = (this.contextCheck === contextCheck);
547 | next();
548 | });
549 |
550 | this.after('create', function(data, next) {
551 | context.after = (this.contextCheck === contextCheck);
552 | next();
553 | });
554 |
555 | this.receive('create', function(data) {
556 | context.receive = (this.contextCheck === contextCheck);
557 | });
558 |
559 | });
560 |
561 | Model.create({
562 | 'contextCheck': contextCheck,
563 | }, function(err, data) {
564 | context.callback = (this.contextCheck === contextCheck);
565 | });
566 |
567 | assert.ok(context.before, 'create before context');
568 | assert.ok(context.after, 'create after context');
569 | assert.ok(context.receive, 'create receive context');
570 | assert.ok(context.callback, 'create callback context');
571 |
572 | });
573 |
574 | QUnit.test('UPDATE - before, after, receive, and callback events are given the correct context when saving a new instance', function(assert) {
575 |
576 | var context = {};
577 | context.updateBefore = false;
578 | context.updateAfter = false;
579 | context.updateReceive = false;
580 | context.saveBefore = false;
581 | context.saveAfter = false;
582 | context.saveReceive = false;
583 | context.callback = false;
584 |
585 | var name = uuid.v4();
586 |
587 | var contextCheck = uuid.v4();
588 |
589 | var Model = marilyn.model(name, function() {
590 |
591 | this.contextCheck = contextCheck;
592 |
593 | this.before('update', function(searchQuery, updateQuery, next) {
594 | context.updateBefore = (this.contextCheck === contextCheck);
595 | next();
596 | });
597 |
598 | this.after('update', function(data, next) {
599 | context.updateAfter = (this.contextCheck === contextCheck);
600 | next();
601 | });
602 |
603 | this.receive('update', function(data) {
604 | context.updateReceive = (this.contextCheck === contextCheck);
605 | });
606 |
607 | this.before('save', function(data, next) {
608 | context.saveBefore = (this.contextCheck === contextCheck);
609 | next();
610 | });
611 |
612 | this.after('save', function(data, next) {
613 | context.saveAfter = (this.contextCheck === contextCheck);
614 | next();
615 | });
616 |
617 | this.receive('save', function(data) {
618 | context.saveReceive = (this.contextCheck === contextCheck);
619 | });
620 |
621 | });
622 |
623 | Model.create({
624 | 'id': 1,
625 | 'someProperty': 'someValue',
626 | }, function(err, data) {
627 |
628 | data.title = 'Something';
629 | data.someProperty = 'someValue';
630 |
631 | data.save(function(err, data) {
632 | context.callback = (this.contextCheck === contextCheck);
633 | });
634 |
635 | });
636 |
637 | assert.ok(context.updateBefore, 'new update before context');
638 | assert.ok(context.updateAfter, 'new update after context');
639 | assert.ok(context.updateReceive, 'new update receive context');
640 | assert.ok(context.saveBefore, 'new update before context');
641 | assert.ok(context.saveAfter, 'new update after context');
642 | assert.ok(context.saveReceive, 'new update receive context');
643 | assert.ok(context.callback, 'new update callback context');
644 |
645 | });
646 |
647 | QUnit.test('UPDATE - before, after, receive, and callback events run when updating and have valid data types', function(assert) {
648 |
649 | var ran = {};
650 | ran.before = 0;
651 | ran.receive = 0;
652 | ran.after = 0;
653 | ran.callback = 0;
654 |
655 | var typeValid = {};
656 | typeValid.before = false;
657 | typeValid.after = false;
658 | typeValid.receive = false;
659 | typeValid.callback = false;
660 |
661 | var name = uuid.v4();
662 |
663 | var Model = marilyn.model(name, function() {
664 |
665 | this.before('update', function(searchQuery, updateQuery, next) {
666 | ran.before = 1;
667 | typeValid.before = (_.isObject(searchQuery) && _.isObject(updateQuery));
668 | next();
669 | });
670 |
671 | this.after('update', function(data, next) {
672 | ran.after = 2;
673 | typeValid.after = _.isArray(data);
674 | next();
675 | });
676 |
677 | this.receive('update', function(data) {
678 | ran.receive = 3;
679 | typeValid.receive = _.isArray(data);
680 | });
681 |
682 | });
683 |
684 | Model.create({
685 | 'id': 1,
686 | 'someProperty': 'someValue',
687 | }, function(err, data) {
688 |
689 | Model.update({
690 | 'id': 1,
691 | }, {
692 | 'title': 'Something',
693 | }, function(err, data) {
694 | ran.callback = 4;
695 | typeValid.callback = _.isArray(data);
696 | });
697 |
698 | });
699 |
700 | assert.equal(ran.before, 1, 'update before ran');
701 | assert.equal(ran.after, 2, 'update after ran');
702 | assert.equal(ran.receive, 3, 'update receive ran');
703 | assert.equal(ran.callback, 4, 'update callback ran');
704 |
705 | assert.ok(typeValid.before, 'update before has valid data type');
706 | assert.ok(typeValid.after, 'update after has valid data type');
707 | assert.ok(typeValid.receive, 'update receive has valid data type');
708 | assert.ok(typeValid.callback, 'update callback has valid data type');
709 |
710 | });
711 |
712 | QUnit.test('UPDATE - before, after, receive, and callback events run when saving an existing instance and have valid data types', function(assert) {
713 |
714 | var ran = {};
715 | ran.updateBefore = 0;
716 | ran.updateReceive = 0;
717 | ran.updateAfter = 0;
718 | ran.saveBefore = 0;
719 | ran.saveReceive = 0;
720 | ran.saveAfter = 0;
721 | ran.callback = 0;
722 |
723 | var typeValid = {};
724 | typeValid.updateBefore = false;
725 | typeValid.updateAfter = false;
726 | typeValid.updateReceive = false;
727 | typeValid.saveBefore = false;
728 | typeValid.saveAfter = false;
729 | typeValid.saveReceive = false;
730 | typeValid.callback = false;
731 |
732 | var name = uuid.v4();
733 |
734 | var Model = marilyn.model(name, function() {
735 |
736 | this.before('save', function(data, next) {
737 | ran.saveBefore = 1;
738 | typeValid.saveBefore = _.isObject(data);
739 | next();
740 | });
741 |
742 | this.before('update', function(searchQuery, updateQuery, next) {
743 | ran.updateBefore = 2;
744 | typeValid.updateBefore = (_.isObject(searchQuery) && _.isObject(updateQuery));
745 | next();
746 | });
747 |
748 | this.after('update', function(data, next) {
749 | ran.updateAfter = 3;
750 | typeValid.updateAfter = _.isArray(data);
751 | next();
752 | });
753 |
754 | this.after('save', function(data, next) {
755 | ran.saveAfter = 4;
756 | typeValid.saveAfter = _.isObject(data);
757 | next();
758 | });
759 |
760 | this.receive('update', function(data) {
761 | ran.updateReceive = 5;
762 | typeValid.updateReceive = _.isArray(data);
763 | });
764 |
765 | this.receive('save', function(data) {
766 | ran.saveReceive = 6;
767 | typeValid.saveReceive = _.isObject(data);
768 | });
769 |
770 | });
771 |
772 | Model.create({
773 | 'id': 1,
774 | 'someProperty': 'someValue',
775 | }, function(err, data) {
776 |
777 | data.title = 'Something';
778 | data.someProperty = 'someValue';
779 |
780 | data.save(function(err, data) {
781 | ran.callback = 7;
782 | typeValid.callback = _.isObject(data);
783 | });
784 |
785 | });
786 |
787 | assert.equal(ran.updateBefore, 2, 'update new instance before ran');
788 | assert.equal(ran.updateAfter, 3, 'update new instance after ran');
789 | assert.equal(ran.updateReceive, 5, 'update new instance receive ran');
790 | assert.equal(ran.saveBefore, 1, 'update new instance before ran');
791 | assert.equal(ran.saveAfter, 4, 'update new instance after ran');
792 | assert.equal(ran.saveReceive, 6, 'update new instance receive ran');
793 | assert.equal(ran.callback, 7, 'update new instance callback ran');
794 |
795 | assert.ok(typeValid.updateBefore, 'update new instance before has valid data type');
796 | assert.ok(typeValid.updateAfter, 'update new instance after has valid data type');
797 | assert.ok(typeValid.updateReceive, 'update new instance receive has valid data type');
798 | assert.ok(typeValid.saveBefore, 'update new instance before has valid data type');
799 | assert.ok(typeValid.saveAfter, 'update new instance after has valid data type');
800 | assert.ok(typeValid.saveReceive, 'update new instance receive has valid data type');
801 | assert.ok(typeValid.callback, 'update new instance callback has valid data type');
802 |
803 | });
804 |
805 | /** DELETE */
806 |
807 | QUnit.test('DELETE - before, after, receive, and callback events are given the correct context', function(assert) {
808 |
809 | var context = {};
810 | context.before = false;
811 | context.after = false;
812 | context.receive = false;
813 | context.callback = false;
814 |
815 | var name = uuid.v4();
816 |
817 | var contextCheck = uuid.v4();
818 |
819 | var Model = marilyn.model(name, function() {
820 |
821 | this.contextCheck = contextCheck;
822 |
823 | this.before('delete', function(data, next) {
824 | context.before = (this.contextCheck === contextCheck);
825 | next();
826 | });
827 |
828 | this.after('delete', function(data, next) {
829 | context.after = (this.contextCheck === contextCheck);
830 | next();
831 | });
832 |
833 | this.receive('delete', function(data) {
834 | context.receive = (this.contextCheck === contextCheck);
835 | });
836 |
837 | });
838 |
839 | Model.create({
840 | 'id': 1,
841 | 'someProperty': 'someValue',
842 | }, function(err, data) {
843 |
844 | Model.del({
845 | 'id': 1
846 | }, function(err, results) {
847 | context.callback = (this.contextCheck === contextCheck);
848 | });
849 |
850 | });
851 |
852 | assert.ok(context.before, 'delete before context');
853 | assert.ok(context.after, 'delete after context');
854 | assert.ok(context.receive, 'delete receive context');
855 | assert.ok(context.callback, 'delete callback context');
856 |
857 | });
858 |
859 | QUnit.test('DELETE - before, after, receive, and callback events run when deleting and have valid data types', function(assert) {
860 |
861 | var ran = {};
862 | ran.before = 0;
863 | ran.receive = 0;
864 | ran.after = 0;
865 | ran.callback = 0;
866 |
867 | var typeValid = {};
868 | typeValid.before = false;
869 | typeValid.after = false;
870 | typeValid.receive = false;
871 | typeValid.callback = false;
872 |
873 | var name = uuid.v4();
874 |
875 | var Model = marilyn.model(name, function() {
876 |
877 | this.before('delete', function(query, next) {
878 | ran.before = 1;
879 | typeValid.before = _.isObject(query);
880 | next();
881 | });
882 |
883 | this.after('delete', function(data, next) {
884 | ran.after = 2;
885 | typeValid.after = _.isArray(data);
886 | next();
887 | });
888 |
889 | this.receive('delete', function(data) {
890 | ran.receive = 3;
891 | typeValid.receive = _.isArray(data);
892 | });
893 |
894 | });
895 |
896 | Model.create({
897 | 'id': 1,
898 | 'someProperty': 'someValue',
899 | }, function(err, data) {
900 |
901 | Model.del({
902 | 'id': 1
903 | }, function(err, results) {
904 | ran.callback = 4;
905 | typeValid.callback = _.isArray(results);
906 | });
907 |
908 | });
909 |
910 | assert.equal(ran.before, 1, 'delete before ran');
911 | assert.equal(ran.after, 2, 'delete after ran');
912 | assert.equal(ran.receive, 3, 'delete receive ran');
913 | assert.equal(ran.callback, 4, 'delete callback ran');
914 |
915 | assert.ok(typeValid.before, 'delete before has valid data type');
916 | assert.ok(typeValid.after, 'delete after has valid data type');
917 | assert.ok(typeValid.receive, 'delete receive has valid data type');
918 | assert.ok(typeValid.callback, 'delete callback has valid data type');
919 |
920 | });
921 |
922 | QUnit.test('DELETE - before, after, and receive events run when calling instance delete method and have valid data types', function(assert) {
923 |
924 | var ran = {};
925 | ran.before = 0;
926 | ran.receive = 0;
927 | ran.after = 0;
928 | ran.callback = 0;
929 |
930 | var typeValid = {};
931 | typeValid.before = false;
932 | typeValid.after = false;
933 | typeValid.receive = false;
934 | typeValid.callback = false;
935 |
936 | var name = uuid.v4();
937 |
938 | var Model = marilyn.model(name, function() {
939 |
940 | this.before('delete', function(query, next) {
941 | ran.before = 1;
942 | typeValid.before = _.isObject(query);
943 | next();
944 | });
945 |
946 | this.after('delete', function(data, next) {
947 | ran.after = 2;
948 | typeValid.after = _.isArray(data);
949 | next();
950 | });
951 |
952 | this.receive('delete', function(data) {
953 | ran.receive = 3;
954 | typeValid.receive = _.isArray(data);
955 | });
956 |
957 | });
958 |
959 | Model.create({
960 | 'id': 1,
961 | 'someProperty': 'someValue',
962 | }, function(err, data) {
963 |
964 | data.delete(function(err, results) {
965 | ran.callback = 4;
966 | typeValid.callback = _.isArray(results);
967 | });
968 |
969 | });
970 |
971 | assert.equal(ran.before, 1, 'delete before ran');
972 | assert.equal(ran.after, 2, 'delete after ran');
973 | assert.equal(ran.receive, 3, 'delete receive ran');
974 | assert.equal(ran.callback, 4, 'delete callback ran');
975 |
976 | assert.ok(typeValid.before, 'delete before has valid data type');
977 | assert.ok(typeValid.after, 'delete after has valid data type');
978 | assert.ok(typeValid.receive, 'delete receive has valid data type');
979 | assert.ok(typeValid.callback, 'delete callback has valid data type');
980 |
981 | });
982 |
983 | /** MULTIPLE EVENTS */
984 |
985 | QUnit.test('MULTIPLE - before, after, and receive, events run when passed for multiple event types and have valid data types', function(assert) {
986 |
987 | var ran = {};
988 | ran.before = 0;
989 | ran.receive = 0;
990 | ran.after = 0;
991 |
992 | var name = uuid.v4();
993 |
994 | var Model = marilyn.model(name, function() {
995 |
996 | // updates can't be tested for because it's callback has a different signature
997 |
998 | this.before(['create', 'read', 'readOne', 'delete'], function(data, next) {
999 | ran.before++;
1000 | next();
1001 | });
1002 |
1003 | this.after(['create', 'read', 'readOne', 'delete'], function(data, next) {
1004 | ran.after++;
1005 | next();
1006 | });
1007 |
1008 | this.receive(['create', 'read', 'readOne', 'delete'], function(data) {
1009 | ran.receive++;
1010 | });
1011 |
1012 | });
1013 |
1014 | Model.create({
1015 | 'id': 1,
1016 | 'someProperty': 'someValue',
1017 | }, function(err, data) {
1018 |
1019 | Model.read({});
1020 |
1021 | Model.readOne({
1022 | 'id': 1
1023 | });
1024 |
1025 | Model.del({
1026 | 'id': 1
1027 | });
1028 |
1029 | });
1030 |
1031 | assert.equal(ran.before, 4, 'multiple before ran');
1032 | assert.equal(ran.after, 4, 'multiple after ran');
1033 | assert.equal(ran.receive, 4, 'multiple receive ran');
1034 |
1035 | });
1036 |
1037 | /** PLUGINS */
1038 |
1039 | QUnit.test('PLUGINS - plugin functions are called', function(assert) {
1040 |
1041 | var called = false;
1042 |
1043 | var name = uuid.v4();
1044 |
1045 | var Model = marilyn.model(name);
1046 |
1047 | Model.use(function() {
1048 | called = true;
1049 | });
1050 |
1051 | assert.ok(called, 'plugin function was called');
1052 |
1053 | });
1054 |
1055 | QUnit.test('PLUGINS - plugin functions have the correct context', function(assert) {
1056 |
1057 | var called = false;
1058 |
1059 | var name = uuid.v4();
1060 | var randomTestingValue = uuid.v4();
1061 |
1062 | var Model = marilyn.model(name, function() {
1063 | this.testingPropery = randomTestingValue;
1064 | });
1065 |
1066 | Model.use(function() {
1067 | if (randomTestingValue === this.testingPropery) {
1068 | called = true;
1069 | }
1070 | });
1071 |
1072 | assert.ok(called, 'plugin function was called');
1073 |
1074 | });
--------------------------------------------------------------------------------