Implement most of what we need; this isn't fully compatible yet, tho.
Showing
4 changed files
with
217 additions
and
8 deletions
... | @@ -2,10 +2,92 @@ define(function(require) { | ... | @@ -2,10 +2,92 @@ define(function(require) { |
2 | 'use strict'; | 2 | 'use strict'; |
3 | var module = require('module'); | 3 | var module = require('module'); |
4 | var Backbone = require('backbone'); | 4 | var Backbone = require('backbone'); |
5 | var _ = require('underscore'); | ||
5 | 6 | ||
6 | var BackboneModelOverlay = Backbone.Model.extend({ | 7 | var BackboneModelOverlay = Backbone.Model.extend({ |
7 | initialize: function(data, options) { | 8 | initialize: function(data, options) { |
8 | BackboneModelOverlay.__super__.initialize.apply(this, arguments); | 9 | BackboneModelOverlay.__super__.initialize.apply(this, arguments); |
10 | var parent = this._parent = options.parent; | ||
11 | if (parent) { | ||
12 | parent.on('change', function(value, parentModel, options) { | ||
13 | var parentChanged = this._parent.changedAttributes(); | ||
14 | this.changed = parentChanged; | ||
15 | _.each(parentChanged, function(value, key) { | ||
16 | this.trigger('change:' + key, value, this, options); | ||
17 | }, this); | ||
18 | this.trigger('change', this, options); | ||
19 | }, this); | ||
20 | } | ||
21 | var override = this._override = {}; | ||
22 | _.each(_.keys(this.attributes), function(key) { | ||
23 | override[key] = true; | ||
24 | }); | ||
25 | }, | ||
26 | get: function(key) { | ||
27 | if (this._override[key]) { | ||
28 | return BackboneModelOverlay.__super__.get.apply(this, arguments); | ||
29 | } else { | ||
30 | var parent = this._parent; | ||
31 | return parent && parent.get.apply(parent, arguments); | ||
32 | } | ||
33 | }, | ||
34 | previous: function(attr) { | ||
35 | return this.previousAttributes()[attr]; | ||
36 | }, | ||
37 | previousAttributes: function() { | ||
38 | var parent = this._parent; | ||
39 | var data = BackboneModelOverlay.__super__.previousAttributes.apply(this, arguments); | ||
40 | var parentData = parent ? parent.previousAttributes.apply(parent, arguments) : null; | ||
41 | return _.extend({}, parentData, data); | ||
42 | }, | ||
43 | changedAttributes: function() { | ||
44 | var parent = this._parent; | ||
45 | var data = BackboneModelOverlay.__super__.changedAttributes.apply(this, arguments); | ||
46 | var parentData = parent ? parent.changedAttributes.apply(parent, arguments) : null; | ||
47 | return _.extend({}, parentData, data); | ||
48 | }, | ||
49 | set: function(key, value, options) { | ||
50 | if (!!!key) { | ||
51 | return this; | ||
52 | } | ||
53 | var attrs; | ||
54 | if (typeof key === 'object') { | ||
55 | attrs = key; | ||
56 | options = value; | ||
57 | } else { | ||
58 | (attrs = {})[key] = value; | ||
59 | } | ||
60 | |||
61 | options || (options = {}); | ||
62 | var unset = options.unset; | ||
63 | |||
64 | var parent = this._parent; | ||
65 | if (parent) { | ||
66 | if (!options.overlay) { | ||
67 | return parent.set.apply(this, arguments); | ||
68 | } | ||
69 | var override = this._override; | ||
70 | _.each(attrs, function(value, key) { | ||
71 | if (unset) { | ||
72 | delete override[key]; | ||
73 | } else { | ||
74 | override[key] = true; | ||
75 | } | ||
76 | }); | ||
77 | } | ||
78 | return BackboneModelOverlay.__super__.set.apply(this, arguments); | ||
79 | }, | ||
80 | toJSON: function(options) { | ||
81 | var data = BackboneModelOverlay.__super__.toJSON.apply(this, arguments); | ||
82 | var parent = this._parent; | ||
83 | if (parent) { | ||
84 | data = _.extend(data, parent.toJSON.apply(parent, arguments)); | ||
85 | } | ||
86 | var superGet = BackboneModelOverlay.__super__.get; | ||
87 | _.each(this._override, function(value, key) { | ||
88 | data[key] = superGet.call(this, key, options); | ||
89 | }, this); | ||
90 | return data; | ||
9 | }, | 91 | }, |
10 | }); | 92 | }); |
11 | return BackboneModelOverlay; | 93 | return BackboneModelOverlay; | ... | ... |
... | @@ -8,22 +8,149 @@ define(function(require) { | ... | @@ -8,22 +8,149 @@ define(function(require) { |
8 | var BackboneModelOverlay = require('backbone-model-overlay'); | 8 | var BackboneModelOverlay = require('backbone-model-overlay'); |
9 | 9 | ||
10 | describe('BackboneModelOverlay', function() { | 10 | describe('BackboneModelOverlay', function() { |
11 | var staticParentData = {inParent: 'Parent'}; | ||
12 | var staticChildData = {inChild: 'Child'}; | ||
11 | it('exists', function() { | 13 | it('exists', function() { |
12 | expect(BackboneModelOverlay).toBeDefined(); | 14 | expect(BackboneModelOverlay).toBeDefined(); |
13 | }); | 15 | }); |
16 | var model, parent; | ||
17 | var attributes; | ||
18 | var perAttributeChange; | ||
19 | var previous; | ||
20 | function doBeforeEach(parentData, childData) { | ||
21 | var options; | ||
22 | if (parentData) { | ||
23 | parent = new Backbone.Model(parentData); | ||
24 | options = {parent: parent}; | ||
25 | } else { | ||
26 | parent = null; | ||
27 | } | ||
28 | model = new BackboneModelOverlay(childData, options); | ||
29 | attributes = []; | ||
30 | perAttributeChange = {}; | ||
31 | previous = []; | ||
32 | model.on('change', function() { | ||
33 | attributes.push(model.toJSON()); | ||
34 | previous.push(model.previousAttributes()); | ||
35 | _.each(model.changedAttributes(), function(value, key) { | ||
36 | if (perAttributeChange[key] === undefined) { | ||
37 | perAttributeChange[key] = [value]; | ||
38 | } else { | ||
39 | perAttributeChange[key].push(value); | ||
40 | } | ||
14 | }); | 41 | }); |
15 | describe('BackboneModelOverlay', function() { | ||
16 | var model; | ||
17 | beforeEach(function() { | ||
18 | model = new BackboneModelOverlay(); | ||
19 | }); | 42 | }); |
43 | } | ||
44 | describe('no-parent', function() { | ||
20 | describe('empty', function() { | 45 | describe('empty', function() { |
46 | beforeEach(function() { | ||
47 | doBeforeEach(); | ||
48 | }); | ||
21 | it('get', function() { | 49 | it('get', function() { |
50 | expect(perAttributeChange).toEqual({}); | ||
51 | expect(previous).toEqual([]); | ||
52 | expect(attributes).toEqual([]); | ||
22 | expect(model.get('missing')).toBeUndefined(); | 53 | expect(model.get('missing')).toBeUndefined(); |
23 | }); | ||
24 | it('toJSON', function() { | ||
25 | expect(model.toJSON()).toEqual({}); | 54 | expect(model.toJSON()).toEqual({}); |
26 | }); | 55 | }); |
27 | }); | 56 | }); |
57 | describe('baseline', function() { | ||
58 | beforeEach(function() { | ||
59 | doBeforeEach(null, staticChildData); | ||
60 | model.set('newChild', 'OldChild', {overlay: true}); | ||
61 | model.set('newChild', 'NewChild', {overlay: true}); | ||
62 | }); | ||
63 | it('get', function() { | ||
64 | expect(perAttributeChange).toEqual({ | ||
65 | newChild: ['OldChild', 'NewChild'], | ||
66 | }); | ||
67 | expect(previous).toEqual([ | ||
68 | {inChild: 'Child'}, | ||
69 | {inChild: 'Child', newChild: 'OldChild'}, | ||
70 | ]); | ||
71 | expect(attributes).toEqual([ | ||
72 | {inChild: 'Child', newChild: 'OldChild'}, | ||
73 | {inChild: 'Child', newChild: 'NewChild'}, | ||
74 | ]); | ||
75 | expect(model.get('missing')).toBeUndefined(); | ||
76 | expect(model.toJSON()).toEqual({inChild: 'Child', newChild: 'NewChild'}); | ||
77 | }); | ||
78 | }); | ||
79 | }); | ||
80 | describe('with-parent', function() { | ||
81 | beforeEach(function() { | ||
82 | doBeforeEach(staticParentData, staticChildData); | ||
83 | }); | ||
84 | describe('initial', function() { | ||
85 | it('get', function() { | ||
86 | expect(parent.get('missing')).toBeUndefined(); | ||
87 | expect(model.get('missing')).toBeUndefined(); | ||
88 | |||
89 | expect(parent.get('inParent')).toEqual('Parent'); | ||
90 | expect(model.get('inParent')).toEqual('Parent'); | ||
91 | |||
92 | expect(parent.get('inChild')).toBeUndefined(); | ||
93 | expect(model.get('inChild')).toEqual('Child'); | ||
94 | |||
95 | expect(attributes).toEqual([]); | ||
96 | expect(model.toJSON()).toEqual({inParent: 'Parent', inChild: 'Child'}); | ||
97 | }); | ||
98 | }); | ||
99 | describe('add', function() { | ||
100 | beforeEach(function() { | ||
101 | parent.set('newParent', 'OldParent'); | ||
102 | model.set('newChild', 'OldChild', {overlay: true}); | ||
103 | parent.set('newParent', 'NewParent'); | ||
104 | model.set('newChild', 'NewChild', {overlay: true}); | ||
105 | model.set('passThruParent', 'PassThruParent'); | ||
106 | }); | ||
107 | it('get:perAttributeChange', function() { | ||
108 | expect(perAttributeChange).toEqual({ | ||
109 | newParent: ['OldParent', 'OldParent', 'NewParent', 'NewParent', 'NewParent'], | ||
110 | newChild: ['OldChild', 'NewChild'], | ||
111 | passThruParent: ['PassThruParent'], | ||
112 | }); | ||
113 | }); | ||
114 | it('get:previous', function() { | ||
115 | _.each(previous, function(v) { | ||
116 | //console.log('111', JSON.stringify(v)); | ||
117 | }); | ||
118 | expect(previous).toEqual([ | ||
119 | {inParent: 'Parent'}, | ||
120 | {inParent: 'Parent', inChild: 'Child'}, | ||
121 | {inParent: 'Parent', newParent: 'OldParent', inChild: 'Child'}, | ||
122 | {inParent: 'Parent', newParent: 'OldParent', inChild: 'Child', newChild: 'OldChild'}, | ||
123 | {inParent: 'Parent', newParent: 'OldParent', inChild: 'Child', newChild: 'NewChild'}, | ||
124 | ]); | ||
125 | }); | ||
126 | it('get:attributes', function() { | ||
127 | expect(attributes).toEqual([ | ||
128 | {inParent: 'Parent', newParent: 'OldParent', inChild: 'Child'}, | ||
129 | {inParent: 'Parent', newParent: 'OldParent', inChild: 'Child', newChild: 'OldChild'}, | ||
130 | {inParent: 'Parent', newParent: 'NewParent', inChild: 'Child', newChild: 'OldChild'}, | ||
131 | {inParent: 'Parent', newParent: 'NewParent', inChild: 'Child', newChild: 'NewChild'}, | ||
132 | {inParent: 'Parent', newParent: 'NewParent', inChild: 'Child', newChild: 'NewChild', passThruParent: 'PassThruParent'}, | ||
133 | ]); | ||
134 | }); | ||
135 | it('get:parent:separate-checks', function() { | ||
136 | expect(parent.get('missing')).toBeUndefined(); | ||
137 | expect(model.get('missing')).toBeUndefined(); | ||
138 | |||
139 | expect(parent.get('inParent')).toEqual('Parent'); | ||
140 | expect(parent.get('newParent')).toEqual('NewParent'); | ||
141 | expect(model.get('inParent')).toEqual('Parent'); | ||
142 | expect(model.get('newParent')).toEqual('NewParent'); | ||
143 | }); | ||
144 | it('get:child:separate-checks:1', function() { | ||
145 | expect(parent.get('inChild')).toBeUndefined(); | ||
146 | expect(parent.get('newChild')).toBeUndefined(); | ||
147 | expect(model.get('inChild')).toEqual('Child'); | ||
148 | }); | ||
149 | it('get:child:separate-checks:2', function() { | ||
150 | expect(model.get('newChild')).toEqual('NewChild'); | ||
151 | expect(model.toJSON()).toEqual({inParent: 'Parent', newParent: 'NewParent', inChild: 'Child', newChild: 'NewChild', passThruParent: 'PassThruParent'}); | ||
152 | }); | ||
153 | }); | ||
154 | }); | ||
28 | }); | 155 | }); |
29 | }); | 156 | }); | ... | ... |
-
Please register or sign in to post a comment