1 // ========================================================================== 2 // Project: The M-Project - Mobile HTML5 Application Framework 3 // Copyright: (c) 2011 panacoda GmbH. All rights reserved. 4 // Creator: Dominik 5 // Date: 26.07.2011 6 // License: Dual licensed under the MIT or GPL Version 2 licenses. 7 // http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE 8 // http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE 9 // ========================================================================== 10 11 m_require('core/utility/logger.js'); 12 13 /** 14 * @class 15 * 16 * A data consumer can be called a read-only data provider. It's only job is it to retrieve some data form 17 * remote services, e.g. a webservice, and to push them into the store. 18 * 19 * Note: So far we only support data in JSON format! 20 * 21 * @extends M.Object 22 */ 23 M.DataConsumer = M.Object.extend( 24 /** @scope M.DataConsumer.prototype */ { 25 26 /** 27 * The type of this object. 28 * 29 * @type String 30 */ 31 type: 'M.DataConsumer', 32 33 /** 34 * This property can be used to specify the path to the desired data within 35 * the response. Simply name the path by concatenating the path parts with 36 * a '.', e.g.: 'path.to.my.desired.response'. 37 * 38 * @type String 39 */ 40 responsePath: null, 41 42 /** 43 * This property specifies the used http method for the request. By default 44 * GET is used. 45 * 46 * @type String 47 */ 48 httpMethod: 'GET', 49 50 /** 51 * This property can be used to specify whether or not to append any fetched 52 * data sets to the existing records. If set to NO, the model's records are 53 * removed whenever the find() method is called. 54 * 55 * @type Boolean 56 */ 57 appendRecords: YES, 58 59 /** 60 * The urlParams property will be pushed to the url() method of your data 61 * consumer. This should look like: 62 * 63 * url: function(query, rpp) { 64 * return 'http://www.myserver.com/request?query=' + query + '&rpp=' + rpp 65 * } 66 * 67 * @type String 68 */ 69 urlParams: null, 70 71 /** 72 * Use this method within your model to configure the data consumer. Set 73 * resp. override all the default object's properties, e.g.: 74 * 75 * { 76 * urlParams: { 77 * query: 'html5', 78 * rpp: 10 79 * }, 80 * appendRecords: YES, 81 * callbacks: { 82 * success: { 83 * target: MyApp.MyController, 84 * action: 'itWorked' 85 * }, 86 * error: { 87 * action: function(e) { 88 * console.log(e); 89 * } 90 * } 91 * }, 92 * map: function(obj) { 93 * return { 94 * userName: obj.from_user, 95 * userImage: obj.profile_image_url, 96 * createdAt: obj.created_at, 97 * tweet: obj.text 98 * }; 99 * } 100 * } 101 * 102 * @param {Object} obj The configuration parameters for the data consumer. 103 */ 104 configure: function(obj) { 105 return this.extend(obj); 106 }, 107 108 /** 109 * This method is automatically called by the model, if you call the model's 110 * find(). To execute the data consuming processs imply pass along an object 111 * specifying the call's parameters as follows: 112 * 113 * { 114 * urlParams: { 115 * query: 'html5', 116 * rpp: 10 117 * } 118 * } 119 * 120 * These parameters will automatically be added to the url, using the 121 * url() method of your data consumer. 122 * 123 * Depending on the success/failure of the call, the specified success 124 * resp. error callback will be called. 125 * 126 * @param {Object} obj The options for the call. 127 */ 128 find: function(obj) { 129 this.include(obj); 130 131 var that = this; 132 M.Request.init({ 133 url: this.bindToCaller(this, this.url, _.toArray(this.urlParams))(), 134 isJSON: YES, 135 callbacks: { 136 success: { 137 target: this, 138 action: function(data, message, request){ 139 /* if no data was returned, skip this */ 140 if(data) { 141 /* apply response path */ 142 if(this.responsePath) { 143 var responsePath = this.responsePath.split('.'); 144 _.each(responsePath, function(subPath) { 145 data = data[subPath]; 146 }); 147 } 148 149 /* if no data was found inside responsePath, skip */ 150 if(data && !_.isArray(data) || _.isArray(data) && data.length > 0) { 151 /* make sure we've got an array */ 152 if(!_.isArray(data)) { 153 data = [data]; 154 } 155 156 /* apply map function and create a record for all data sets */ 157 var records = []; 158 _.each(data, function(d) { 159 var record = obj.model.createRecord(that.map(d)); 160 records.push(record); 161 }); 162 163 /* call callback */ 164 if(this.callbacks && M.EventDispatcher.checkHandler(this.callbacks['success'])) { 165 M.EventDispatcher.callHandler(this.callbacks['success'], null, NO, [records]); 166 } 167 } else { 168 /* log message, that there were no data sets found in given response path */ 169 M.Logger.log('There were no data sets found in response path \'' + this.responsePath + '\'.', M.INFO); 170 171 /* call callback */ 172 if(this.callbacks && M.EventDispatcher.checkHandler(this.callbacks['success'])) { 173 M.EventDispatcher.callHandler(this.callbacks['success'], null, NO, [[]]); 174 } 175 } 176 } else { 177 /* log message, this there were no data sets returned */ 178 M.Logger.log('There was no data returned for url \'' + this.bindToCaller(this, this.url, _.toArray(this.urlParams))() + '\'.', M.INFO); 179 180 /* call callback */ 181 if(this.callbacks && M.EventDispatcher.checkHandler(this.callbacks['success'])) { 182 M.EventDispatcher.callHandler(this.callbacks['success'], null, NO, [[]]); 183 } 184 } 185 } 186 }, 187 error: { 188 target: this, 189 action: function(request, message){ 190 /* call callback */ 191 if(this.callbacks && M.EventDispatcher.checkHandler(this.callbacks['error'])) { 192 M.EventDispatcher.callHandler(this.callbacks['error'], null, NO, message); 193 } 194 } 195 } 196 } 197 }).send(); 198 }, 199 200 /** 201 * Override this method within the data consumer's configuration, to map 202 * the response object to your model's properties as follows: 203 * 204 * map: function(obj) { 205 * return { 206 * userName: obj.from_user, 207 * userImage: obj.profile_image_url, 208 * createdAt: obj.created_at, 209 * tweet: obj.text 210 * }; 211 * } 212 * 213 * @param {Object} obj The response object. 214 * @interface 215 */ 216 map: function(obj) { 217 // needs to be implemented by concrete data consumer object 218 }, 219 220 /** 221 * Override this method within the data consumer's configuration, to tell 222 * the component which url to connect to and with which parameters as 223 * follows: 224 * 225 * url: function(query, rpp) { 226 * return 'http://www.myserver.com/request?query=' + query + '&rpp=' + rpp 227 * } 228 * 229 * The parameters passed to this method are defined by the configuration 230 * of your data consumer. See the urlParams property for further information 231 * about that. 232 * 233 * @interface 234 */ 235 url: function() { 236 // needs to be implemented by concrete data consumer object 237 } 238 239 });