/** @class TODO: Describe Class @extends SC.DataSource @since SproutCore 1.0 */ // For testing SC.DISABLE_REMOTE_REQUESTS = NO; SC.RailsDataSource = SC.DataSource.extend( { _resourcePathFormat: '/sc/%@.json%@', getFullResourcePath: function(resourcePath, queryParams) { var params = ''; if (queryParams) { var i = 0; for (var key in queryParams) { if (queryParams.hasOwnProperty(key)) { params += '%@%@=%@'.fmt(i === 0 ? '?' : '&', key, queryParams[key]); i++; } } } return this._resourcePathFormat.fmt(resourcePath, params); }, // STANDARD DATA SOURCE METHODS // fetch: function(store, query) { var recordType = query.recordType || query.recordTypes[0]; var url = this.getFullResourcePath(recordType.resourcePathFor('fetch')); var fetchParams = { query: query, store: store }; if (!SC.DISABLE_REMOTE_REQUESTS) { return NO; } else { SC.Request.getUrl(url).set('isJSON', YES) .notify(this, this._didFetchRecords, fetchParams) .send(); return YES; // Not necessary, but good form } }, retrieveRecord: function(store, storeKey, id) { // map storeKey back to record type var recordType = SC.Store.recordTypeFor(storeKey); var url = this.getFullResourcePath(recordType.resourcePathFor('retrieve', storeKey)); var retrieveParams = { store: store, storeKey: storeKey }; if (SC.DISABLE_REMOTE_REQUESTS) { return NO; } else { SC.Request.getUrl(url).set('isJSON', YES) .notify(this, this._didRetrieveRecord, retrieveParams) .send(); return YES; } }, createRecord: function(store, storeKey, params) { var dataHash = store.readDataHash(storeKey), recordType = SC.Store.recordTypeFor(storeKey), obj = this._dataToParams(recordType, 'create', dataHash); var url = this.getFullResourcePath(recordType.resourcePathFor('create', storeKey, dataHash)); var createParams = { store: store, storeKey: storeKey }; if (SC.DISABLE_REMOTE_REQUESTS) { this._didCreateRecord(null, createParams); } else { SC.Request.postUrl(url).set('isJSON', YES) .header('X-Requested-With', 'XMLHttpRequest') .notify(this, this._didCreateRecord, createParams) .send(obj); } return YES ; }, updateRecord: function(store, storeKey, params) { if(!params) params = {}; var id = store.idFor(storeKey), dataHash = store.readDataHash(storeKey), recordType = SC.Store.recordTypeFor(storeKey), obj = this._dataToParams(recordType, 'update', dataHash), request; var url = this.getFullResourcePath(recordType.resourcePathFor('update', storeKey)); var updateParams = { store: store, storeKey: storeKey }; if (!SC.DISABLE_REMOTE_REQUESTS) { // Send request unless disabled request = SC.Request.putUrl(url).set('isJSON', YES).header('X-Requested-With', 'XMLHttpRequest'); // Don't notify if skipping response if (!params.skipResponse) request.notify(this, this._didUpdateRecord, updateParams); request.send(obj); } if (SC.DISABLE_REMOTE_REQUESTS || params.skipResponse) { // Don't wait for a server response this._didUpdateRecord(null, updateParams); } return YES ; }, destroyRecord: function(store, storeKey, params) { if (!params) params = {}; var status = store.readStatus(storeKey); if (status === SC.Record.DESTROYED_CLEAN) { // Already clean - probably from calling destroy on a new record // Is there a better place to handle this? store.removeDataHash(storeKey, status); store.dataHashDidChange(storeKey); } else { // Destroy normally var id = store.idFor(storeKey), recordType = SC.Store.recordTypeFor(storeKey), notifyParams = { store: store, storeKey: storeKey }; var url = this.getFullResourcePath(recordType.resourcePathFor('destroy', storeKey)); if (params.noRequest || SC.DISABLE_REMOTE_REQUESTS) { this._didDestroyRecord(null, notifyParams); } else { SC.Request.deleteUrl(url) .header('X-Requested-With', 'XMLHttpRequest') .notify(this, this._didDestroyRecord, notifyParams).send(); } } return YES ; }, cancel: function(store, storeKeys) { return NO; }, _didFetchRecords: function(response, params) { var store = params.store, query = params.query, results = response.get('response'), resultItem, cleanedResults= [], obj; if (SC.$ok(response)) { for(idx=0; idx < results.length; idx++) { resultItem = results[idx]; // Hackish way to ignore key for (var k in resultItem) { obj = resultItem[k]; break; } cleanedResults.push(obj); } // load the contacts into the store... store.loadRecords(params.recordTypes, cleanedResults); // notify store that we handled the fetch store.dataSourceDidFetchQuery(query); } else { // handle error case store.dataSourceDidErrorQuery(query, response); } }, _didRetrieveRecord: function(response, params) { var store = params.store, storeKey = params.storeKey, results = response.get('response'), cleanedResults; if (SC.$ok(response)) { // normal: load into store...response == dataHash // Get first item for (var k in results) { cleanedResults = results[k]; break; } store.dataSourceDidComplete(storeKey, cleanedResults, cleanedResults.id); } else { // error: indicate as such...response == error this._processError(storeKey, response); } }, _didCreateRecord: function(response, params) { var results = response && response.get('response'), storeKey = params.storeKey, store = params.store, resultsKeys= [], cleanedResults; if (!response) { // Request was skipped, pretend it succeeded store.dataSourceDidComplete(storeKey); } else if (SC.$ok(response)) { for(var k in results) resultsKeys.push(k); cleanedResults = results[resultsKeys[0]]; store.dataSourceDidComplete(storeKey, cleanedResults, cleanedResults.id); } else this._processError(storeKey, response); }, _didUpdateRecord: function(response, params){ var storeKey = params.storeKey, store = params.store; if (SC.$ok(response)) { store.dataSourceDidComplete(storeKey); } else this._processError(storeKey, response); }, _didDestroyRecord: function(response, params){ var storeKey = params.storeKey, store = params.store; // Response will be null if no actual request made and SC.$ok will still evaluate true // This is fine since we want to pretend we succeeded anyway if (SC.$ok(response)) { store.dataSourceDidDestroy(storeKey); } else this._processError(storeKey, response); }, _processError: function(storeKey, response) { var status = response.get('status'); if (status === 401) { // Unauthorized SC.AlertPane.error("Not signed in", "You are not signed in. Please sign in again."); } store.dataSourceDidError(storeKey, response); }, _dataToParams: function(recordType, action, data) { var resourceParams = recordType.resourceParamsFor(action), baseParam = recordType.resourceBaseParam, cleanedData = {}, item; cleanedData[baseParam] = {}; for (v in data) { item = data[v]; if(resourceParams.indexOf(v) != -1 && item != null) { if(item == true) item = 1; if(item == false) item = 0; cleanedData[baseParam][v] = item; } } return cleanedData; } });