sproutcore 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/History.txt +43 -0
  2. data/Manifest.txt +12 -3
  3. data/bin/sc-build +19 -3
  4. data/bin/sc-install +5 -0
  5. data/bin/sc-remove +5 -0
  6. data/bin/sc-update +5 -0
  7. data/frameworks/prototype/prototype.js +267 -230
  8. data/frameworks/sproutcore/HISTORY +281 -135
  9. data/frameworks/sproutcore/controllers/array.js +133 -22
  10. data/frameworks/sproutcore/controllers/collection.js +4 -5
  11. data/frameworks/sproutcore/controllers/object.js +8 -2
  12. data/frameworks/sproutcore/core.js +361 -159
  13. data/frameworks/sproutcore/{foundation → debug}/unittest.js +3 -3
  14. data/frameworks/sproutcore/english.lproj/detect-browser +1 -1
  15. data/frameworks/sproutcore/english.lproj/theme.css +2 -2
  16. data/frameworks/sproutcore/foundation/application.js +6 -1
  17. data/frameworks/sproutcore/foundation/benchmark.js +37 -11
  18. data/frameworks/sproutcore/foundation/date.js +1 -1
  19. data/frameworks/sproutcore/foundation/enumerator.js +105 -0
  20. data/frameworks/sproutcore/foundation/object.js +19 -20
  21. data/frameworks/sproutcore/foundation/responder.js +1 -1
  22. data/frameworks/sproutcore/foundation/set.js +164 -57
  23. data/frameworks/sproutcore/foundation/string.js +151 -47
  24. data/frameworks/sproutcore/foundation/utils.js +84 -3
  25. data/frameworks/sproutcore/lib/collection_view.rb +1 -0
  26. data/frameworks/sproutcore/license.js +28 -0
  27. data/frameworks/sproutcore/mixins/array.js +73 -209
  28. data/frameworks/sproutcore/mixins/delegate_support.js +1 -1
  29. data/frameworks/sproutcore/mixins/enumerable.js +1006 -0
  30. data/frameworks/sproutcore/mixins/observable.js +153 -84
  31. data/frameworks/sproutcore/mixins/selection_support.js +13 -1
  32. data/frameworks/sproutcore/models/record.js +74 -27
  33. data/frameworks/sproutcore/models/store.js +7 -3
  34. data/frameworks/sproutcore/server/rails_server.js +82 -0
  35. data/frameworks/sproutcore/server/rest_server.js +178 -0
  36. data/frameworks/sproutcore/{foundation → server}/server.js +101 -48
  37. data/frameworks/sproutcore/tests/core/guidFor.rhtml +114 -0
  38. data/frameworks/sproutcore/tests/foundation/array.rhtml +6 -7
  39. data/frameworks/sproutcore/tests/foundation/set.rhtml +254 -0
  40. data/frameworks/sproutcore/tests/mixins/enumerable.rhtml +421 -0
  41. data/frameworks/sproutcore/tests/mixins/observable.rhtml +127 -0
  42. data/frameworks/sproutcore/tests/models/model.rhtml +23 -22
  43. data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +2 -2
  44. data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +112 -109
  45. data/frameworks/sproutcore/tests/views/view/frame.rhtml +91 -88
  46. data/frameworks/sproutcore/validators/date.js +1 -7
  47. data/frameworks/sproutcore/views/collection/collection.js +7 -2
  48. data/frameworks/sproutcore/views/list_item.js +141 -3
  49. data/frameworks/sproutcore/views/split.js +14 -11
  50. data/frameworks/sproutcore/views/view.js +9 -6
  51. data/lib/sproutcore/build_tools/html_builder.rb +19 -3
  52. data/lib/sproutcore/build_tools/resource_builder.rb +9 -3
  53. data/lib/sproutcore/bundle.rb +21 -0
  54. data/lib/sproutcore/bundle_manifest.rb +64 -20
  55. data/lib/sproutcore/helpers/capture_helper.rb +2 -2
  56. data/lib/sproutcore/library.rb +33 -9
  57. data/lib/sproutcore/merb/bundle_controller.rb +16 -5
  58. data/lib/sproutcore/version.rb +1 -1
  59. data/lib/sproutcore/view_helpers.rb +1 -1
  60. data/{sc-config.rb → sc-config} +5 -2
  61. metadata +24 -5
@@ -0,0 +1,178 @@
1
+ // ========================================================================
2
+ // SproutCore
3
+ // copyright 2006-2008 Sprout Systems, Inc.
4
+ // ========================================================================
5
+
6
+ require('core') ;
7
+ require('server') ;
8
+
9
+ /**
10
+ @class
11
+
12
+ Usually you wouldn't need to call any of the methods on this class or it's
13
+ superclass, except for calling the +listFor+ method. The other methods are
14
+ called for you when you work with your model objects. For example, calling
15
+ myObject.commit(); will call the commitRecords method on this server if you
16
+ had defined this server to be to the +dataSource+ of myObject.
17
+
18
+ To have an SC model reflect data on a backend server attach an instance of
19
+ this class to your application. For example:
20
+
21
+ {{{
22
+ Contacts = SC.Object.create({
23
+ server: SC.RestServer.create({ prefix: ['Contacts'] })
24
+ }) ;
25
+ }}}
26
+
27
+ Then attach that server as the +dataSource+ to each model class that you
28
+ want to have reflected. Also define a +resourceURL+ which defines the URL
29
+ where the collection of your model can be queried. For example:
30
+
31
+ {{{
32
+ Contacts.Contact = SC.Record.extend(
33
+ dataSource: Contacts.server,
34
+ resourceURL: 'sc/contacts',
35
+ properties: ['guid','firstName','lastName'],
36
+ primaryKey: 'guid'
37
+ }) ;
38
+ }}}
39
+
40
+ When you work with your models, behind the scenes SC will use 5 main methods
41
+ on this server. Each is listed below, together with the HTTP method used in
42
+ the call to the backend server and the URL that is being called. The URL is
43
+ based on the example given above.
44
+
45
+ listFor GET /sc/contacts
46
+
47
+ createRecords POST /sc/contacts
48
+
49
+ refreshRecords
50
+ for one record GET /sc/contacts/12345
51
+
52
+ refreshRecords
53
+ for many records GET /sc/contacts?ids=1,2,3,4,5,6
54
+
55
+ commitRecords
56
+ for one record PUT /sc/contacts/12345
57
+
58
+ commitRecords
59
+ for many records PUT /sc/contacts?ids=1,2,3,4,5
60
+
61
+ destroyRecords
62
+ for one record DELETE /sc/contacts/12345
63
+
64
+ destroyRecords
65
+ for many records DELETE /sc/contacts?ids=1,2,3,4,5
66
+
67
+ The above is the default behaviour of this server. If you want different
68
+ URLs to be generated then extend this class and override the +urlFor+
69
+ method.
70
+
71
+ Another way to override the above is to tell SC where member resources can
72
+ be refreshed, committed and destroyed. For example, when SC calls
73
+
74
+ {{{
75
+ GET /sc/contacts
76
+ }}}
77
+
78
+ you could reply as follows:
79
+
80
+ {{{
81
+ records: [
82
+ { guid: '123',
83
+ type: "Contact",
84
+ refreshURL: "/contacts?refresh=123",
85
+ updateURL: "/contacts/123?update=Y",
86
+ destroyURL: "/contacts/123",
87
+ firstName: "Charles",
88
+ ...
89
+ }],
90
+ ...
91
+ }
92
+ }}}
93
+
94
+ Then when contact 123 needs to be refreshed later on by SC, it will call:
95
+
96
+ {{{
97
+ GET /contacts?refresh=123
98
+ }}}
99
+
100
+ instead of GET /contacts/123. Note that this only works for members on your
101
+ resource. If a collection of contacts needed to be refreshed it would still
102
+ call for example GET /contacts?id=123,456,789 instead of making 3 separate
103
+ calls.
104
+
105
+ Because some browsers cannot actually perform an HTTP PUT or HTTP DELETE it
106
+ will actually perform an HTTP POST but will put an additional key,value pair
107
+ in the post data packet. For HTTP PUT it will add _method='put' and for
108
+ HTTP DELETE it will add _method='delete' in the post data.
109
+
110
+ Via the SC.Server#request method you can also call collection and member
111
+ functions on your resource. Use the +action+ parameter for this. For
112
+ example, server.request('contacts', 'archive', null, params, 'delete')
113
+ would call:
114
+
115
+ {{{
116
+ DELETE /contacts/archive
117
+ }}}
118
+
119
+ And server.request('contacts', 'give', [12345], {'amount': 1000}, 'put')
120
+ would call:
121
+
122
+ {{{
123
+ PUT /contacts/12345/give
124
+ }}}
125
+
126
+ with post data amount=1000.
127
+
128
+ Alternatively explicitely define the URL to use by setting the +url+
129
+ property in the +params+ argument that is passed to the server.request
130
+ method. For example:
131
+
132
+ {{{
133
+ Contacts.server.request(null,null,null, {url: '/sc/archive'}, 'delete')
134
+ }}}
135
+
136
+ would call:
137
+
138
+ {{{
139
+ DELETE /sc/archive
140
+ }}}
141
+
142
+
143
+ @extends SC.Server
144
+ @author Lawrence Pit
145
+ @copyright 2006-2008, Sprout Systems, Inc. and contributors.
146
+ @since SproutCore 1.0
147
+ */
148
+ SC.RestServer = SC.Server.extend({
149
+
150
+ /**
151
+ @see SC.Server.urlFor
152
+ **/
153
+ urlFor: function(resource, action, ids, params, method) {
154
+ url = resource;
155
+ if (ids && ids.length == 1) url = url + '/' + ids[0];
156
+ if (action && action != '') url = url + '/' + action;
157
+ return url;
158
+ },
159
+
160
+
161
+ /* privates, overrides the values in SC.Server */
162
+
163
+ _listForAction: '',
164
+ _listForMethod: 'get',
165
+
166
+ _createAction: '',
167
+ _createMethod: 'post',
168
+
169
+ _refreshAction: '',
170
+ _refreshMethod: 'get',
171
+
172
+ _commitAction: '',
173
+ _commitMethod: 'put',
174
+
175
+ _destroyAction: '',
176
+ _destroyMethod: 'delete'
177
+
178
+ }) ;
@@ -62,8 +62,9 @@ SC.Server = SC.Object.extend({
62
62
  // onFailure -- function invoked when request fails. Same format.
63
63
  // requestContext -- simply passed back.
64
64
  // cacheCode -- String indicating the time of the last refresh.
65
+ // url -- override the default url building with this url.
65
66
  //
66
- request: function(resource, verb, ids, params, method) {
67
+ request: function(resource, action, ids, params, method) {
67
68
 
68
69
  // Get Settings and Options
69
70
  if (!params) params = {} ;
@@ -72,24 +73,29 @@ SC.Server = SC.Object.extend({
72
73
  var onNotModified = params.onNotModified; delete params.onNotModified ;
73
74
  var onFailure = params.onFailure ; delete params.onFailure ;
74
75
  var context = params.requestContext ; delete params.requestContext ;
76
+ var accept = params.accept ; delete params.accept ;
75
77
  var cacheCode = params.cacheCode; delete params.cacheCode ;
78
+ var url = params.url; delete params.url;
79
+
80
+ opts.requestHeaders = {'Accept': 'application/json, text/javascript, application/xml, text/xml, text/html, */*'}
81
+ if (accept) opts.requestHeaders['Accept'] = accept ;
82
+ if (cacheCode) opts.requestHeaders['Sproutit-Cache'] = cacheCode ;
83
+ opts.method = method || 'get' ;
84
+
85
+ if (!url) url = this.urlFor(resource, action, ids, params, opts.method) ;
76
86
 
77
87
  // handle ids
78
- var idPart = '' ;
79
- if (ids) if (ids.length > 1) {
88
+ if (ids && ids.length > 1) {
80
89
  params.ids = [ids].flatten().join(',') ;
81
- } else if (ids.length == 1) {
82
- idPart = '/' + ids[0] ;
83
- }
90
+ }
84
91
 
92
+ // adds a custom HTTP header for remote requests
93
+ opts.requestHeaders = {'X-SproutCore-Version' : '1.0'}
94
+
85
95
  // convert parameters.
86
96
  var parameters = this._toQueryString(params) ;
87
97
  if (parameters && parameters.length > 0) opts.parameters = parameters ;
88
98
 
89
- // prepare request headers and options
90
- if (cacheCode) opts.requestHeaders = ['Sproutit-Cache',cacheCode] ;
91
- opts.method = method || 'get' ;
92
- var url = this.urlFormat.format(resource,verb) + idPart;
93
99
  var request = null ; //will container the ajax request
94
100
 
95
101
  // Save callback functions.
@@ -107,10 +113,27 @@ SC.Server = SC.Object.extend({
107
113
  if (onFailure) onFailure(transport.status, transport, cacheCode,context);
108
114
  } ;
109
115
 
110
- console.log('REQUEST: %@'.fmt(url)) ;
116
+ console.log('REQUEST: %@ %@'.fmt(opts.method, url)) ;
117
+
111
118
  request = new Ajax.Request(url,opts) ;
112
119
  },
113
120
 
121
+ /**
122
+ Generates the URL that is going to be called by this server. Note that you
123
+ should only return relative URLs. You can only call resources that are on
124
+ the same domain as where this script was downloaded from.
125
+
126
+ @param {String} resource the URL where the collection of the resource can be queried
127
+ @param {String} action the action that should be performed on the resource
128
+ @param {Array} ids array of identifiers of your model instances
129
+ @param {Array} params parameters that were passed to the SC.Server#request method
130
+ @param {String} method the HTTP method that will be used
131
+ @returns {String} the URL to use in the request to the backend server
132
+ **/
133
+ urlFor: function(resource, action, ids, params, method) {
134
+ var idPart = (ids && ids.length == 1) ? ids[0] : '';
135
+ return this.urlFormat.format(resource, action) + idPart;
136
+ },
114
137
 
115
138
  // RECORD METHODS
116
139
  // These methods do the basic record changes.
@@ -123,6 +146,7 @@ SC.Server = SC.Object.extend({
123
146
  listFor: function(opts) {
124
147
  var recordType = opts.recordType ;
125
148
  var resource = recordType.resourceURL() ;
149
+
126
150
  if (!resource) return false ;
127
151
 
128
152
  var order = opts.order || 'id' ;
@@ -147,10 +171,12 @@ SC.Server = SC.Object.extend({
147
171
  if (opts.offset) params.offset = opts.offset;
148
172
  if (opts.limit) params.limit = opts.limit ;
149
173
  if (order) params.order = order ;
150
-
151
- this.request(resource,'list',null,params) ;
174
+ this.request(resource, this._listForAction, null, params, this._listMethod) ;
152
175
  },
153
176
 
177
+ _listForAction: 'list',
178
+ _listMethod: 'get',
179
+
154
180
  _listSuccess: function(status, transport, cacheCode, context) {
155
181
  var json = eval('json='+transport.responseText) ;
156
182
  if (!json) { console.log('invalid json!'); return; }
@@ -202,14 +228,17 @@ SC.Server = SC.Object.extend({
202
228
  }) ;
203
229
 
204
230
  // issue request
205
- this.request(resource,'create',null,{
231
+ this.request(resource, this._createAction, null, {
206
232
  requestContext: context,
207
233
  onSuccess: this._createSuccess.bind(this),
208
234
  onFailure: this._createFailure.bind(this),
209
235
  records: data
210
- },'post') ;
236
+ }, this._createMethod) ;
211
237
  }
212
238
  },
239
+
240
+ _createAction: 'create',
241
+ _createMethod: 'post',
213
242
 
214
243
  // This method is called when a create is successful. It first goes through
215
244
  // and assigns the primaryKey to each record.
@@ -261,16 +290,23 @@ SC.Server = SC.Object.extend({
261
290
  });
262
291
  context._recordType = curRecords[0].recordType ; // default rec type.
263
292
 
264
- // issue request
265
- this.request(resource,'show',ids,{
293
+ params = {
266
294
  requestContext: context,
267
295
  cacheCode: ((cacheCode=='') ? null : cacheCode),
268
296
  onSuccess: this._refreshSuccess.bind(this),
269
297
  onFailure: this._refreshFailure.bind(this)
270
- }) ;
298
+ };
299
+
300
+ if (ids.length == 1 && curRecords[0].refreshURL) params['url'] = curRecords[0].refreshURL;
301
+
302
+ // issue request
303
+ this.request(resource, this._refreshAction, ids, params, this._refreshMethod) ;
271
304
  }
272
305
  },
273
306
 
307
+ _refreshAction: 'show',
308
+ _refreshMethod: 'get',
309
+
274
310
  // This method is called when a refresh is successful. It expects an array
275
311
  // of hashes, which it will convert to records.
276
312
  _refreshSuccess: function(status, transport, cacheCode, context) {
@@ -299,50 +335,58 @@ SC.Server = SC.Object.extend({
299
335
  var server = this ;
300
336
 
301
337
  // start format differences
338
+ var data = null;
302
339
  switch(this.get('postFormat')){
303
340
  case SC.URL_ENCODED_FORMAT:
304
- var data = curRecords.map(function(rec) {
341
+ data = curRecords.map(function(rec) {
305
342
  return server._decamelizeData(rec.getPropertyData()) ;
306
343
  }) ;
307
-
308
- // issue request
309
- this.request(resource,'update',null,{
310
- requestContext: records,
311
- onSuccess: this._commitSuccess.bind(this),
312
- onFailure: this._commitFailure.bind(this),
313
- records: data
314
- },'post') ;
315
- break;
316
-
344
+ break;
317
345
  case SC.JSON_FORMAT:
318
346
  // get all records and put them into an array
319
347
  var objects = [];
320
348
  for(rec in curRecords){
321
349
  if (!curRecords.hasOwnProperty(rec)) continue ;
322
- objects.push(curRecords[rec].get('attributes'));
350
+ objects.push(curRecords[rec].get('attributes') || {});
323
351
  }
324
352
 
325
353
  // convert to JSON and escape if this.escapeJSON is true
326
354
  if(this.get('escapeJSON')){
327
- var data = escape(objects.toJSONString());
355
+ data = escape(objects.toJSONString());
328
356
  } else {
329
- var data = objects.toJSONString();
357
+ data = objects.toJSONString();
330
358
  }
331
-
332
- // issue request
333
- this.request(resource,'update',null,{
334
- requestContext: records,
335
- onSuccess: this._commitSuccess.bind(this),
336
- onFailure: this._commitFailure.bind(this),
337
- records: data
338
- },'post') ;
339
- break;
359
+ break;
340
360
  default:
341
361
  break;
342
362
  }
343
- // end format differences
363
+ // end format differences
364
+
365
+ if (data) {
366
+ var ids = [];
367
+ if (curRecords.length == 1) {
368
+ var primaryKey = curRecords[0].get('primaryKey') ;
369
+ var key = curRecords[0].get(primaryKey);
370
+ if (key) ids.push(key);
371
+ }
372
+
373
+ params = {
374
+ requestContext: records,
375
+ onSuccess: this._commitSuccess.bind(this),
376
+ onFailure: this._commitFailure.bind(this),
377
+ records: data
378
+ };
379
+
380
+ if (ids.length == 1 && curRecords[0].updateURL) params['url'] = curRecords[0].updateURL;
381
+
382
+ // issue request
383
+ this.request(resource, this._commitAction, ids, params, this._commitMethod) ;
384
+ }
344
385
  }
345
386
  },
387
+
388
+ _commitAction: 'update',
389
+ _commitMethod: 'post',
346
390
 
347
391
  // This method is called when a refresh is successful. It expects an array
348
392
  // of hashes, which it will convert to records.
@@ -388,13 +432,22 @@ SC.Server = SC.Object.extend({
388
432
 
389
433
  // issue request -- we may not have ids to send tho (for ex, if all
390
434
  // records were newRecords.)
391
- if (ids && ids.length > 0) this.request(resource,'destroy',ids,{
392
- requestContext: records,
393
- onSuccess: this._destroySuccess.bind(this),
394
- onFailure: this._destroyFailure.bind(this)
395
- },'post') ;
435
+ if (ids && ids.length > 0) {
436
+ params = {
437
+ requestContext: records,
438
+ onSuccess: this._destroySuccess.bind(this),
439
+ onFailure: this._destroyFailure.bind(this)
440
+ };
441
+
442
+ if (ids.length == 1 && curRecords[0].destroyURL) params['url'] = curRecords[0].destroyURL;
443
+
444
+ this.request(resource, this._destroyAction, ids, params, this._destroyMethod) ;
445
+ }
396
446
  }
397
447
  },
448
+
449
+ _destroyAction: 'destroy',
450
+ _destroyMethod: 'post',
398
451
 
399
452
  _destroySuccess: function(status, transport, cacheCode, records) {
400
453
  console.log('destroySuccess!') ;
@@ -420,7 +473,7 @@ SC.Server = SC.Object.extend({
420
473
 
421
474
  // convert the 'id' property to 'guid'
422
475
  if (data.id) { data.guid = data.id; delete data.id; }
423
-
476
+
424
477
  // find the recordType
425
478
  if (data.type) {
426
479
  var recordName = data.type.capitalize() ;
@@ -0,0 +1,114 @@
1
+ <% content_for('final') do %>
2
+ <script>
3
+
4
+ Test.context("Object", {
5
+
6
+ setup: function() {
7
+
8
+ objectA = {} ;
9
+ objectB = {} ;
10
+ },
11
+
12
+ "should return same guid for same instance every time": function() {
13
+ assertEqual(SC.guidFor(objectA), SC.guidFor(objectA)) ;
14
+ },
15
+
16
+ "should return different guid for different instances": function() {
17
+ assertNotEqual(SC.guidFor(objectA), SC.guidFor(objectB)) ;
18
+ },
19
+
20
+ "guid should not parse to a number": function() {
21
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(objectA), 0)));
22
+ }
23
+
24
+ });
25
+
26
+ Test.context("String", {
27
+
28
+ setup: function() {
29
+ stringA = "string A" ;
30
+ stringACopy = "string A" ;
31
+
32
+ stringB = "string B" ;
33
+ },
34
+
35
+ "same string instance should have same guide every time": function() {
36
+ assertEqual(SC.guidFor(stringA), SC.guidFor(stringA)) ;
37
+ },
38
+
39
+ "two string instances with same value should have same guid": function() {
40
+ assertEqual(SC.guidFor(stringA), SC.guidFor(stringACopy)) ;
41
+ },
42
+
43
+ "two instances with different value should have different guid": function(){
44
+ assertNotEqual(SC.guidFor(stringA), SC.guidFor(stringB)) ;
45
+ },
46
+
47
+ "guid should not parse to a number": function() {
48
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(stringA), 0)));
49
+ }
50
+
51
+ });
52
+
53
+ Test.context("Number", {
54
+
55
+ setup: function() {
56
+ numberA = 23 ;
57
+ numberACopy = 23 ;
58
+
59
+ numberB = 34 ;
60
+ },
61
+
62
+ "same number instance should have same guide every time": function() {
63
+ assertEqual(SC.guidFor(numberA), SC.guidFor(numberA)) ;
64
+ },
65
+
66
+ "two number instances with same value should have same guid": function() {
67
+ assertEqual(SC.guidFor(numberA), SC.guidFor(numberACopy)) ;
68
+ },
69
+
70
+ "two instances with different value should have different guid": function(){
71
+ assertNotEqual(SC.guidFor(numberA), SC.guidFor(numberB)) ;
72
+ },
73
+
74
+ "guid should not parse to a number": function() {
75
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(numberA), 0)));
76
+ }
77
+ });
78
+
79
+ Test.context("Boolean", {
80
+
81
+ "should always have same guid": function() {
82
+ assertEqual(SC.guidFor(true), SC.guidFor(true)) ;
83
+ assertEqual(SC.guidFor(false), SC.guidFor(false)) ;
84
+ },
85
+
86
+ "true should have different guid than false": function() {
87
+ assertNotEqual(SC.guidFor(true), SC.guidFor(false)) ;
88
+ },
89
+
90
+ "guid should not parse to a number": function() {
91
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(true), 0)));
92
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(false), 0)));
93
+ }
94
+ });
95
+
96
+ Test.context("Null and Undefined", {
97
+
98
+ "should always have same guid": function() {
99
+ assertEqual(SC.guidFor(null), SC.guidFor(null)) ;
100
+ assertEqual(SC.guidFor(undefined), SC.guidFor(undefined)) ;
101
+ },
102
+
103
+ "null should have different guid than undefined": function() {
104
+ assertNotEqual(SC.guidFor(null), SC.guidFor(undefined)) ;
105
+ },
106
+
107
+ "guid should not parse to a number": function() {
108
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(null), 0)));
109
+ assertEqual(YES, isNaN(parseInt(SC.guidFor(undefined), 0)));
110
+ }
111
+ });
112
+
113
+ </script>
114
+ <% end %>
@@ -27,7 +27,7 @@ ArrayTests = function(factoryFunc) {
27
27
  } ;
28
28
 
29
29
  this.observe = function() {
30
- var keys = $A(arguments) ;
30
+ var keys = SC.$A(arguments) ;
31
31
  var loc = keys.length ;
32
32
  while(--loc >= 0) {
33
33
  this.a.addObserver(keys[loc], this.observer) ;
@@ -157,15 +157,14 @@ ArrayTests = function(factoryFunc) {
157
157
 
158
158
  var cnt = 0 ;
159
159
  var items = [] ;
160
- this.a.each(function( item, idx ) {
160
+ this.a.forEach(function( item, idx ) {
161
161
  items.push(item) ;
162
162
  cnt++ ;
163
163
  }) ;
164
-
165
- items[0].shouldEqual('A') ;
166
- items[1].shouldEqual('B') ;
167
- items[2].shouldEqual('C') ;
168
164
  cnt.shouldEqual(this.a.get('length')) ;
165
+ assertEqual('A', items[0]) ;
166
+ assertEqual('B', items[1]) ;
167
+ assertEqual('C',items[2]) ;
169
168
  },
170
169
 
171
170
  "ary.isEqual() should return true when array contents match": function() {
@@ -202,7 +201,7 @@ DummyArray = SC.Object.extend(SC.Array, {
202
201
  this.content.replace(idx,amt,objects) ;
203
202
 
204
203
  this.set('length', this.content.length) ;
205
- this.arrayContentDidChange() ;
204
+ this.enumerableContentDidChange() ;
206
205
  this.endPropertyChanges() ;
207
206
  },
208
207