alpha-simprini-engine 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +38 -0
  4. data/app/assets/javascripts/alpha_simprini.coffee +15 -0
  5. data/app/assets/javascripts/alpha_simprini/application.coffee +22 -0
  6. data/app/assets/javascripts/alpha_simprini/coffee_kup/binding.coffee +119 -0
  7. data/app/assets/javascripts/alpha_simprini/coffee_kup/helpers.coffee +17 -0
  8. data/app/assets/javascripts/alpha_simprini/coffee_kup/tags.coffee +7 -0
  9. data/app/assets/javascripts/alpha_simprini/collection.coffee +62 -0
  10. data/app/assets/javascripts/alpha_simprini/delegate.coffee +21 -0
  11. data/app/assets/javascripts/alpha_simprini/event.coffee +75 -0
  12. data/app/assets/javascripts/alpha_simprini/html.coffee +56 -0
  13. data/app/assets/javascripts/alpha_simprini/mixin.coffee +10 -0
  14. data/app/assets/javascripts/alpha_simprini/model.coffee +137 -0
  15. data/app/assets/javascripts/alpha_simprini/models/radio_selection_model.coffee +9 -0
  16. data/app/assets/javascripts/alpha_simprini/packer.coffee +93 -0
  17. data/app/assets/javascripts/alpha_simprini/template_helpers.coffee +8 -0
  18. data/app/assets/javascripts/alpha_simprini/view.coffee +182 -0
  19. data/app/assets/javascripts/alpha_simprini/views/canvas.coffee +2 -0
  20. data/app/assets/javascripts/alpha_simprini/views/horizontal_split.coffee +9 -0
  21. data/app/assets/javascripts/alpha_simprini/views/panel.coffee +2 -0
  22. data/app/assets/javascripts/alpha_simprini/views/splitter.coffee +2 -0
  23. data/app/assets/javascripts/alpha_simprini/views/stage.coffee +7 -0
  24. data/app/assets/javascripts/alpha_simprini/views/vertical_split.coffee +8 -0
  25. data/app/assets/javascripts/alpha_simprini/views/viewport.coffee +0 -0
  26. data/app/assets/javascripts/bundle.js +16416 -0
  27. data/app/assets/javascripts/lib/backbone.coffee +11 -0
  28. data/app/assets/javascripts/lib/koffeecup_backbone_databind.coffee +25 -0
  29. data/app/assets/javascripts/lib/module.coffee +15 -0
  30. data/app/assets/javascripts/lib/state_machine.coffee +33 -0
  31. data/app/assets/javascripts/lib/string.coffee +13 -0
  32. data/app/assets/javascripts/qunit.js +1513 -0
  33. data/app/assets/javascripts/test.coffee +4 -0
  34. data/app/assets/javascripts/test/alpha_simprini/coffee_kup/binding.coffee +118 -0
  35. data/app/assets/javascripts/test/alpha_simprini/coffee_kup/helpers.coffee +10 -0
  36. data/app/assets/javascripts/test/alpha_simprini/coffee_kup/tags.coffee +18 -0
  37. data/app/assets/javascripts/test/alpha_simprini/collection.coffee +7 -0
  38. data/app/assets/javascripts/test/alpha_simprini/event.coffee +43 -0
  39. data/app/assets/javascripts/test/alpha_simprini/model.coffee +63 -0
  40. data/app/assets/javascripts/test/alpha_simprini/view.coffee +72 -0
  41. data/app/assets/javascripts/test/alpha_simprini/views/canvas.coffee +8 -0
  42. data/app/assets/javascripts/test/alpha_simprini/views/horizontal_split.coffee +13 -0
  43. data/app/assets/javascripts/test/alpha_simprini/views/vertical_split.coffee +13 -0
  44. data/app/assets/javascripts/test/backbone/collection.js +345 -0
  45. data/app/assets/javascripts/test/backbone/events.js +86 -0
  46. data/app/assets/javascripts/test/backbone/model.js +426 -0
  47. data/app/assets/javascripts/test/backbone/view.js +137 -0
  48. data/app/assets/stylesheets/bootstrap.sass +6 -0
  49. data/app/assets/stylesheets/paneling.sass +2 -0
  50. data/app/assets/stylesheets/qunit.scss +228 -0
  51. data/app/assets/stylesheets/table.sass +10 -0
  52. data/app/assets/stylesheets/typography.sass +19 -0
  53. data/app/controllers/alpha_simprini/test_controller.rb +3 -0
  54. data/app/controllers/resources_controller.rb +7 -0
  55. data/app/views/alpha_simprini/test/root.rb +13 -0
  56. data/app/views/resources/base.rb +2 -0
  57. data/app/views/resources/edit.rb +9 -0
  58. data/app/views/resources/form.rb +26 -0
  59. data/app/views/resources/index.rb +41 -0
  60. data/app/views/resources/new.rb +9 -0
  61. data/app/views/resources/show.rb +42 -0
  62. data/app/views/resources/show_has_many.rb +28 -0
  63. data/config/locales/en.yml +4 -0
  64. data/config/routes.rb +3 -0
  65. data/lib/alpha-simprini-engine.rb +33 -0
  66. data/lib/alpha-simprini-engine/version.rb +3 -0
  67. data/lib/alpha_simprini/directive_processor.rb +77 -0
  68. data/lib/alpha_simprini/packer.rb +161 -0
  69. data/lib/alpha_simprini/page.rb +37 -0
  70. data/lib/tasks/alpha-simprini-engine_tasks.rake +4 -0
  71. data/test/alpha-simprini-engine_test.rb +7 -0
  72. data/test/dummy/Rakefile +7 -0
  73. data/test/dummy/app/assets/javascripts/application.js +9 -0
  74. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  75. data/test/dummy/app/controllers/application_controller.rb +3 -0
  76. data/test/dummy/app/helpers/application_helper.rb +2 -0
  77. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  78. data/test/dummy/config.ru +4 -0
  79. data/test/dummy/config/application.rb +42 -0
  80. data/test/dummy/config/boot.rb +10 -0
  81. data/test/dummy/config/database.yml +25 -0
  82. data/test/dummy/config/environment.rb +5 -0
  83. data/test/dummy/config/environments/development.rb +27 -0
  84. data/test/dummy/config/environments/production.rb +51 -0
  85. data/test/dummy/config/environments/test.rb +39 -0
  86. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  87. data/test/dummy/config/initializers/inflections.rb +10 -0
  88. data/test/dummy/config/initializers/mime_types.rb +5 -0
  89. data/test/dummy/config/initializers/secret_token.rb +7 -0
  90. data/test/dummy/config/initializers/session_store.rb +8 -0
  91. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  92. data/test/dummy/config/locales/en.yml +4 -0
  93. data/test/dummy/config/routes.rb +58 -0
  94. data/test/dummy/db/development.sqlite3 +0 -0
  95. data/test/dummy/log/development.log +800 -0
  96. data/test/dummy/public/404.html +26 -0
  97. data/test/dummy/public/422.html +26 -0
  98. data/test/dummy/public/500.html +26 -0
  99. data/test/dummy/public/favicon.ico +0 -0
  100. data/test/dummy/script/rails +6 -0
  101. data/test/dummy/tmp/cache/assets/C24/D70/sprockets%2F388a550030239f3784f207fa32954f64 +0 -0
  102. data/test/dummy/tmp/cache/assets/C34/620/sprockets%2F097ee8532856d48d70420c006f475889 +0 -0
  103. data/test/dummy/tmp/cache/assets/C6F/710/sprockets%2F908d18b0f21c18dad508851126164a41 +0 -0
  104. data/test/dummy/tmp/cache/assets/CA7/120/sprockets%2F35278a6c0150c40394fe02629c8b0eb9 +0 -0
  105. data/test/dummy/tmp/cache/assets/CDB/A80/sprockets%2F443b1c6e71b482fdd83b1833b7e27712 +0 -0
  106. data/test/dummy/tmp/cache/assets/CE3/FB0/sprockets%2F6d3f87a4232e0368bb86760780c5f1bd +0 -0
  107. data/test/dummy/tmp/cache/assets/D1C/160/sprockets%2F87876f7e55ebc78ae11c6c08a27f0605 +0 -0
  108. data/test/dummy/tmp/cache/assets/D26/6C0/sprockets%2F65bce2254bb3140a50326ea1654ea5df +0 -0
  109. data/test/dummy/tmp/cache/assets/D26/FA0/sprockets%2F1f50f59148df5f3e2ab889df381d9744 +0 -0
  110. data/test/dummy/tmp/cache/assets/D36/220/sprockets%2F841654b01d89eada195d7d1fe10b57b0 +0 -0
  111. data/test/dummy/tmp/cache/assets/D47/410/sprockets%2Ff1fe319eb4064bd5b861ac28f7d76555 +18122 -0
  112. data/test/dummy/tmp/cache/assets/D57/1E0/sprockets%2Fe267e3e1059799c429e4f5cfb95ec53c +0 -0
  113. data/test/dummy/tmp/cache/assets/D64/3E0/sprockets%2Fb49b7f8034b81ec601bc32fb127fbe08 +0 -0
  114. data/test/dummy/tmp/cache/assets/D65/850/sprockets%2F7a9b6a0e2d51adb43a1d3f859213e6b8 +15469 -0
  115. data/test/dummy/tmp/cache/assets/D6C/E30/sprockets%2Fcc7322b05ac2bef75e9f4f145d38117f +0 -0
  116. data/test/dummy/tmp/cache/assets/D7D/E00/sprockets%2Fce33b82451c73df1cfec69928b4f77b6 +0 -0
  117. data/test/dummy/tmp/cache/assets/D85/5D0/sprockets%2Fcf743946c378c148b19ce78eeaa9cc75 +0 -0
  118. data/test/dummy/tmp/cache/assets/DBB/560/sprockets%2F5a1d70cf7dd81a5ab5ce7a40a3a38d06 +0 -0
  119. data/test/dummy/tmp/cache/assets/DC1/670/sprockets%2Fefdfad0542298a1db90d24dc27ecc250 +0 -0
  120. data/test/dummy/tmp/cache/assets/DC5/1E0/sprockets%2F7517f22ad0f9dc13b8f0f6e0cc0e5de2 +0 -0
  121. data/test/dummy/tmp/cache/assets/DCC/220/sprockets%2F28d953c6ab9cc9a7db15600aabc6d77d +208 -0
  122. data/test/dummy/tmp/cache/assets/DCF/DF0/sprockets%2Ff0d3068f05825e1dfd9f5aaba946d3dc +0 -0
  123. data/test/dummy/tmp/cache/assets/DD3/0E0/sprockets%2F3e9e08a334bae7deadb5335f5e4f195f +0 -0
  124. data/test/dummy/tmp/cache/assets/DDB/980/sprockets%2Fbd2abc80a17c901e2f497f88f9be58cf +0 -0
  125. data/test/dummy/tmp/cache/assets/E00/ED0/sprockets%2Ffecf3bffeb200a47e96bd04cbc878076 +1535 -0
  126. data/test/dummy/tmp/cache/assets/EFB/560/sprockets%2Fbca0daf751c020cdeddabbefa51cb6ea +0 -0
  127. data/test/dummy/tmp/restart.txt +0 -0
  128. data/test/test_helper.rb +10 -0
  129. metadata +326 -0
@@ -0,0 +1,86 @@
1
+ $(document).ready(function() {
2
+
3
+ module("Backbone.Events");
4
+
5
+ test("Events: bind and trigger", function() {
6
+ var obj = { counter: 0 };
7
+ _.extend(obj,Backbone.Events);
8
+ obj.bind('event', function() { obj.counter += 1; });
9
+ obj.trigger('event');
10
+ equals(obj.counter,1,'counter should be incremented.');
11
+ obj.trigger('event');
12
+ obj.trigger('event');
13
+ obj.trigger('event');
14
+ obj.trigger('event');
15
+ equals(obj.counter, 5, 'counter should be incremented five times.');
16
+ });
17
+
18
+ test("Events: bind, then unbind all functions", function() {
19
+ var obj = { counter: 0 };
20
+ _.extend(obj,Backbone.Events);
21
+ var callback = function() { obj.counter += 1; };
22
+ obj.bind('event', callback);
23
+ obj.trigger('event');
24
+ obj.unbind('event');
25
+ obj.trigger('event');
26
+ equals(obj.counter, 1, 'counter should have only been incremented once.');
27
+ });
28
+
29
+ test("Events: bind two callbacks, unbind only one", function() {
30
+ var obj = { counterA: 0, counterB: 0 };
31
+ _.extend(obj,Backbone.Events);
32
+ var callback = function() { obj.counterA += 1; };
33
+ obj.bind('event', callback);
34
+ obj.bind('event', function() { obj.counterB += 1; });
35
+ obj.trigger('event');
36
+ obj.unbind('event', callback);
37
+ obj.trigger('event');
38
+ equals(obj.counterA, 1, 'counterA should have only been incremented once.');
39
+ equals(obj.counterB, 2, 'counterB should have been incremented twice.');
40
+ });
41
+
42
+ test("Events: unbind a callback in the midst of it firing", function() {
43
+ var obj = {counter: 0};
44
+ _.extend(obj, Backbone.Events);
45
+ var callback = function() {
46
+ obj.counter += 1;
47
+ obj.unbind('event', callback);
48
+ };
49
+ obj.bind('event', callback);
50
+ obj.trigger('event');
51
+ obj.trigger('event');
52
+ obj.trigger('event');
53
+ equals(obj.counter, 1, 'the callback should have been unbound.');
54
+ });
55
+
56
+ test("Events: two binds that unbind themeselves", function() {
57
+ var obj = { counterA: 0, counterB: 0 };
58
+ _.extend(obj,Backbone.Events);
59
+ var incrA = function(){ obj.counterA += 1; obj.unbind('event', incrA); };
60
+ var incrB = function(){ obj.counterB += 1; obj.unbind('event', incrB); };
61
+ obj.bind('event', incrA);
62
+ obj.bind('event', incrB);
63
+ obj.trigger('event');
64
+ obj.trigger('event');
65
+ obj.trigger('event');
66
+ equals(obj.counterA, 1, 'counterA should have only been incremented once.');
67
+ equals(obj.counterB, 1, 'counterB should have only been incremented once.');
68
+ });
69
+
70
+ test("Events: bind a callback with a supplied context", function () {
71
+ expect(1);
72
+
73
+ var TestClass = function () { return this; }
74
+ TestClass.prototype.assertTrue = function () {
75
+ ok(true, '`this` was bound to the callback')
76
+ };
77
+
78
+ var obj = _.extend({},Backbone.Events);
79
+
80
+ obj.bind('event', function () { this.assertTrue(); }, (new TestClass));
81
+
82
+ obj.trigger('event');
83
+
84
+ });
85
+
86
+ });
@@ -0,0 +1,426 @@
1
+ $(document).ready(function() {
2
+
3
+ module("Backbone.Model");
4
+
5
+ // Variable to catch the last request.
6
+ window.lastRequest = null;
7
+
8
+ window.originalSync = Backbone.sync;
9
+
10
+ // Stub out Backbone.request...
11
+ Backbone.sync = function() {
12
+ lastRequest = _.toArray(arguments);
13
+ };
14
+
15
+ var attrs = {
16
+ id : '1-the-tempest',
17
+ title : "The Tempest",
18
+ author : "Bill Shakespeare",
19
+ length : 123
20
+ };
21
+
22
+ var proxy = Backbone.Model.extend();
23
+ var doc = new proxy(attrs);
24
+
25
+ var klass = Backbone.Collection.extend({
26
+ url : function() { return '/collection'; }
27
+ });
28
+
29
+ var collection = new klass();
30
+ collection.add(doc);
31
+
32
+ test("Model: initialize", function() {
33
+ var Model = Backbone.Model.extend({
34
+ initialize: function() {
35
+ this.one = 1;
36
+ equals(this.collection, collection);
37
+ }
38
+ });
39
+ var model = new Model({}, {collection: collection});
40
+ equals(model.one, 1);
41
+ equals(model.collection, collection);
42
+ });
43
+
44
+ test("Model: initialize with attributes and options", function() {
45
+ var Model = Backbone.Model.extend({
46
+ initialize: function(attributes, options) {
47
+ this.one = options.one;
48
+ }
49
+ });
50
+ var model = new Model({}, {one: 1});
51
+ equals(model.one, 1);
52
+ });
53
+
54
+ test("Model: url", function() {
55
+ equals(doc.url(), '/collection/1-the-tempest');
56
+ doc.collection.url = '/collection/';
57
+ equals(doc.url(), '/collection/1-the-tempest');
58
+ doc.collection = null;
59
+ var failed = false;
60
+ try {
61
+ doc.url();
62
+ } catch (e) {
63
+ failed = true;
64
+ }
65
+ equals(failed, true);
66
+ doc.collection = collection;
67
+ });
68
+
69
+ test("Model: url when using urlRoot, and uri encoding", function() {
70
+ var Model = Backbone.Model.extend({
71
+ urlRoot: '/collection'
72
+ });
73
+ var model = new Model();
74
+ equals(model.url(), '/collection');
75
+ model.set({id: '+1+'});
76
+ equals(model.url(), '/collection/%2B1%2B');
77
+ });
78
+
79
+ test("Model: clone", function() {
80
+ attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
81
+ a = new Backbone.Model(attrs);
82
+ b = a.clone();
83
+ equals(a.get('foo'), 1);
84
+ equals(a.get('bar'), 2);
85
+ equals(a.get('baz'), 3);
86
+ equals(b.get('foo'), a.get('foo'), "Foo should be the same on the clone.");
87
+ equals(b.get('bar'), a.get('bar'), "Bar should be the same on the clone.");
88
+ equals(b.get('baz'), a.get('baz'), "Baz should be the same on the clone.");
89
+ a.set({foo : 100});
90
+ equals(a.get('foo'), 100);
91
+ equals(b.get('foo'), 1, "Changing a parent attribute does not change the clone.");
92
+ });
93
+
94
+ test("Model: isNew", function() {
95
+ attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
96
+ a = new Backbone.Model(attrs);
97
+ ok(a.isNew(), "it should be new");
98
+ attrs = { 'foo': 1, 'bar': 2, 'baz': 3, 'id': -5 };
99
+ a = new Backbone.Model(attrs);
100
+ ok(!a.isNew(), "any defined ID is legal, negative or positive");
101
+ attrs = { 'foo': 1, 'bar': 2, 'baz': 3, 'id': 0 };
102
+ a = new Backbone.Model(attrs);
103
+ ok(!a.isNew(), "any defined ID is legal, including zero");
104
+ ok( new Backbone.Model({ }).isNew(), "is true when there is no id");
105
+ ok(!new Backbone.Model({ 'id': 2 }).isNew(), "is false for a positive integer");
106
+ ok(!new Backbone.Model({ 'id': -5 }).isNew(), "is false for a negative integer");
107
+ });
108
+
109
+ test("Model: get", function() {
110
+ equals(doc.get('title'), 'The Tempest');
111
+ equals(doc.get('author'), 'Bill Shakespeare');
112
+ });
113
+
114
+ test("Model: escape", function() {
115
+ equals(doc.escape('title'), 'The Tempest');
116
+ doc.set({audience: 'Bill & Bob'});
117
+ equals(doc.escape('audience'), 'Bill & Bob');
118
+ doc.set({audience: 'Tim > Joan'});
119
+ equals(doc.escape('audience'), 'Tim > Joan');
120
+ doc.set({audience: 10101});
121
+ equals(doc.escape('audience'), '10101');
122
+ doc.unset('audience');
123
+ equals(doc.escape('audience'), '');
124
+ });
125
+
126
+ test("Model: has", function() {
127
+ attrs = {};
128
+ a = new Backbone.Model(attrs);
129
+ equals(a.has("name"), false);
130
+ _([true, "Truth!", 1, false, '', 0]).each(function(value) {
131
+ a.set({'name': value});
132
+ equals(a.has("name"), true);
133
+ });
134
+ a.unset('name');
135
+ equals(a.has('name'), false);
136
+ _([null, undefined]).each(function(value) {
137
+ a.set({'name': value});
138
+ equals(a.has("name"), false);
139
+ });
140
+ });
141
+
142
+ test("Model: set and unset", function() {
143
+ attrs = {id: 'id', foo: 1, bar: 2, baz: 3};
144
+ a = new Backbone.Model(attrs);
145
+ var changeCount = 0;
146
+ a.bind("change:foo", function() { changeCount += 1; });
147
+ a.set({'foo': 2});
148
+ ok(a.get('foo')== 2, "Foo should have changed.");
149
+ ok(changeCount == 1, "Change count should have incremented.");
150
+ a.set({'foo': 2}); // set with value that is not new shouldn't fire change event
151
+ ok(a.get('foo')== 2, "Foo should NOT have changed, still 2");
152
+ ok(changeCount == 1, "Change count should NOT have incremented.");
153
+
154
+ a.unset('foo');
155
+ ok(a.get('foo')== null, "Foo should have changed");
156
+ ok(changeCount == 2, "Change count should have incremented for unset.");
157
+
158
+ a.unset('id');
159
+ equals(a.id, undefined, "Unsetting the id should remove the id property.");
160
+ });
161
+
162
+ test("Model: multiple unsets", function() {
163
+ var i = 0;
164
+ var counter = function(){ i++; };
165
+ var model = new Backbone.Model({a: 1});
166
+ model.bind("change:a", counter);
167
+ model.set({a: 2});
168
+ model.unset('a');
169
+ model.unset('a');
170
+ equals(i, 2, 'Unset does not fire an event for missing attributes.');
171
+ });
172
+
173
+ test("Model: using a non-default id attribute.", function() {
174
+ var MongoModel = Backbone.Model.extend({idAttribute : '_id'});
175
+ var model = new MongoModel({id: 'eye-dee', _id: 25, title: 'Model'});
176
+ equals(model.get('id'), 'eye-dee');
177
+ equals(model.id, 25);
178
+ equals(model.isNew(), false);
179
+ model.unset('_id');
180
+ equals(model.id, undefined);
181
+ equals(model.isNew(), true);
182
+ });
183
+
184
+ test("Model: set an empty string", function() {
185
+ var model = new Backbone.Model({name : "Model"});
186
+ model.set({name : ''});
187
+ equals(model.get('name'), '');
188
+ });
189
+
190
+ test("Model: clear", function() {
191
+ var changed;
192
+ var model = new Backbone.Model({name : "Model"});
193
+ model.bind("change:name", function(){ changed = true; });
194
+ model.clear();
195
+ equals(changed, true);
196
+ equals(model.get('name'), undefined);
197
+ });
198
+
199
+ test("Model: defaults", function() {
200
+ var Defaulted = Backbone.Model.extend({
201
+ defaults: {
202
+ "one": 1,
203
+ "two": 2
204
+ }
205
+ });
206
+ var model = new Defaulted({two: null});
207
+ equals(model.get('one'), 1);
208
+ equals(model.get('two'), null);
209
+ Defaulted = Backbone.Model.extend({
210
+ defaults: function() {
211
+ return {
212
+ "one": 3,
213
+ "two": 4
214
+ };
215
+ }
216
+ });
217
+ var model = new Defaulted({two: null});
218
+ equals(model.get('one'), 3);
219
+ equals(model.get('two'), null);
220
+ });
221
+
222
+ test("Model: change, hasChanged, changedAttributes, previous, previousAttributes", function() {
223
+ var model = new Backbone.Model({name : "Tim", age : 10});
224
+ equals(model.changedAttributes(), false);
225
+ model.bind('change', function() {
226
+ ok(model.hasChanged('name'), 'name changed');
227
+ ok(!model.hasChanged('age'), 'age did not');
228
+ ok(_.isEqual(model.changedAttributes(), {name : 'Rob'}), 'changedAttributes returns the changed attrs');
229
+ equals(model.previous('name'), 'Tim');
230
+ ok(_.isEqual(model.previousAttributes(), {name : "Tim", age : 10}), 'previousAttributes is correct');
231
+ });
232
+ model.set({name : 'Rob'}, {silent : true});
233
+ equals(model.hasChanged(), true);
234
+ equals(model.hasChanged('name'), true);
235
+ model.change();
236
+ equals(model.get('name'), 'Rob');
237
+ });
238
+
239
+ test("Model: change with options", function() {
240
+ var value;
241
+ var model = new Backbone.Model({name: 'Rob'});
242
+ model.bind('change', function(model, options) {
243
+ value = options.prefix + model.get('name');
244
+ });
245
+ model.set({name: 'Bob'}, {silent: true});
246
+ model.change({prefix: 'Mr. '});
247
+ equals(value, 'Mr. Bob');
248
+ model.set({name: 'Sue'}, {prefix: 'Ms. '});
249
+ equals(value, 'Ms. Sue');
250
+ });
251
+
252
+ test("Model: change after initialize", function () {
253
+ var changed = 0;
254
+ var attrs = {id: 1, label: 'c'};
255
+ var obj = new Backbone.Model(attrs);
256
+ obj.bind('change', function() { changed += 1; });
257
+ obj.set(attrs);
258
+ equals(changed, 0);
259
+ });
260
+
261
+ test("Model: save within change event", function () {
262
+ var model = new Backbone.Model({firstName : "Taylor", lastName: "Swift"});
263
+ model.bind('change', function () {
264
+ model.save();
265
+ ok(_.isEqual(lastRequest[1], model));
266
+ });
267
+ model.set({lastName: 'Hicks'});
268
+ });
269
+
270
+ test("Model: save", function() {
271
+ doc.save({title : "Henry V"});
272
+ equals(lastRequest[0], 'update');
273
+ ok(_.isEqual(lastRequest[1], doc));
274
+ });
275
+
276
+ test("Model: fetch", function() {
277
+ doc.fetch();
278
+ ok(lastRequest[0], 'read');
279
+ ok(_.isEqual(lastRequest[1], doc));
280
+ });
281
+
282
+ test("Model: destroy", function() {
283
+ doc.destroy();
284
+ equals(lastRequest[0], 'delete');
285
+ ok(_.isEqual(lastRequest[1], doc));
286
+ });
287
+
288
+ test("Model: non-persisted destroy", function() {
289
+ attrs = { 'foo': 1, 'bar': 2, 'baz': 3};
290
+ a = new Backbone.Model(attrs);
291
+ a.sync = function() { throw "should not be called"; };
292
+ ok(a.destroy(), "non-persisted model should not call sync");
293
+ });
294
+
295
+ test("Model: validate", function() {
296
+ var lastError;
297
+ var model = new Backbone.Model();
298
+ model.validate = function(attrs) {
299
+ if (attrs.admin) return "Can't change admin status.";
300
+ };
301
+ model.bind('error', function(model, error) {
302
+ lastError = error;
303
+ });
304
+ var result = model.set({a: 100});
305
+ equals(result, model);
306
+ equals(model.get('a'), 100);
307
+ equals(lastError, undefined);
308
+ result = model.set({admin: true}, {silent: true});
309
+ equals(lastError, undefined);
310
+ equals(model.get('admin'), true);
311
+ result = model.set({a: 200, admin: true});
312
+ equals(result, false);
313
+ equals(model.get('a'), 100);
314
+ equals(lastError, "Can't change admin status.");
315
+ });
316
+
317
+ test("Model: validate on unset and clear", function() {
318
+ var error;
319
+ var model = new Backbone.Model({name: "One"});
320
+ model.validate = function(attrs) {
321
+ if ("name" in attrs) {
322
+ if (!attrs.name) {
323
+ error = true;
324
+ return "No thanks.";
325
+ }
326
+ }
327
+ };
328
+ model.set({name: "Two"});
329
+ equals(model.get('name'), 'Two');
330
+ equals(error, undefined);
331
+ model.unset('name');
332
+ equals(error, true);
333
+ equals(model.get('name'), 'Two');
334
+ model.clear();
335
+ equals(model.get('name'), 'Two');
336
+ delete model.validate;
337
+ model.clear();
338
+ equals(model.get('name'), undefined);
339
+ });
340
+
341
+ test("Model: validate with error callback", function() {
342
+ var lastError, boundError;
343
+ var model = new Backbone.Model();
344
+ model.validate = function(attrs) {
345
+ if (attrs.admin) return "Can't change admin status.";
346
+ };
347
+ var callback = function(model, error) {
348
+ lastError = error;
349
+ };
350
+ model.bind('error', function(model, error) {
351
+ boundError = true;
352
+ });
353
+ var result = model.set({a: 100}, {error: callback});
354
+ equals(result, model);
355
+ equals(model.get('a'), 100);
356
+ equals(lastError, undefined);
357
+ equals(boundError, undefined);
358
+ result = model.set({a: 200, admin: true}, {error: callback});
359
+ equals(result, false);
360
+ equals(model.get('a'), 100);
361
+ equals(lastError, "Can't change admin status.");
362
+ equals(boundError, undefined);
363
+ });
364
+
365
+ test("Model: defaults always extend attrs (#459)", function() {
366
+ var Defaulted = Backbone.Model.extend({
367
+ defaults: {one: 1},
368
+ initialize : function(attrs, opts) {
369
+ equals(attrs.one, 1);
370
+ }
371
+ });
372
+ var providedattrs = new Defaulted({});
373
+ var emptyattrs = new Defaulted();
374
+ });
375
+
376
+ test("Model: Inherit class properties", function() {
377
+ var Parent = Backbone.Model.extend({
378
+ instancePropSame: function() {},
379
+ instancePropDiff: function() {}
380
+ }, {
381
+ classProp: function() {}
382
+ });
383
+ var Child = Parent.extend({
384
+ instancePropDiff: function() {}
385
+ });
386
+
387
+ var adult = new Parent;
388
+ var kid = new Child;
389
+
390
+ equals(Child.classProp, Parent.classProp);
391
+ notEqual(Child.classProp, undefined);
392
+
393
+ equals(kid.instancePropSame, adult.instancePropSame);
394
+ notEqual(kid.instancePropSame, undefined);
395
+
396
+ notEqual(Child.prototype.instancePropDiff, Parent.prototype.instancePropDiff);
397
+ notEqual(Child.prototype.instancePropDiff, undefined);
398
+ });
399
+
400
+ test("Model: Nested change events don't clobber previous attributes", function() {
401
+ var A = Backbone.Model.extend({
402
+ initialize: function() {
403
+ this.bind("change:state", function(a, newState) {
404
+ equals(a.previous('state'), undefined);
405
+ equals(newState, 'hello');
406
+ // Fire a nested change event.
407
+ this.set({ other: "whatever" });
408
+ });
409
+ }
410
+ });
411
+
412
+ var B = Backbone.Model.extend({
413
+ initialize: function() {
414
+ this.get("a").bind("change:state", function(a, newState) {
415
+ equals(a.previous('state'), undefined);
416
+ equals(newState, 'hello');
417
+ });
418
+ }
419
+ });
420
+
421
+ a = new A();
422
+ b = new B({a: a});
423
+ a.set({state: 'hello'});
424
+ });
425
+
426
+ });