First pass of monkey-patching nested model support. No tests yet, but
it passes jshint and build targets.
Showing
9 changed files
with
248 additions
and
0 deletions
.gitignore
0 → 100644
Gruntfile.js
0 → 100644
This diff is collapsed.
Click to expand it.
bower.json
0 → 100644
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 | } |
package.json
0 → 100644
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 |
src/scripts/backbone-nested-models.js
0 → 100644
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 | ); |
src/scripts/config.js
0 → 100644
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 | })(); |
src/scripts/jquery.js
0 → 100644
1 | define([], {}); |
-
Please register or sign in to post a comment