6bd3073f by Adam Heath

First pass of monkey-patching nested model support. No tests yet, but

it passes jshint and build targets.
1 parent 40915018
1 {
2 "directory": "src/lib"
3 }
1 .*.swp
2 .tmp/
3 bin/coverage/
4 dist/
5 node_modules/
6 src/lib/
7 .grunt/
8 _SpecRunner.html
This diff is collapsed. Click to expand it.
1 {
2 "name": "backbone-nested-models",
3 "version": "0.0.0",
4 "authors": [
5 "Adam Heath <doogie@brainfood.com>"
6 ],
7 "private": true,
8 "ignore": [
9 "**/.*",
10 "node_modules",
11 "src/lib",
12 "test"
13 ],
14 "dependencies": {
15 "underscore": "~1.6.0",
16 "backbone": "~1.1.0",
17 "backbone-validation": "0.9.1",
18 "requirejs": "~2.1.10"
19 }
20 }
1 {
2 "name": "backbone-nested-models",
3 "version": "0.0.0",
4 "main": [
5 "src/scripts/backbone-nested-models.js"
6 ],
7 "dependencies": {
8 "backbone": "~1.1.0",
9 "backbone-validation": "0.9.1",
10 "requirejs": "~2.1.10"
11 },
12 "devDependencies": {
13 "bower-requirejs": "~0.9.2",
14 "grunt": "~0.4.1",
15 "grunt-contrib-copy": "~0.4.1",
16 "grunt-contrib-concat": "~0.3.0",
17 "grunt-contrib-uglify": "~0.2.0",
18 "grunt-contrib-jshint": "~0.7.0",
19 "grunt-contrib-cssmin": "~0.7.0",
20 "grunt-contrib-connect": "~0.5.0",
21 "grunt-contrib-clean": "~0.5.0",
22 "grunt-contrib-htmlmin": "~0.1.3",
23 "grunt-bower-install": "~0.7.0",
24 "grunt-contrib-imagemin": "~0.2.0",
25 "grunt-contrib-watch": "~0.5.2",
26 "grunt-rev": "~0.1.0",
27 "grunt-autoprefixer": "~0.5.0",
28 "grunt-usemin": "~0.1.10",
29 "grunt-mocha": "~0.4.0",
30 "grunt-newer": "~0.6.0",
31 "grunt-svgmin": "~0.2.0",
32 "grunt-concurrent": "~0.4.0",
33 "load-grunt-tasks": "~0.2.0",
34 "time-grunt": "~0.2.0",
35 "jshint-stylish": "~0.1.3",
36 "grunt-contrib-requirejs": "~0.4.0",
37 "grunt-bower-requirejs": "~0.8.4",
38 "grunt-template-jasmine-istanbul": "~0.2.6",
39 "grunt-template-jasmine-requirejs": "~0.1.10",
40 "grunt-contrib-jasmine": "~0.5.3"
41 },
42 "engines": {
43 "node": ">=0.8.0"
44 }
45 }
46
1 define(
2 [
3 'underscore',
4 'backbone',
5 'backbone-validation',
6 ],
7 function(
8 _,
9 Backbone
10 ) {
11 'use strict';
12
13 function validateNestedValue(attrValue, attrName) {
14 attrValue.validate();
15 var isValid = attrValue.isValid();
16 return isValid ? null : (attrName + ' is invalid');
17 }
18
19 function updateValidation(model) {
20 var oldValidation = model.validation;
21 var allKeys = _.uniq(model.keys().concat(_.keys(oldValidation)));
22 var validation = _.extend({}, oldValidation);
23 var found;
24 var f = function(value) {
25 if (value === validateNestedValue) {
26 found = true;
27 }
28 };
29 for (var i = 0; i < allKeys.length; i++) {
30 var key = allKeys[i];
31 var value = model.get(key);
32 var validators = validation[key];
33 if (validators) {
34 if(_.isArray(validators)) {
35 validators = validators.concat();
36 } else {
37 validators = [validators];
38 }
39 } else {
40 validators = [];
41 }
42 validation[key] = validators;
43 if (value instanceof Backbone.Model) {
44 found = false;
45 _.each(validators, f);
46 if (!found) {
47 validators.push(validateNestedValue);
48 }
49 }
50 }
51 model.validation = validation;
52 return oldValidation;
53 }
54
55 function wrapValidationFunction(modelClass, methodName) {
56 var originalMethod = modelClass.prototype[methodName];
57 modelClass.prototype[methodName] = function() {
58 var oldValidation = updateValidation(this);
59 try {
60 if (originalMethod) {
61 return originalMethod.apply(this, arguments);
62 } else {
63 return modelClass.__super__[methodName].apply(this, arguments);
64 }
65 } finally {
66 this.validation = oldValidation;
67 }
68 };
69 }
70
71 function wrapSetFunction(modelClass) {
72 var originalMethod = modelClass.prototype.set;
73 modelClass.prototype.set = function(key, val, options) {
74 var attr, attrs, curVal, nestedOptions, newVal;
75 if (key === null) {
76 return this;
77 }
78
79 if (typeof key === 'object') {
80 attrs = key;
81 options = val;
82 } else {
83 (attrs = {})[key] = val;
84 }
85 if (options && options.merge) {
86 nestedOptions = {silent: false, merge: true};
87 for (attr in attrs) {
88 curVal = this.get(attr);
89 newVal = attrs[attr];
90 if (curVal instanceof Backbone.Model && newVal instanceof Backbone.Model) {
91 delete attrs[attr];
92 curVal.set(newVal.attributes, nestedOptions);
93 }
94 }
95 }
96 if (originalMethod) {
97 return originalMethod.call(this, attrs, options);
98 } else {
99 return modelClass.__super__.set.call(this, attrs, options);
100 }
101 };
102 }
103
104 function wrapToJSONFunction(modelClass) {
105 var originalMethod = modelClass.prototype.toJSON;
106 modelClass.prototype.toJSON = function(options) {
107 var result;
108 if (originalMethod) {
109 result = originalMethod.apply(this, arguments);
110 } else {
111 result = modelClass.__super__.toJSON.apply(this, arguments);
112 }
113 if (options && options.deep) {
114 _.each(result, function(value, key) {
115 if (value instanceof Backbone.Model) {
116 result[key] = value.toJSON(options);
117 }
118 });
119 }
120 return result;
121 };
122 }
123
124 var NestedModels = {
125 validateNestedValue: validateNestedValue,
126
127 wrapSetFunction: wrapSetFunction,
128 wrapToJSONFunction: wrapToJSONFunction,
129 wrapValidationFunction: wrapValidationFunction,
130
131 mixin: function(modelClass) {
132 wrapSetFunction(modelClass);
133 wrapToJSONFunction(modelClass);
134 wrapValidationFunction(modelClass, 'isValid');
135 wrapValidationFunction(modelClass, 'validate');
136 wrapValidationFunction(modelClass, 'preValidate');
137 return modelClass;
138 },
139 };
140
141 return NestedModels;
142
143 }
144 );
1 /* global require:true */
2 var require;
3 require = (function() {
4 'use strict';
5
6 var require = {
7 baseUrl: 'scripts',
8 shim: {
9
10 },
11 paths: {
12 'backbone-validation': '../lib/backbone-validation/dist/backbone-validation-amd',
13 backbone: '../lib/backbone/backbone',
14 underscore: '../lib/underscore/underscore'
15 }
16 };
17
18 return require;
19 })();
1 define([], {});
1 /* global require */
2 require(
3 [],
4 function() {
5 'use strict';
6 }
7 );