Class: SC.DataSource

A DataSource connects an in-memory store to one or more server backends. To connect to a data backend on a server, subclass SC.DataSource and implement the necessary data source methods to communicate with the particular backend.

Create a Data Source

To implement the data source, subclass SC.DataSource in a file located either in the root level of your app or framework, or in a directory called "data_sources":

MyApp.DataSource = SC.DataSource.extend({
  // implement the data source API...
});

Connect to a Data Source

New SproutCore applications are wired up to fixtures as their data source. When you are ready to connect to a server, swap the use of fixtures with a call to the desired data source.

In core.js:

// change...
store: SC.Store.create().from(SC.Record.fixtures)

// to...
store: SC.Store.create().from('MyApp.DataSource')

Note that the data source class name is referenced by string since the file in which it is defined may not have been loaded yet. The first time a data store tries to access its data source it will look up the class name and instantiate that data source.

Implement the Data Source API

There are three methods that a data store invokes on its data source: fetch() — called the first time you try to find() a query on a store or any time you refresh the record array after that. retrieveRecords() — called when you access an individual record that has not been loaded yet commitRecords() — called if the the store has changes pending and its commitRecords() method is invoked.

The data store will call the commitRecords() method when records need to be created, updated, or deleted. If the server that the data source connects to handles these three actions in a uniform manner, it may be convenient to implement the commitRecords() to handle record creation, updating, and deletion.

However, if the calls the data source will need to make to the server to create, update, and delete records differ from each other to a significant enough degree, it will be more convenient to rely on the default behavior of commitRecords() and instead implement the three methods that it will call by default: createRecords() — called with a list of records that are new and need to be created on the server. updateRecords() — called with a list of records that already exist on the server but that need to be updated. destroyRecords() — called with a list of records that should be deleted on the server.

Multiple records

The retrieveRecords(), createRecords(), updateRecords() and destroyRecords() methods all work on multiple records. If your server API accommodates calls where you can pass a list of records, this might be the best level at which to implement the Data Source API. On the other hand, if the server requires that you send commands for it for individual records, you can rely on the default implementation of these four methods, which will call the following for each individual record, one at a time:

Return Values

All of the methods you implement must return one of three values: - YES — all the records were handled. - NO — none of the records were handled. - SC.MIXED_STATE — some, but not all of the records were handled.

Store Keys

Whenever a data store invokes one of the data source methods it does so with a storeKeys or storeKey argument. Store keys are transient integers assigned to each data hash when it is first loaded into the store. It is used to track data hashes as they move up and down nested stores (even if no associated record is ever created from it).

When passed a storeKey you can use it to retrieve the status, data hash, record type, or record ID, using the following data store methods: readDataHash(storeKey) — returns the data hash associated with a store key, if any. readStatus(storeKey) — returns the current record status associated with the store key. May be SC.Record.EMPTY. SC.Store.recordTypeFor(storeKey) — returns the record type for the associated store key. recordType.idFor(storeKey) — returns the record ID for the associated store key. You must call this method on SC.Record subclass itself, not on an instance of SC.Record.

These methods are safe for reading data from the store. To modify data in the data store you must use the store callbacks described below. The store callbacks will ensure that the record states remain consistent.

Store Callbacks

When a data store calls a data source method, it puts affected records into a BUSY state. To guarantee data integrity and consistency, these records cannot be modified by the rest of the application while they are in the BUSY state.

Because records are "locked" while in the BUSY state, it is the data source's responsibility to invoke a callback on the store for each record or query that was passed to it and that the data source handled. To reduce the amount of work that a data source must do, the data store will automatically unlock the relevant records if the the data source method returned NO, indicating that the records were unhandled.

Although a data source can invoke callback methods at any time, they should usually be invoked after receiving a response from the server. For example, when the data source commits a change to a record by issuing a command to the server, it waits for the server to acknowledge the command before invoking the dataSourceDidComplete() callback.

In some cases a data source may be able to assume a server's response and invoke the callback on the store immediately. This can improve performance because the record can be unlocked right away.

Record-Related Callbacks

When retrieveRecords(), commitRecords(), or any of the related methods are called on a data source, the store puts any records to be handled by the data store in a BUSY state. To release the records the data source must invoke one of the record-related callbacks on the store: dataSourceDidComplete(storeKey,dataHash, id) — the most common callback. You might use this callback when you have retrieved a record to load its contents into the store. The callback tells the store that the data source is finished with the storeKey in question. The dataHash and id arguments are optional and will replace the current dataHash and/or id. Also see "Loading Records" below. dataSourceDidError(storeKey, error) — a data source should call this when a request could not be completed because an error occurred. The error argument is optional and can contain more information about the error. dataSourceDidCancel(storeKey) — a data source should call this when an operation is cancelled for some reason. This could be used when the user is able to cancel an operation that is in progress.

Loading Records into the Store

Instead of orchestrating multiple dataSourceDidComplete() callbacks when loading multiple records, a data source can call the loadRecords() method on the store, passing in a recordType, and array of data hashes, and optionally an array of ids. The loadRecords() method takes care of looking up storeKeys and calling the dataSourceDidComplete() callback as needed.

loadRecords() is often the most convenient way to get large blocks of data into the store, especially in response to a fetch() or retrieveRecords() call.

Query-Related Callbacks

Like records, queries that are passed through the fetch() method also have an associated status property; accessed through the status property on the record array returned from find(). To properly reset this status, a data source must invoke an appropriate query-related callback on the store. The callbacks for queries are similar to those for records: dataSourceDidFetchQuery(query) — the data source must call this when it has completed fetching any related data for the query. This returns the query results (i.e. the record array) status into a READY state. If the query is a 'remote' type, the ordered array of store keys representing the results from the server must be passed as a second argument. dataSourceDidErrorQuery(query, error) — the data source should call this if it encounters an error in executing the query. This puts the query results into an ERROR state. dataSourceDidCancelQuery(query) — the data source should call this if loading the results is cancelled.

Defined in: data_source.js

Since:
SproutCore 1.0

Instance Methods

Instance Method Detail

cancel(store, storeKeys)

Invoked by the store whenever it needs to cancel one or more records that are currently in-flight. If any of the storeKeys match records you are currently acting upon, you should cancel the in-progress operation and return YES.

If you implement an in-memory data source that immediately services the other requests, then this method will never be called on your data source.

To support cascading data stores, be sure to return NO if you cannot retrieve any of the keys, YES if you can retrieve all of the, or SC.MIXED_STATE if you can retrieve some of the.

Parameters:
store SC.Store
the requesting store
storeKeys Array
array of storeKeys to retrieve
Returns:
Boolean
YES if data source can handle keys
commitRecords(store, createStoreKeys, updateStoreKeys, destroyStoreKeys, params)

Invoked by the store whenever it has one or more records with pending changes that need to be sent back to the server. The store keys will be separated into three categories:

  • createStoreKeys: records that need to be created on server
  • updateStoreKeys: existing records that have been modified
  • destroyStoreKeys: records need to be destroyed on the server

If you do not override this method yourself, this method will actually invoke createRecords(), updateRecords(), and destroyRecords() on the dataSource, passing each array of storeKeys. You can usually implement those methods instead of overriding this method.

However, if your server API can sync multiple changes at once, you may prefer to override this method instead.

To support cascading data stores, be sure to return NO if you cannot handle any of the keys, YES if you can handle all of the keys, or SC.MIXED_STATE if you can handle some of them.

Parameters:
store SC.Store
the requesting store
createStoreKeys Array
keys to create
updateStoreKeys Array
keys to update
destroyStoreKeys Array
keys to destroy
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES if data source can handle keys
createRecord(store, storeKey, params)

Called from createdRecords() to created a single record. This is the most basic primitive to can implement to support creating a record.

To support cascading data stores, be sure to return NO if you cannot handle the passed storeKey or YES if you can.

Parameters:
store SC.Store
the requesting store
storeKey Array
key to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES if handled
createRecords(store, storeKeys, params)

Called from commitRecords() to commit newly created records to the store. You can override this method to actually send the created records to your store. The default version will simply call createRecord() for each storeKey.

To support cascading data stores, be sure to return NO if you cannot handle any of the keys, YES if you can handle all of the keys, or SC.MIXED_STATE if you can handle some of them.

Parameters:
store SC.Store
the requesting store
storeKeys Array
keys to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES, NO, or SC.MIXED_STATE
destroyRecord(store, storeKey, params)

Called from destroyRecords() to destroy a single record. This is the most basic primitive to can implement to support destroying a record.

To support cascading data stores, be sure to return NO if you cannot handle the passed storeKey or YES if you can.

Parameters:
store SC.Store
the requesting store
storeKey Array
key to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES if handled
destroyRecords(store, storeKeys, params)

Called from commitRecords() to commit destroyed records to the store. You can override this method to actually send the destroyed records to your store. The default version will simply call destroyRecord() for each storeKey.

To support cascading data stores, be sure to return NO if you cannot handle any of the keys, YES if you can handle all of the keys, or SC.MIXED_STATE if you can handle some of them.

Parameters:
store SC.Store
the requesting store
storeKeys Array
keys to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES, NO, or SC.MIXED_STATE
fetch(store, query)

Invoked by the store whenever it needs to retrieve data matching a specific query, triggered by find(). This method is called anytime you invoke SC.Store#find() with a query or SC.RecordArray#refresh(). You should override this method to actually retrieve data from the server needed to fulfill the query. If the query is a remote query, then you will also need to provide the contents of the query as well.

Handling Local Queries

Most queries you create in your application will be local queries. Local queries are populated automatically from whatever data you have in memory. When your fetch() method is called on a local queries, all you need to do is load any records that might be matched by the query into memory.

The way you choose which queries to fetch is up to you, though usually it can be something fairly straightforward such as loading all records of a specified type.

When you finish loading any data that might be required for your query, you should always call SC.Store#dataSourceDidFetchQuery() to put the query back into the READY state. You should call this method even if you choose not to load any new data into the store in order to notify that the store that you think it is ready to return results for the query.

Handling Remote Queries

Remote queries are special queries whose results will be populated by the server instead of from memory. Usually you will only need to use this type of query when loading large amounts of data from the server.

Like local queries, to fetch a remote query you will need to load any data you need to fetch from the server and add the records to the store. Once you are finished loading this data, however, you must also call SC.Store#dataSourceDidFetchQuery() with the array of storeKeys that represent the latest results from the server.

If you want to support incremental loading from the server for remote queries, you can do so by passing a SC.SparseArray instance instead of a regular array of storeKeys and then populate the sparse array on demand.

Handling Errors and Cancellations

If you encounter an error while trying to fetch the results for a query you can call SC.Store#dataSourceDidErrorQuery() instead. This will put the query results into an error state.

If you had to cancel fetching a query before the results were returned, you can instead call SC.Store#dataSourceDidCancelQuery(). This will set the query back into the state it was in previously before it started loading the query.

Return Values

When you return from this method, be sure to return a Boolean. YES means you handled the query, NO means you can't handle the query. When using a cascading data source, returning NO will mean the next data source will be asked to fetch the same results as well.

Parameters:
store SC.Store
the requesting store
query SC.Query
query describing the request
Returns:
Boolean
YES if you can handle fetching the query, NO otherwise
retrieveRecord(store, storeKey, id)
Called from `retrieveRecords()` to retrieve a single record.
Parameters:
store SC.Store
the requesting store
storeKey Array
key to retrieve
id String
the id to retrieve
Returns:
Boolean
YES if handled
retrieveRecords(store, storeKeys, ids)

Called by the store whenever it needs to load a specific set of store keys. The default implementation will call retrieveRecord() for each storeKey.

You should implement either retrieveRecord() or retrieveRecords() to actually fetch the records referenced by the storeKeys .

Parameters:
store SC.Store
the requesting store
storeKeys Array
ids Array
- optional
Returns:
Boolean
YES if handled, NO otherwise
updateRecord(store, storeKey, params)

Called from updatesRecords() to update a single record. This is the most basic primitive to can implement to support updating a record.

To support cascading data stores, be sure to return NO if you cannot handle the passed storeKey or YES if you can.

Parameters:
store SC.Store
the requesting store
storeKey Array
key to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES if handled
updateRecords(store, storeKeys, params)

Called from commitRecords() to commit modified existing records to the store. You can override this method to actually send the updated records to your store. The default version will simply call updateRecord() for each storeKey.

To support cascading data stores, be sure to return NO if you cannot handle any of the keys, YES if you can handle all of the keys, or SC.MIXED_STATE if you can handle some of them.

Parameters:
store SC.Store
the requesting store
storeKeys Array
keys to update
params Hash
to be passed down to data source. originated from the commitRecords() call on the store
Returns:
Boolean
YES, NO, or SC.MIXED_STATE
Documentation generated by JsDoc Toolkit 2.4.0 on Wed Apr 08 2015 10:02:20 GMT-0600 (CST)