Following the life cycle of SC.Record

Records in SproutCore follow a life cycle similar to records in any other database, but with an important difference. Since the SproutCore store hosts the data only temporarily (remember it's a quick access cache representing remote data), we don't actually perform final data modifications within the application. Instead, we invoke requests and modifications (Create, Read, Update, Destroy) on a remote data store and update our local record state to match them.

We've already seen that records have a status value, such as SC.Record.READY_CLEAN, which indicates the current state of the record. To help us understand every possible state in the life cycle of a SproutCore record, I've created figures of the SC.Record states. You should find these figures useful when you begin working with records. We will refer back to them repeatedly in this section as we learn how to load, unload, create, read, update, and ultimately destroy records.

The following figure shows the static states of a record and the methods on the store that we use for transition between them. The methods are on the store because the store manages the state of each record and so, ultimately determines how the record state should change.

Note that transitions are not immediate and so, there are several intermediate busy states that exist while we wait for a confirmation from the remote data source. As you can see in the figure, the record will typically be in one of the six static states: READY_NEW, READY_CLEAN, READY_DIRTY, DESTROYED_DIRTY, DESTROYED_CLEAN, or EMPTY. We will look at each of these states in more detail shortly.

Following the life cycle of SC.Record

Loaded records (READY_CLEAN)

The SproutCore data store includes support for loading and unloading records. Loading a record is different from creating a record because the record has already been created somewhere on a remote store and we are just loading it temporarily for use within our application. Likewise, unloading a record is not the same as destroying one because the real remote record is not actually being destroyed.

To load a record, we call loadRecord on the store. This method takes the type of the record and the data hash of the record as arguments. You can also pass the ID as the third argument but in case you don't, it will be looked up from within the data hash according to the record's primaryKey value.

For example:

var storeKey;

storeKey = MyApp.store.loadRecord(MyApp.Calendar, { 
  guid: 'calendar3',
  title: "Birthdays" 
});

As you can see from the state figure, a record that is loaded will initially be in the READY_CLEAN state, which means it is synchronized with the remote data store as far as we know.

The return value for loadRecord is the unique storeKey value for the record. Store keys represent how the store manages its records internally. Because the same ID may be shared between different types of records and because newly created records usually have no ID, the store generates a unique store key to properly identify each record. We will find that we use the store key from time to time when working with the store, and you can retrieve it from the record if you need.

For example:

storeKey = myRecord.get('storeKey'),

Finally, as we will see with each of these methods, there is a multiple record version of loadRecord, which is loadRecords. To use loadRecords or any of the other pluralized methods, we simply pass the same arguments, but within arrays.

Note

You will notice in the figure that loadRecord appears each time with pushRetrieve in parenthesis. This is because the loadRecord method is really a wrapper over pushRetrieve and dataSourceDidComplete (a method we will look at later) and depending on the state of the record, calls the appropriate method.

Unloaded records (EMPTY)

In a lot of SproutCore applications, the number of records can exceed thousands, tens of thousands, and even millions of records. In such a scenario, it is absolutely necessary to also unload records in order to free the memory. To unload a record that has been loaded, we call unloadRecord on the store and pass either the record type and ID or the store key to it.

For example:

MyApp.store.unloadRecord(MyApp.Calendar, 'calendar3'),

This is equivalent to the following code:

MyApp.store.unloadRecord(null, null, aCalendar.get('storeKey'));

Note

Note that once a record is unloaded, it still exists in the store but will be in the EMPTY state and have no data hash. Unloading multiple records is done with unloadRecords.

To be read records (BUSY_LOADING and BUSY_REFRESH)

This sub-section is titled "To be read records" rather than "Reading records" because we aren't doing the actual reading from the remote database directly. Due to this slight semantic difference, the record we receive from the store will either be in the BUSY_LOADING or one of the two BUSY_REFRESH states while it is actually being read from the remote source and sent to us. The following figure shows these additional busy states.

To be read records (BUSY_LOADING and BUSY_REFRESH)

The typical way we request records is with the find method on the store. We can retrieve all records of a type by passing the record type to find. If we pass a specific ID as the second argument, it will return just the matching record of that type. We can also retrieve all records that match certain conditions by passing a query object to find.

For example:

// An SC.RecordArray of event records.
allEvents = MyApp.store.find(MyApp.Event);

// A single event record.
event = MyApp.store.find(MyApp.Event, 'event1'), 

// An SC.RecordArray of event records starting after now.
futureEvents = MyApp.store.find(SC.Query.create({
  recordType: MyApp.Event,
  conditions: "startAt > " + (new Date()).getTime()
});

The previous example introduces an important class in the SproutCore Model layer that we haven't looked at yet, SC.RecordArray. When we retrieve a group of records, the returned object is always a record array, which is like a normal array, but has a status value that we can use to know when the actual array was filled. Record arrays are also able to update their contents live from the store when they are based on local query objects. We'll look more at working with query objects later on in this chapter.

Another thing to know is that the find method is actually just a wrapper over retrieveRecord, materializeRecord, and some internal code for handling query objects. When you call find with a record type and ID, it will call retrieveRecord if the record isn't loaded, in order to retrieve it from the remote source or will call materializeRecord if it is loaded, to return an instance of the record class. While you will likely never use retrieveRecord over find, the plural version retrieveRecords allows you to request a specific set of record IDs at once, which may be of use.

Lastly, the other store method shown in the figure is the refreshRecord method. We can call this on already loaded records in order to re-fetch their data from the remote source. Depending on the current state of the loaded record, this will put the record into the BUSY_REFRESH_CLEAN or BUSY_REFRESH_DIRTY state.

To be created records (READY_NEW)

Creating a new record in the client is simple enough. We simply call createRecord on the store and pass the type of record, the data hash for the record, and optionally an ID for the record. Typically, you will not have an ID for the record yet because assigning unique IDs is usually the job of the remote data store.

For example:

newCalendar = MyApp.store.createRecord(MyApp.Calendar, { title: "Family" });

console.log(newCalendar.toString());
// > MyApp.Calendar({title: Family}) READY_NEW

As you can see from the static record states figure, a call to createRecord is the only entry point to the READY_NEW state. This state indicates that the record can be used but has not yet been created in the remote data store.

Use createRecords to create multiple records at once.

Tip

Remember that we use attribute names and not property names in the data hash. For instance, if the displayPrice property uses the display_price key, the data hash passed to createRecord should contain a display_price attribute.

To be updated records (READY_DIRTY)

Once a record is in the READY_CLEAN state, modifications to its attributes will automatically move it to the READY_DIRTY state. The dirty state indicates that the record is no longer synchronized with the remote data store and that the remote data store will need to be updated.

In the static record state figure, the connection between READY_CLEAN and READY_DIRTY shows the recordDidChange method of the store. While you could call this, you won't typically need to, since it will be called for us by using key-value coding (that is, set) to change the record attributes.

There is also a recordsDidChange method for indicating multiple record changes.

To be destroyed records (DESTROYED_DIRTY)

Destroying a record on the client is again very simple. The store provides the destroyRecord and destroyRecords methods that work almost identically to the unloadRecord and unloadRecords methods. This means that you pass the same arguments to destroyRecord, either the record type and ID or the store key of the record. The difference is that with destroyRecord, the data hash of the record is not immediately cleared and its state becomes DESTROYED_DIRTY, indicating that it needs to be synchronized with the server.

For example:

MyApp.store.destroyRecord(MyApp.Calendar, toBeDestroyedCalendar.get('id')); 

console.log(toBeDestroyedCalendar.toString());
// > MyApp.Calendar({guid: work-calendar, title: Work}) DESTROYED_DIRTY

By the way, we can get the same result by simply calling destroy on the record instance as shown in the following code line:

toBeDestroyedCalendar.destroy(); 

We will look at how to complete the transition between the DESTROYED_DIRTY and DESTROYED_CLEAN states shortly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.198.81