1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 sc_require('data_sources/data_source'); 9 10 /** @class 11 12 A cascading data source will actually forward requests onto an array of 13 additional data sources, stopping when one of the data sources returns YES, 14 indicating that it handled the request. 15 16 You can use a cascading data source to tie together multiple data sources, 17 treating them as a single namespace. 18 19 ## Configuring a Cascade Data Source 20 21 You will usually define your cascading data source in your main method after 22 all the classes you have are loaded. 23 24 MyApp.dataSource = SC.CascadeDataSource.create({ 25 dataSources: "prefs youtube photos".w(), 26 27 prefs: MyApp.PrefsDataSource.create({ root: "/prefs" }), 28 youtube: YouTube.YouTubeDataSource.create({ apiKey: "123456" }), 29 photos: MyApp.PhotosDataSource.create({ root: "photos" }) 30 31 }); 32 33 MyApp.store.set('dataSource', MyApp.dataSource); 34 35 Note that the order you define your dataSources property will determine the 36 order in which requests will cascade from the store. 37 38 Alternatively, you can use a more jQuery-like API for defining your data 39 sources: 40 41 MyApp.dataSource = SC.CascadeDataSource.create() 42 .from(MyApp.PrefsDataSource.create({ root: "/prefs" })) 43 .from(YouTube.YouTubeDataSource.create({ apiKey: "123456" })) 44 .from(MyApp.PhotosDataSource.create({ root: "photos" })); 45 46 MyApp.store.set('dataSource', MyApp.dataSource); 47 48 In this case, the order you call from() will determine the order the request 49 will cascade. 50 51 @extends SC.DataSource 52 @since SproutCore 1.0 53 */ 54 SC.CascadeDataSource = SC.DataSource.extend( 55 /** @scope SC.CascadeDataSource.prototype */ { 56 57 /** 58 The data sources used by the cascade, in the order that they are to be 59 followed. Usually when you define the cascade, you will define this 60 array. 61 62 @type Array 63 */ 64 dataSources: null, 65 66 /** 67 Add a data source to the list of sources to use when cascading. Used to 68 build the data source cascade effect. 69 70 @param {SC.DataSource} dataSource a data source instance to add. 71 @returns {SC.CascadeDataSource} receiver 72 */ 73 from: function(dataSource) { 74 var dataSources = this.get('dataSources'); 75 if (!dataSources) this.set('dataSources', dataSources = []); 76 dataSources.push(dataSource); 77 return this ; 78 }, 79 80 // .......................................................... 81 // SC.STORE ENTRY POINTS 82 // 83 84 /** @private - just cascades */ 85 fetch: function(store, query) { 86 var sources = this.get('dataSources'), 87 len = sources ? sources.length : 0, 88 ret = NO, 89 cur, source, idx; 90 91 for(idx=0; (ret !== YES) && idx<len; idx++) { 92 source = sources.objectAt(idx); 93 cur = source.fetch ? source.fetch.apply(source, arguments) : NO; 94 ret = this._handleResponse(ret, cur); 95 } 96 97 return ret ; 98 }, 99 100 101 /** @private - just cascades */ 102 retrieveRecords: function(store, storeKeys, ids) { 103 var sources = this.get('dataSources'), 104 len = sources ? sources.length : 0, 105 ret = NO, 106 cur, source, idx; 107 108 for(idx=0; (ret !== YES) && idx<len; idx++) { 109 source = sources.objectAt(idx); 110 cur = source.retrieveRecords.apply(source, arguments); 111 ret = this._handleResponse(ret, cur); 112 } 113 114 return ret ; 115 }, 116 117 /** @private - just cascades */ 118 commitRecords: function(store, createStoreKeys, updateStoreKeys, destroyStoreKeys, params) { 119 var sources = this.get('dataSources'), 120 len = sources ? sources.length : 0, 121 ret = NO, 122 cur, source, idx; 123 124 for(idx=0; (ret !== YES) && idx<len; idx++) { 125 source = sources.objectAt(idx); 126 cur = source.commitRecords.apply(source, arguments); 127 ret = this._handleResponse(ret, cur); 128 } 129 130 return ret ; 131 }, 132 133 /** @private - just cascades */ 134 cancel: function(store, storeKeys) { 135 var sources = this.get('dataSources'), 136 len = sources ? sources.length : 0, 137 ret = NO, 138 cur, source, idx; 139 140 for(idx=0; (ret !== YES) && idx<len; idx++) { 141 source = sources.objectAt(idx); 142 cur = source.cancel.apply(source, arguments); 143 ret = this._handleResponse(ret, cur); 144 } 145 146 return ret ; 147 }, 148 149 // .......................................................... 150 // INTERNAL SUPPORT 151 // 152 153 /** @private */ 154 init: function() { 155 sc_super(); 156 157 // if a dataSources array is defined, look for any strings and lookup 158 // the same on the data source. Replace. 159 var sources = this.get('dataSources'), 160 idx = sources ? sources.get('length') : 0, 161 source; 162 while(--idx>=0) { 163 source = sources[idx]; 164 if (SC.typeOf(source) === SC.T_STRING) sources[idx] = this.get(source); 165 } 166 167 }, 168 169 /** @private - Determine the proper return value. */ 170 _handleResponse: function(current, response) { 171 if (response === YES) return YES ; 172 else if (current === NO) return (response === NO) ? NO : SC.MIXED_STATE ; 173 else return SC.MIXED_STATE ; 174 } 175 176 }); 177