-
Notifications
You must be signed in to change notification settings - Fork 0
/
record_lifecycle.html
863 lines (720 loc) · 46.6 KB
/
record_lifecycle.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
<!-- plugin styles -->
<link rel="stylesheet" type="text/css" href="stylesheets/highlight-github.css" />
<!-- guides styles -->
<link rel="stylesheet" type="text/css" href="stylesheets/reset.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/main.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<link rel="stylesheet" type="text/css" href="stylesheets/guides.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/overrides.style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/overrides.print.css" media="print" />
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet">
<meta name="generator" content="DocPad v6.79.0" />
</head>
<body class="guide">
<header role="banner">
<div class="container">
<h1 id="logo">
<a href="http://www.sproutcore.com"><img src="images/header/logo.png" alt="SproutCore" /></a>
</h1>
<nav role="navigation">
<ul>
<li><a href="http://sproutcore.com/about/">About</a></li>
<li><a href="http://showcase.sproutcore.com">Showcase</a></li>
<li class="active"><a href="http://guides.sproutcore.com">Guides</a></li>
<li><a href="http://docs.sproutcore.com">Docs</a></li>
<li><a href="http://sproutcore.com/community/">Community</a></li>
<li><a href="http://blog.sproutcore.com">Blog</a></li>
</ul>
</nav>
</div>
</header>
<div id="feature">
<div class="wrapper">
<div class="feature_header">
<a href="/"><img src="images/graphics/guides.png"></a>
<h2><a href="/">SproutCore Guides</a></h2>
<p>These guides are designed to help you write and perfect your code.</p>
</div>
<div class="feature_sidebar">
<a href="index.html" id="guidesMenu">
Guides Index <span id="guidesArrow">▸</span>
</a>
<div id="guides" class="clearfix" style="display: none;">
<a href="index.html" class="guidesMenu">
Guides Index <span class="guidesArrow">▾</span>
</a>
<hr style="clear:both;">
<dl class='L'><dt>Start Here</dt>
<dd><a href='/getting_started.html'>Getting Started: Part 1</a></dd>
<dd><a href='/getting_started_2.html'>Getting Started: Part 2</a></dd>
<dd><a href='/getting_started_3.html'>Getting Started: Part 3</a></dd>
<dd><a href='/core_concepts_sc_object.html'>Classes and SC.Object</a></dd>
<dd><a href='/core_concepts_kvo.html'>Computed Properties, Observers and Bindings</a></dd><dt>Views</dt>
<dd><a href='/views.html'>Core View Concepts</a></dd><dt>Models</dt>
<dd><a href='/records.html'>SproutCore Records</a></dd>
<dd><a href='/fixtures.html'>Using Fixtures</a></dd><dt>Theming</dt>
<dd><a href='/chance.html'>Using Chance, SproutCore's CSS Framework</a></dd>
<dd><a href='/theming_app.html'>Theming Your App</a></dd></dl><dl class='R'><dt>Testing</dt>
<dd><a href='/adding_unit_test.html'>Adding a Unit Test</a></dd>
<dd><a href='/unit_test_framework.html'>Unit Testing</a></dd>
<dd><a href='/writing_unit_tests.html'>Writing Unit Tests</a></dd>
<dd><a href='/running_unit_tests.html'>Running Unit Tests</a></dd>
<dd><a href='/todos_tdd.html'>SproutCore Development Using TDD</a></dd><dt>Extras</dt>
<dd><a href='/build_tools.html'>SproutCore's Build Tools</a></dd>
<dd><a href='/run_loop.html'>The Run Loop</a></dd>
<dd><a href='/enumerables.html'>Enumerables</a></dd><dt>Contributing to SproutCore</dt>
<dd><a href='/style_guide.html'>Javascript Guidelines</a></dd>
<dd><a href='/commit_code.html'>Committer Guidelines</a></dd>
<dd><a href='/documentation_guidelines.html'>Documentation Guidelines</a></dd>
<dd><a href='/contribute.html'>Contributing Guides</a></dd><dt>Thanks</dt>
<dd><a href='/credits.html'>Credits</a></dd></dl>
</div>
</div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<div class='headerSection'>
<h2>Understanding the Record Lifecycle</h2>
<p>This guide describes the lifecycle of Record objects in SproutCore. By referring to this guide you will be able to:</p>
<ul>
<li>Understand the statuses and substatuses that a record can be in at any given time.</li>
<li>Understand how a record progresses from creation to modification to destruction in a SproutCore database.</li>
</ul>
</div>
<h3 id='-The-Lifecycle-of-a-SproutCore-Record-'>1 - The Lifecycle of a SproutCore Record
</h3>
<p>SproutCore can manage much of the work of reading from, writing to, creating
and destroying the records in your application. It does this by applying a set
of statuses onto records, and using these to track the status of individual
records as they travel through the application. You can access the status of a
given record, at any time using the <tt>status</tt> function. Internally, this status
is stored as an integer - normally then, you will test to find out what status
you are in by using the & operator for statuses, and the === operator for
substatuses.</p>
<div class='note'><p> In the examples in this section, we assume that you are using an
application called MyApp, with a model called MyModel that inherits from
SC.Record and has a single string attribute: 'name'. There is a data store
stored inside Myapp.store. Changes are not set to auto-commit.
</p></div>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> record = MyApp.MyModel.find(MyApp.store, <span class="hljs-number">1</span>);
<span class="hljs-comment">// Assuming this record already exists in the store...</span>
record.status; <span class="hljs-comment">// Meaningless numeric response</span>
record.status & SC.Record.READY; <span class="hljs-comment">// Will return a 'truthy' value, because the record is in a ready status</span>
record.status & SC.Record.DESTROYED; <span class="hljs-comment">// Will return a 'non-truthy' value, because the record is not in a destroyed status</span>
record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// Will return true, because it is in the READY_CLEAN substatus</span>
record.status === SC.Record.READY_DIRTY; <span class="hljs-comment">// Will return false, because while the record is READY, it is not in the READY_DIRTY substatus</span>
record.name = <span class="hljs-string">"A different name"</span>; <span class="hljs-comment">// The record has now been altered, and should be marked as 'dirty' until it is saved to the server</span>
record.status & SC.Record.READY; <span class="hljs-comment">// Will still return true, since the record is still in a ready status</span>
record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// Will return now return false - the record is no longer the READY_CLEAN substatus...</span>
record.status === SC.Record.READY_DIRTY; <span class="hljs-comment">// ... because it's now dirty, so this statement is now true</span></code></div></pre>
<p>The status (like all the record attributes) is not stored in the SC.Record
object directly, but rather in its entry in the data store. This means if you
have two instances of the same record, they will always have the same status.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> record = MyApp.MyModel.find(MyApp.store, <span class="hljs-number">1</span>);
the_same_record = MyApp.MyModel.find(MyApp.store, <span class="hljs-number">1</span>);
record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// true</span>
the_same_record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// also true</span>
record.name = <span class="hljs-string">"A completely different name from previous"</span>;
record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// false</span>
the_same_record.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// also false</span>
record.status === SC.Record.READY_DIRTY; <span class="hljs-comment">// true</span>
the_same_record.status === SC.Record.READY_DIRTY; <span class="hljs-comment">// also true</span></code></div></pre>
<div class='note'><p> You will not necessarily see the behaviour above if you are using the
fixtures store. This is because the fixtures store commits changes immediately
- there is no remote store, so records are never 'dirty'. For more details see
the guide on 'Using Fixtures'.
</p></div>
<h3 id='-An-Overview-of-the-Statuses-'>2 - An Overview of the Statuses
</h3>
<p>There are three major record statuses in SproutCore:</p>
<ul>
<li><tt>SC.Record.READY</tt></li>
<li><tt>SC.Record.BUSY</tt></li>
<li><tt>SC.Record.DESTROYED</tt></li>
</ul>
<p>There are, additionally, two other statuses for special conditions a record
might find itself in: </p>
<ul>
<li><tt>SC.Record.EMPTY</tt></li>
<li><tt>SC.Record.ERROR</tt></li>
</ul>
<p>When your application creates a new record or first tries to download an
existing one from the server, the record will first enter the
<tt>SC.Record.EMPTY</tt> status, as it's various attributes and connection to the
data store are initialized. This status is transitory, however, and the record
should transition out more or less instantly.</p>
<p>When the record is initialized and exists in a stable, waiting condition in
the local data store, it will be in the <tt>SC.Record.READY</tt> status. This is the
normal "resting" status of a record.</p>
<p>From this status, changes could be made to the record - while these changes
are being committed back to the server (or for other reasons) the record would
be in the <tt>SC.Record.BUSY</tt> status.</p>
<p>Finally, if the record is deleted, it would enter the <tt>SC.Record.DESTROYED</tt>
status. At any point, if an error occurred or the record was left in an
unstable status, it may enter the <tt>SC.Record.ERROR</tt> status. Let's go into each
of these statuses - and any attached sub-statuses - in more detail.</p>
<h3 id='-SC-Record-EMPTY-Status-'>3 - SC.Record.EMPTY Status
</h3>
<p>The <tt>SC.Record.EMPTY</tt> status is a special, transitory status that a record is
in before it has been fully initialized. <tt>SC.Record.EMPTY</tt> is something of a
non-status - it is, essentially, the status a record is in before it has been
initialized with a status. As a result, you should never see a record in this
status unless you are really digging into the low-level mechanics of how
SC.Record initializes records.</p>
<p>The <tt>SC.Record.EMPTY</tt> status can only be entered when the new object is
created - it can never be reentered again.</p>
<p>A record in the <tt>SC.Record.EMPTY</tt> status can exit into any of the other four
major statuses, depending on how the record was created. Most commonly,
however, a record will take one of two paths forward:</p>
<ul>
<li>The application is creating a new record that is not in the datastore already.
In this case, the record will be transitioned into the <tt>SC.Record.READY</tt>
status (more specifically, the <tt>SC.Record.READY_NEW</tt> substatus).</li>
<li>The application is retrieving an existing record from the remote data source.
In this case, the record will begin the record retrieval process, and enter
the <tt>SC.Record.BUSY</tt> status (more specifically, the <tt>SC.Record.BUSY_LOADING</tt>)</li>
</ul>
<div class='note'><p> Keep in mind - status is stored not in the record object, but in the
datastore. So, creating a new SC.Record object may not necessarily involve
putting the record through <tt>SC.Empty</tt> at all. SC.Empty is the status a record
is in when it is first created in the data store - when you create a brand new
record, or look up a record from the remote data source for the first time. In
both of these cases, it is highly unlikely that you'll even see the <tt>SC.Empty</tt>
status. If this is confusing, please see the sample record biographies, later
in this document.
</p></div>
<h3 id='-SC-Record-READY-Status-'>4 - SC.Record.READY Status
</h3>
<p>The <tt>SC.Record.READY</tt> status is the 'normal' status for a record to exist in.
If a record is in ready status, this implies three things:</p>
<ul>
<li>The record is fully initialized</li>
<li>The record is in an internally consistent status</li>
<li>The record is available to be read from and written to</li>
</ul>
<p><img src="images/records/ready_substates.png" alt="Ready Substates"></p>
<p>The <tt>SC.Record.READY</tt> status has three sub-statuses, which denote the
condition that the record is in in relation to the remote database.</p>
<h4 id='-SC-Record-READY-NEW-'>4.1 - SC.Record.READY_NEW
</h4>
<p>A record in this status was created in memory but has not yet been committed
back to the server. If you commit changes on your store, this record will be
sent to the data source automatically. Likewise if you destroy this record it
will simply be removed from memory without notifying the server. The data
source also cannot modify records while they are in this status since the
remote data source doesn't even know that the record exists, presumably. A
record enters <tt>SC.Record.READY_NEW</tt>, normally, from <tt>SC.Record.EMPTY</tt>, because
a new record has been created from scratch. A record in <tt>SC.Record.READY_NEW</tt>,
then, will either eventually be destroyed, transitioning to <tt>SC.Record.DESTROYED_CLEAN</tt>,
or it will be committed, transitioning it to <tt>SC.Record.BUSY_CREATING</tt>.</p>
<h4 id='-SC-Record-READY-CLEAN-'>4.2 - SC.Record.READY_CLEAN
</h4>
<p>This is the status of a record that has been retrieved by the store, and for
which there are no pending changes to be committed back to the remote data
source. If you commit changes on your store, this record will NOT be included</p>
<ul>
<li><p>because there are no changes to commit back to the store. Likewise, if your
data source receives a notification from the server that this record has
changed, it can modify this record without any kind of error since no changes
would be lost. Records should normally, then, only enter the <tt>SC.Record.READY_CLEAN</tt>
status from <tt>SC.Record.BUSY</tt> - after having been retrieved from or updated against
the server. Clean records frequently transition into many different statuses:</p>
</li>
<li><p>If the application triggers a destroy action, it enters the
<tt>SC.Record.DESTROYED_DIRTY</tt> status (where its destruction can then be synced to
the server, as described later in this document)</p>
</li>
<li>If the application writes new data to the object, it will move to the
<tt>SC.Record.READY_DIRTY</tt> status, where it will await syncing to the server</li>
<li>If an application requests a refresh of the application against the remote
data source, it will enter the <tt>SC.Record.BUSY_REFRESH_CLEAN</tt> status</li>
</ul>
<h4 id='-SC-Record-READY-DIRTY-'>4.3 - SC.Record.READY_DIRTY
</h4>
<p>This is the status of a record that exists on the server but has since been
modified in your SproutCore application. It has pending changes that will be
sent to the server when you commit changes in your store. If your remote data
source tries to modify this record, it will throw an error since local changes
would be lost - the changes from the server could overwrite the changes that
have been made on the local application. The only exception to this would be
if a refresh is called, manually, by the application. In this case, the record
will enter the <tt>SC.Record.REFRESH_DIRTY</tt> status, where it overwrites the
attributes in the record with whatever the remote data source returns. A
record enters the <tt>SC.Record.READY_DIRTY</tt>, thus, only from <tt>SC.Record.READY_CLEAN</tt>,
because a change is made to the clean record. It should normally only exit this
status in two ways:</p>
<ul>
<li>The data source initiates a sync of the record to the remote data source - this
will transition the record into the <tt>SC.Record.BUSY_COMMITTING</tt> status.</li>
<li>The application chooses to delete the record before it ever commits the changes.
The record would then be moved to <tt>SC.Record.DESTROYED_DIRTY</tt>.</li>
</ul>
<div class='warning'><p> A record in <tt>SC.Record.REFRESH_DIRTY</tt> is analogous to an unsaved
file: the user has made changes, but you haven't saved them yet. It's
important to make sure that you take care of these records before an
application closes, and/or make sure that the user is aware of the difference
between a record they've changed that hasn't been saved, and a record they've
changed that HAS been saved
</p></div>
<h3 id='-SC-Record-BUSY-Status-'>5 - SC.Record.BUSY Status
</h3>
<p>The <tt>SC.Record.BUSY</tt> status is a catch-all status for times when a record
should be inaccessible to other processes, usually because it is being
synchronized with a remote data source. A record in the <tt>SC.Record.BUSY</tt>
status has the following properties:</p>
<ul>
<li>It is in the midst of a blocking process - when the process completes (successfully
or unsuccessfully), it is responsible for moving the record into a non-busy status.</li>
<li>It cannot have new data written to it.</li>
<li>It cannot be updated from the remote data source. Changes can be made by the application
(upstream changes), but downstream changes from the remote server will raise an error.</li>
<li>It can still be read from.</li>
</ul>
<div class='warning'><p> When writing your application, forgetting the <tt>SC.Record.BUSY</tt> is a
pretty common error. If you write functions that try to change record objects,
it is important to ensure these objects are not busy. This is usually
accomplished by testing for the status of a record before writing, by closely
controlling when and how server communication occurs, or both.
</p></div>
<p><img src="images/records/busy_substates.png" alt="Busy Substates"></p>
<p><tt>SC.Record.BUSY</tt> has six substatuses.</p>
<h4 id='-SC-Record-BUSY-LOADING-'>5.1 - SC.Record.BUSY_LOADING
</h4>
<p>When you first get a record from the store, it will usually be in the
BUSY_LOADING status. This means that the record did not exist in the store
and the remote data source has not yet returned any data to the data store.
All properties will be empty at this point. Normally, this means the status is
entered from the <tt>SC.Record.EMPTY</tt> status. Once data is retrieved from the
server for this record, it will transition to <tt>SC.Record.READY_CLEAN</tt>.</p>
<h4 id='-SC-Record-BUSY-CREATING-'>5.2 - SC.Record.BUSY_CREATING
</h4>
<p>When a brand new, unsaved record (i.e., one in <tt>SC.Record.READY_NEW</tt>) is
committed to the server for the first time, it is placed in this status, to
indicate that it is in the process of being remotely created, for the first
time. This means, of course, that this status is entered only from the <tt>SC.Record.READY_NEW</tt>
status. Once the server responds to indicate a successful save, it will transition
into <tt>SC.Record.READY_CLEAN</tt>. If the creation fails, the record will return to <tt>SC.Record.READY_NEW</tt>.</p>
<h4 id='-SC-Record-BUSY-COMMITTING-'>5.3 - SC.Record.BUSY_COMMITTING
</h4>
<p>When an existing record's changes are being committed to the store, it enters
this status. The status is transitioned into from the <tt>SC.Record.READY_DIRTY</tt>
status (since there are changes that need to be committed). If the commit
succeeds, it is transitioned in the <tt>SC.Record.READY_CLEAN</tt> status. If it
fails, it will transition back into <tt>SC.Record.READY_DIRTY</tt>.</p>
<h4 id='-SC-Record-BUSY-REFRESH-DIRTY-'>5.4 - SC.Record.BUSY_REFRESH_DIRTY
</h4>
<p>A record in this status has local changes but you asked the data source to
reload the data from the server anyway (that is, it was in the <tt>SC.Record.READY_DIRTY</tt>
status, but you ordered a manual refresh). If the server update completes successfully,
it will replace any local changes with a fresh copy from the server, and transition the
record into the <tt>SC.Record.READY_CLEAN</tt> status. If the refresh fails, the record will
return to the <tt>SC.Record.READY_DIRTY</tt> status and its data will be left intact.</p>
<h4 id='-SC-Record-BUSY-REFRESH-CLEAN-'>5.5 - SC.Record.BUSY_REFRESH_CLEAN
</h4>
<p>A record in this status has no local changes but you asked it to reload the
data from the server in case there have been any changes remotely (that is, it
was in <tt>SC.Record.READY_CLEAN</tt>, but you requested a manual refresh of the
record). When the server finishes, success or failure, this record will
return to the <tt>SC.Record.READY_CLEAN</tt> status. If the request succeeds, it
should overwrite the data in the record with any changes from the remote data
source.</p>
<h4 id='-SC-Record-BUSY-DESTROYING-'>5.6 - SC.Record.BUSY_DESTROYING
</h4>
<p>A record in this status was destroyed in the store and now is being destroyed
on the server as well. Once the destroy has completed, the record will become <tt>SC.Record.DESTROYED_CLEAN</tt>.
If the destroy fails, it will become <tt>SC.Record.DESTROYED_DIRTY</tt> again.</p>
<h3 id='-SC-Record-DESTROYED-Status-'>6 - SC.Record.DESTROYED Status
</h3>
<p>This status is the opposite of <tt>SC.Record.READY_NEW</tt>: your application has
ordered that the record be destroyed, and it's marked as such in the local
data store. Because this change is asynchronous - the record is not, normally,
REMOVED from the data store, it's just marked as destroyed. These records have
the following properties:</p>
<ul>
<li>The record has been marked for destruction, or has been destroyed. It should
not be treated as viable data</li>
</ul>
<p><img src="images/records/destroyed_substates.png" alt="Destroyed Substates"></p>
<p><tt>SC.Record.DESTROYED</tt> has two substatuses, which denote whether or not the
destruction has been pushed back to the remote data source.</p>
<h4 id='-SC-Record-DESTROYED-CLEAN-'>6.1 - SC.Record.DESTROYED_CLEAN
</h4>
<p>A record in this status has been destroyed both in the store and on the
server. There are two ways for a record two enter this status:</p>
<ul>
<li>The record was in +SC.Record.READY_NEW, and was destroyed. Because this
means it was never written to the server in the first place, the record is
clean, and has reached a final status.</li>
<li>The record was in the process of destruction (that is, it was in
<tt>SC.Record.BUSY_DESTROYING</tt>), and the server returned a response implying that
the destruction was completed successfully. The record has reached a final status</li>
</ul>
<p>Because a clean, destroyed record has been removed from the remote data
source, it is normally most appropriate to treat it as being in a final
status. Normally, you'll not transition it into any other status.</p>
<h4 id='-SC-Record-DESTROYED-DIRTY-'>6.2 - SC.Record.DESTROYED_DIRTY
</h4>
<p>A record in this status was destroyed in the store by the application, but the
action of destruction has not been yet synchronized to the server. A record in
this status should be static, and cannot accept changes from the server unless
the server also puts it into a destroyed status. A record enters this status
because it was in an <tt>SC.Record.READY_CLEAN</tt> or <tt>SC.Record.READY_DIRTY</tt>
status, and was marked for deletion by your application. Committing the record
to the <tt>SC_BUSY_DESTROYING</tt> status for final destruction on the remote data
source is the only way to escape this status.</p>
<h3 id='-SC-Record-ERROR-Status-'>7 - SC.Record.ERROR Status
</h3>
<p>A record in this status has, for some reason, entered an unstable status. Any
status can, theoretically, transition into the <tt>SC.Record.ERROR</tt> status, if an
illegal function is called, or if the record enters a data status that's
ambiguous. In general, once a record ends up in this status, it should be
considered suspect and destroyed - the application should regenerate or re-
retrieve this data manually.</p>
<h3 id='-Biography-of-a-Record-'>8 - Biography of a Record
</h3>
<p>Some of these life-paths are easier to follow by examining them through the
lifecycle of an actual record. In these examples, we'll presume that we have
an application called <tt>BabyNames</tt>, that we are building to contain a
dictionary of baby names, corresponding to a short passage describing their
origin and meaning. As such, there is a model in our application, <tt>BabyNames.Entry</tt>
that inherits from <tt>SC.Record</tt>. It has two string attributes, a <tt>name</tt> and a <tt>meaning</tt>.
Our application has a data store, <tt>BabyNames.store</tt> that connects to an external
data source.</p>
<h4 id='-Creating-a-Record-'>8.1 - Creating a Record
</h4>
<p>Our crack research team has been reading and came across an intriguing new
baby name to add to our database, and thus are creating a new <tt>BabyNames.Entry</tt>
record, like so:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> <span class="hljs-keyword">var</span> astrolabe = BabyNames.store.createRecord{
BabyNames.Entry, {
<span class="hljs-attr">name</span>: <span class="hljs-string">"Astrolabe"</span>
}
}</code></div></pre>
<p>Initially, upon creation of this record, there will be an instant in which the
record is in the <tt>SC.Record.EMPTY</tt> status, but we will never see this change,
because immediately the createRecord process will set an explicit status for
the record. The record that gets returned will be ready for us to work with:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.EMPTY; <span class="hljs-comment">// this is false, by the time the record is returned</span>
astrolabe.status & SC.Record.READY; <span class="hljs-comment">// returns a 'truthy' value</span>
astrolabe.status === SC.Record.READY_NEW; <span class="hljs-comment">// returns true - we have a new, unsaved record</span></code></div></pre>
<div class='note'><p> For the purposes of this discussion, we're not going to explore the work
of assigning an id to the record. For a discussion of this process, see the
guide on Using Records and the Data Store
</p></div>
<p>This record, currently, exists only in our local data store. If our research
team closed their browser, this entry would be lost, forever.</p>
<p>Because this is a new record, there's no worries about clean versus dirty
status. So, when we set attributes on the record, there is no status change:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status === SC.Record.READY_NEW; <span class="hljs-comment">// returns true</span>
astrolabe.meaning = <span class="hljs-string">"A scientific instrument used anciently in astronomy."</span>
astrolabe.status === SC.Record.READY_NEW; <span class="hljs-comment">// still returns true</span></code></div></pre>
<p>Now, our researcher is happy with the record. As such, she commits all the
pending changes to the server.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> BabyNames.store.commitRecords();</code></div></pre>
<p>At this point, a complex series of events happens. First, the store searches
through the records it holds, and extracts every record that is awaiting a
commit (any record, in other words, that is in <tt>SC.Record.READY_NEW</tt>, <tt>SC.Record.READY_DIRTY</tt>,
or <tt>SC.Record.DESTROYED_DIRTY</tt>). Our record certainly fits these criteria, so it
is committed to the server to update. Now, presuming that our remote data source
is a bit, slow, it may a take a while for this commit to complete. If this is
true, we can now see our record in a different status:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">// False: Our record is now in a different status...</span>
astrolabe.status & SC.Record.BUSY; <span class="hljs-comment">// True: ...busy, as it tries to work with the server</span>
astrolabe.status === SC.Record.BUSY_CREATING; <span class="hljs-comment">// True: Specifically, it is trying to create this record</span></code></div></pre>
<p>After a moment, though, our server accepts our new record creation, and we've
completed the process of creating this record.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">// True</span>
astrolabe.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// Our record is saved to the server</span></code></div></pre>
<div class='note'><p> So, what happens if the record does NOT save? In large part, this
depends on how we write the code for our data store. It is often a good idea,
for instance, to have our data store watch for, say, validation errors, and
handle these by simply canceling the create and, perhaps, setting a validation
errors field on the record. Then, the record would go back into
<tt>SC.Record.READY_NEW</tt>. The point is, deciding how these responses will be
handled is up to us when we write the code to communicate with the remote data
source. For more information on programming a data source, see the guide on
"Connecting with a DataSource"
</p></div>
<h4 id='-Modifying-a-record-'>8.2 - Modifying a record
</h4>
<p>Our crack researchers get a call from the company president: they've had a
complaint from a customer that they don't believe Astrolabe is a real name.
The researchers decide that perhaps they should put in some more information
on the name, to reassure people that the name has, truly, been used in the
past. First, of course, they'll need to look up the record in the data source.
(For the purposes of this document, rather than getting into the fine art of
writing queries, we will assume that the <tt>id</tt> of the Astrolabe record is <tt>12345</tt>)</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> <span class="hljs-keyword">var</span> astrolabe = BabyNames.Entry.find(BabyNames.store, <span class="hljs-number">12345</span>);</code></div></pre>
<p>Now, in the background, the application will look in it's data store and
check: do I have a record here that has id 12345? If we had, for instance, not
closed our browser between the creation example above and now (presuming we
wrote our data source correctly), then we should already have this record
pulled into our data source, somewhere. At this point, the record will just be
returned immediately. No status needs to be set on it at all, because the
status, you'll remember is stored in the data source, not in the record
object, so it will just have the status we last left it with - in this case, <tt>SC.Record.READY_CLEAN</tt>.
Setting up an SC.Record instance is, essentially, nothing more than setting up
a pointer to an entry in the data store.</p>
<p>But, what if we HAD closed the application, and this record had not been used
in our application since we reopened it? In this case, the record for
Astrolabe exists on the remote server, but not in our local data store. The
data store will look up the ID <tt>12345</tt>, and find nothing, and then proceed to
check the remote data source instead, requesting the record from there.
Presume, again, that our remote server is a tad slow. In this case, now when
we look at the record, what will be it's status?</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">// false - this record is not ready for us to use, yet</span>
astrolabe.status & SC.Record.BUSY; <span class="hljs-comment">// true - the record is busy because it's waiting for a server response</span>
astrolabe.status === SC.Record.BUSY_LOADING; <span class="hljs-comment">// true - the record is busy loading the record from the server</span></code></div></pre>
<p>Eventually, the server responds, and hands us the record we're looking for. At
this point, the data store will write this data into it's internal storage,
and you have a valid, clean record to use:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">// true - it's ready for you to work with, now!</span>
astrolabe.status & SC.Record.READY_CLEAN; <span class="hljs-comment">// true - since we just retrieved it from the server, it's clean, and needs no updates</span></code></div></pre>
<div class='note'><p> Again, the behavior surrounding a failure - like, if we were searching
for a record that doesn't exist on the remote data source - is up to us. We
may choose, for instance, to move the record into the <tt>SC.Record.ERROR</tt> status -
since we are treating it like an existing record, but it does not, in fact,
exist, it's in an inconsistent status, right? We'd code this into our data
source.
</p></div>
<p>At this point, the researcher sits back and thinks, trying to remember where
she DID hear the name 'Astrolabe'. This takes her about 15 minutes, before she
remembers, so she figures, maybe she ought to just check and make sure no one
has altered this record elsewhere, in the meantime, so she refreshes it from
the server</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> BabyNames.store.find(BabyNames.Entry, <span class="hljs-number">12345</span>);
astrolabe.status & SC.Record.READY; <span class="hljs-comment">// False: Now we're busy talking to the server again...</span>
astrolabe.status === SC.Record.BUSY_REFRESH_CLEAN; <span class="hljs-comment">// True: looks like the server is still busy letting us know the changes</span>
<span class="hljs-comment">// wait a few minutes</span>
astrolabe.status & SC.Record.READY; <span class="hljs-comment">// TRUE: Good, refresh is complete!</span>
astrolabe.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// TRUE: And, our record is clean and ready</span></code></div></pre>
<p>Now, it's time to make our changes:</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.meaning = <span class="hljs-string">"A scientific instrument used anciently in astronomy. Used by famous medieval lovers Abelard and Heloise, as the name for their illegitimate son. Not a very popular name, since."</span></code></div></pre>
<p>Again, though, this record has not been saved. If we close the browser, now,
we'll lose our hard work.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">// True: Yes, the record is still ready for work</span>
astrolabe.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// False: But it has not been synced to the server!</span>
astrolabe.status === SC.Record.READY_DIRTY; <span class="hljs-comment">// True: The record exists on the server, but we've made changes that haven't been saved yet!</span></code></div></pre>
<p>So, we'd better save our changes, now.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> BabyNames.store.commitRecords();</code></div></pre>
<p>Since our record is in the <tt>SC.Record.READY_DIRTY</tt> status, it should be
included in the refreshes, so it will start pushing these changes to the
server.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">//False - not ready, busy talking to the server</span>
astrolabe.status & SC.Record.BUSY; <span class="hljs-comment">//True</span>
astrolabe.status === SC.Record.BUSY_COMMITTING; <span class="hljs-comment">// Since we're updating an existing record, we are in the SC.Record.BUSY_COMMITING status, this time</span>
<span class="hljs-comment">// Now we wait a minute for the process to finish...</span>
astrolabe.status & SC.Record.READY; <span class="hljs-comment">// True, so communication with the server completed...</span>
astrolabe.status === SC.Record.READY_CLEAN; <span class="hljs-comment">// Also true, so communication was successful!</span></code></div></pre>
<p>So, now our record has been updated.</p>
<div class='note'><p> Frequently, you'll want to check on the status of your record's update.
This guide follow this pattern very frequently - testing against
<tt>SC.Record.READY</tt> will tell you whether the record is done communicating, first.
But this, of course, doesn't necessarily mean communication SUCCEEDED, just
that it COMPLETED. The second step is to test if the status equal
<tt>SC.Record.READY_CLEAN</tt>. Additionally, in your application, it may be a good
idea to also test for <tt>SC.Record.ERROR</tt>, to see if the communication was aborted
in an unstable status.
</p></div>
<h4 id='-Deleting-a-Record-'>8.3 - Deleting a Record
</h4>
<p>As crazy as this may seem, apparently there isn't a lot of popular interest in
the name 'Astrolabe' these days. The CEO has a chat with the research team
about this, and asks them to remove the record since it's just cluttering up
the database and making customers roll their eyes. This process is pretty
simple. The find should follow the exact same process as in the update task.
The only difference is the actual process of deletion.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.READY; <span class="hljs-comment">//True: So, it's available to be worked with</span>
astrolabe.status == SC.Record.READY_CLEAN; <span class="hljs-comment">//True: There's no changes hanging around on the record. Let's go ahead and delete it.</span>
astrolabe.destroy();</code></div></pre>
<p>Now, the destroy status does not make any change, immediately on the server,
it essentially just marks the record for deletion. So, if we closed the
application now, then the record would still be in the database, next time we
reopened it.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> astrolabe.status & SC.Record.DESTROYED; <span class="hljs-comment">//True, this record is destroyed in our local data store...</span>
astrolabe.status === SC.Record.DESTROYED_CLEAN; <span class="hljs-comment">//False: ...but we haven't synced this deletion to the remote server yet!</span>
astrolabe.status === SC.Record.DESTROYED_DIRTY; <span class="hljs-comment">// True: record is marked to be deleted when we sync with our server</span></code></div></pre>
<p>So, the next time we sync to the server, we'll go through a process more or
less parallel to record creation.</p>
<pre class="highlighted"><div class="code_container"><code class="hljs javascript"> BabyNames.store.commitRecords(); <span class="hljs-comment">//So, since our record is in dirty, it should be included in the commits</span>
astrolabe.status & SC.Record.BUSY; <span class="hljs-comment">// True - looks like we're in the process of talking to the server</span>
astrolabe.status === SC.Record.BUSY_DESTROYING; <span class="hljs-comment">// True - we're busy destroying the record remotely</span>
<span class="hljs-comment">// So, lets wait and let the server finish</span>
astrolabe.status & SC.Record.DESTROYED; <span class="hljs-comment">// True - looks like we finished trying to delete the record.</span>
astrolabe.status === SC.Record.DESTROYED_CLEAN; <span class="hljs-comment">// True - and it looks like the deletion succeeded!</span></code></div></pre>
<h3 id='-Changelog-'>9 - Changelog
</h3>
<ul>
<li>March 30, 2011: Fixed some spelling and clarity issues by <a href="credits.html#jasonpgignac">Jason Gignac</a></li>
<li>September 3, 2013: converted to Markdown format for DocPad guides by <a href="credits.html#deeDude">deeDude</a></li>
<li>September 11, 2013: added Changelog section by <a href="credits.html#topherfangio">Topher Fangio</a></li>
</ul>
</div>
<!-- TODO: re-add index_items //-->
<div id="subCol">
<h3 class="chapter"><img src="images/graphics/chapters.png" alt="">Chapters</h3>
<ol class='chapters'>
<li>
<a href='#-The-Lifecycle-of-a-SproutCore-Record-'><p> The Lifecycle of a SproutCore Record
</p></a>
<ul>
</ul>
</li>
<li>
<a href='#-An-Overview-of-the-Statuses-'><p> An Overview of the Statuses
</p></a>
<ul>
</ul>
</li>
<li>
<a href='#-SC-Record-EMPTY-Status-'><p> SC.Record.EMPTY Status
</p></a>
<ul>
</ul>
</li>
<li>
<a href='#-SC-Record-READY-Status-'><p> SC.Record.READY Status
</p></a>
<ul>
<li><a href='#-SC-Record-READY-NEW-'><p> SC.Record.READY_NEW
</p></a></li>
<li><a href='#-SC-Record-READY-CLEAN-'><p> SC.Record.READY_CLEAN
</p></a></li>
<li><a href='#-SC-Record-READY-DIRTY-'><p> SC.Record.READY_DIRTY
</p></a></li>
</ul>
</li>
<li>
<a href='#-SC-Record-BUSY-Status-'><p> SC.Record.BUSY Status
</p></a>
<ul>
<li><a href='#-SC-Record-BUSY-LOADING-'><p> SC.Record.BUSY_LOADING
</p></a></li>
<li><a href='#-SC-Record-BUSY-CREATING-'><p> SC.Record.BUSY_CREATING
</p></a></li>
<li><a href='#-SC-Record-BUSY-COMMITTING-'><p> SC.Record.BUSY_COMMITTING
</p></a></li>
<li><a href='#-SC-Record-BUSY-REFRESH-DIRTY-'><p> SC.Record.BUSY_REFRESH_DIRTY
</p></a></li>
<li><a href='#-SC-Record-BUSY-REFRESH-CLEAN-'><p> SC.Record.BUSY_REFRESH_CLEAN
</p></a></li>
<li><a href='#-SC-Record-BUSY-DESTROYING-'><p> SC.Record.BUSY_DESTROYING
</p></a></li>
</ul>
</li>
<li>
<a href='#-SC-Record-DESTROYED-Status-'><p> SC.Record.DESTROYED Status
</p></a>
<ul>
<li><a href='#-SC-Record-DESTROYED-CLEAN-'><p> SC.Record.DESTROYED_CLEAN
</p></a></li>
<li><a href='#-SC-Record-DESTROYED-DIRTY-'><p> SC.Record.DESTROYED_DIRTY
</p></a></li>
</ul>
</li>
<li>
<a href='#-SC-Record-ERROR-Status-'><p> SC.Record.ERROR Status
</p></a>
<ul>
</ul>
</li>
<li>
<a href='#-Biography-of-a-Record-'><p> Biography of a Record
</p></a>
<ul>
<li><a href='#-Creating-a-Record-'><p> Creating a Record
</p></a></li>
<li><a href='#-Modifying-a-record-'><p> Modifying a record
</p></a></li>
<li><a href='#-Deleting-a-Record-'><p> Deleting a Record
</p></a></li>
</ul>
</li>
<li>
<a href='#-Changelog-'><p> Changelog
</p></a>
<ul>
</ul>
</li>
</ol>
</div>
</div>
</div>
<hr class="hide" />
<footer>
<div class="container">
<div class="col">
<a href="index.html"><img src="images/footer/sc_logo_medium.png" alt="SproutCore" /></a>
<p id="license">
SproutCore framework is under MIT License.
</p>
</div>
<nav class="col">
<h1>Site Navigation</h1>
<ul>
<li><a href="http://www.sproutcore.com/about/">About</a></li>
<li><a href="http://docs.sproutcore.com">Docs</a></li>
<li><a href="http://blog.sproutcore.com">Blog</a></li>
<li><a href="http://www.sproutcore.com/install/?redirect=1">Download</a></li>
<li><a href="http://www.sproutcore.com/community/">Community</a></li>
<li><a href="https://github.com/sproutcore/sproutcore">GitHub</a></li>
<li><a href="mailto:community@sproutcore.com">Contact Us</a></li>
<li><a href="http://guides.sproutcore.com">Guides</a></li>
</ul>
<a href="http://www.w3.org/html/logo/"><img src="images/footer/html5_tech.png" alt="HTML5 Powered" /></a>
</nav>
<div class="col">
<h1>Get in Touch, Stay Informed</h1>
<ul class="social">
<li><a href="http://twitter.com/sproutcore"><img src="images/footer/twitter.png" alt="Twitter" /></a></li>
<li><a href="http://www.facebook.com/sproutcore"><img src="images/footer/facebook.png" alt="Facebook" /></a></li>
<li><a href="http://groups.google.com/group/sproutcore"><img src="images/footer/google.png" alt="Google Groups" /></a></li>
<li><a href="http://groups.google.com/group/sproutcore">Subscribe To Mailing List</a></li>
</ul>
<a href="http://eepurl.com/dK1-Y" target="_blank" id="newsletter" class="button secondary">Sign Up For Our Newsletter!</a>
<!-- Commented out until we have mailchimp API integration.
<h2>Subscribe To Newsletter</h2>
<div id="subscribe">
<form>
<input type="text" name="email" />
<button name="subscribe">Sign-Up</button>
<div class="processing"><img src="/img/spinner.gif"></div>
<div class="error"></div>
</form>
</div>
-->
</div>
<a href="#feature" class="top">Back To Top</a>
</div>
</footer>
<script type='text/javascript' src='/javascripts/jquery.min.js'></script>
<script type='text/javascript'>
/*
* Make the guides link clickable
*/
function guideMenu(e){
if (document.getElementById('guides').style.display == "none") {
document.getElementById('guides').style.display = "block";
document.getElementById('guidesArrow').innerHTML = "▾";
$('body').click(function(e){ guideMenu(e); });
} else {
document.getElementById('guides').style.display = "none";
document.getElementById('guidesArrow').innerHTML = "▸";
$('body').unbind('click');
}
return false;
}
$('#guidesMenu, .guidesMenu').click(guideMenu);
</script>
</body>
</html>