/ext-4.1.0_b3/src/data/Operation.js

https://bitbucket.org/srogerf/javascript · JavaScript · 354 lines · 124 code · 42 blank · 188 comment · 22 complexity · d43dfe82b132bffe12af51ad52a05de4 MD5 · raw file

  1. /**
  2. * @author Ed Spencer
  3. *
  4. * Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
  5. * used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
  6. * Operation objects directly.
  7. *
  8. * Several Operations can be batched together in a {@link Ext.data.Batch batch}.
  9. */
  10. Ext.define('Ext.data.Operation', {
  11. /**
  12. * @cfg {Boolean} synchronous
  13. * True if this Operation is to be executed synchronously. This property is inspected by a
  14. * {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
  15. */
  16. synchronous: true,
  17. /**
  18. * @cfg {String} action
  19. * The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
  20. */
  21. action: undefined,
  22. /**
  23. * @cfg {Ext.util.Filter[]} filters
  24. * Optional array of filter objects. Only applies to 'read' actions.
  25. */
  26. filters: undefined,
  27. /**
  28. * @cfg {Ext.util.Sorter[]} sorters
  29. * Optional array of sorter objects. Only applies to 'read' actions.
  30. */
  31. sorters: undefined,
  32. /**
  33. * @cfg {Ext.util.Grouper[]} groupers
  34. * Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
  35. */
  36. groupers: undefined,
  37. /**
  38. * @cfg {Number} start
  39. * The start index (offset), used in paging when running a 'read' action.
  40. */
  41. start: undefined,
  42. /**
  43. * @cfg {Number} limit
  44. * The number of records to load. Used on 'read' actions when paging is being used.
  45. */
  46. limit: undefined,
  47. /**
  48. * @cfg {Ext.data.Batch} batch
  49. * The batch that this Operation is a part of.
  50. */
  51. batch: undefined,
  52. /**
  53. * @cfg {Function} callback
  54. * Function to execute when operation completed.
  55. * @cfg {Ext.data.Model[]} callback.records Array of records.
  56. * @cfg {Ext.data.Operation} callback.operation The Operation itself.
  57. * @cfg {Boolean} callback.success True when operation completed successfully.
  58. */
  59. callback: undefined,
  60. /**
  61. * @cfg {Object} scope
  62. * Scope for the {@link #callback} function.
  63. */
  64. scope: undefined,
  65. /**
  66. * @property {Boolean} started
  67. * The start status of this Operation. Use {@link #isStarted}.
  68. * @readonly
  69. * @private
  70. */
  71. started: false,
  72. /**
  73. * @property {Boolean} running
  74. * The run status of this Operation. Use {@link #isRunning}.
  75. * @readonly
  76. * @private
  77. */
  78. running: false,
  79. /**
  80. * @property {Boolean} complete
  81. * The completion status of this Operation. Use {@link #isComplete}.
  82. * @readonly
  83. * @private
  84. */
  85. complete: false,
  86. /**
  87. * @property {Boolean} success
  88. * Whether the Operation was successful or not. This starts as undefined and is set to true
  89. * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
  90. * {@link #wasSuccessful} to query success status.
  91. * @readonly
  92. * @private
  93. */
  94. success: undefined,
  95. /**
  96. * @property {Boolean} exception
  97. * The exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
  98. * @readonly
  99. * @private
  100. */
  101. exception: false,
  102. /**
  103. * @property {String/Object} error
  104. * The error object passed when {@link #setException} was called. This could be any object or primitive.
  105. * @private
  106. */
  107. error: undefined,
  108. /**
  109. * @property {RegExp} actionCommitRecordsRe
  110. * The RegExp used to categorize actions that require record commits.
  111. */
  112. actionCommitRecordsRe: /^(?:create|update)$/i,
  113. /**
  114. * @property {RegExp} actionSkipSyncRe
  115. * The RegExp used to categorize actions that skip local record synchronization. This defaults
  116. * to match 'destroy'.
  117. */
  118. actionSkipSyncRe: /^destroy$/i,
  119. /**
  120. * Creates new Operation object.
  121. * @param {Object} config (optional) Config object.
  122. */
  123. constructor: function(config) {
  124. Ext.apply(this, config || {});
  125. },
  126. /**
  127. * This method is called to commit data to this instance's records given the records in
  128. * the server response. This is followed by calling {@link Ext.data.Model#commit} on all
  129. * those records (for 'create' and 'update' actions).
  130. *
  131. * If this {@link #action} is 'destroy', any server records are ignored and the
  132. * {@link Ext.data.Model#commit} method is not called.
  133. *
  134. * @param {Ext.data.Model[]} serverRecords An array of {@link Ext.data.Model} objects returned by
  135. * the server.
  136. * @markdown
  137. */
  138. commitRecords: function (serverRecords) {
  139. var me = this,
  140. mc, index, clientRecords, serverRec, clientRec;
  141. if (!me.actionSkipSyncRe.test(me.action)) {
  142. clientRecords = me.records;
  143. if (clientRecords && clientRecords.length) {
  144. if(clientRecords.length > 1) {
  145. // if this operation has multiple records, client records need to be matched up with server records
  146. // so that any data returned from the server can be updated in the client records.
  147. mc = new Ext.util.MixedCollection();
  148. mc.addAll(serverRecords);
  149. for (index = clientRecords.length; index--; ) {
  150. clientRec = clientRecords[index];
  151. serverRec = mc.findBy(function(record) {
  152. var clientRecordId = clientRec.getId();
  153. if(clientRecordId && record.getId() === clientRecordId) {
  154. return true;
  155. }
  156. // if the server record cannot be found by id, find by internalId.
  157. // this allows client records that did not previously exist on the server
  158. // to be updated with the correct server id and data.
  159. return record.internalId === clientRec.internalId;
  160. });
  161. // replace client record data with server record data
  162. me.updateClientRecord(clientRec, serverRec);
  163. }
  164. } else {
  165. // operation only has one record, so just match the first client record up with the first server record
  166. clientRec = clientRecords[0];
  167. serverRec = serverRecords[0];
  168. // if the client record is not a phantom, make sure the ids match before replacing the client data with server data.
  169. if(serverRec && (clientRec.phantom || clientRec.getId() === serverRec.getId())) {
  170. me.updateClientRecord(clientRec, serverRec);
  171. }
  172. }
  173. if (me.actionCommitRecordsRe.test(me.action)) {
  174. for (index = clientRecords.length; index--; ) {
  175. clientRecords[index].commit();
  176. }
  177. }
  178. }
  179. }
  180. },
  181. /**
  182. * Replaces the data in a client record with the data from a server record. If either record is undefined, does nothing.
  183. * Since non-persistent fields will have default values in the server record, this method only replaces data for persistent
  184. * fields to avoid overwriting the client record's data with default values from the server record.
  185. * @private
  186. * @param {Ext.data.Model} [clientRecord]
  187. * @param {Ext.data.Model} [serverRecord]
  188. */
  189. updateClientRecord: function(clientRecord, serverRecord) {
  190. if (clientRecord && serverRecord) {
  191. clientRecord.beginEdit();
  192. var fields = clientRecord.fields.items,
  193. fLen = fields.length,
  194. field, f;
  195. for (f = 0; f < fLen; f++) {
  196. field = fields[f];
  197. if (field.persist) {
  198. clientRecord.set(field.name, serverRecord.get(field.name));
  199. }
  200. }
  201. if(clientRecord.phantom) {
  202. clientRecord.setId(serverRecord.getId());
  203. }
  204. clientRecord.endEdit(true);
  205. }
  206. },
  207. /**
  208. * Marks the Operation as started.
  209. */
  210. setStarted: function() {
  211. this.started = true;
  212. this.running = true;
  213. },
  214. /**
  215. * Marks the Operation as completed.
  216. */
  217. setCompleted: function() {
  218. this.complete = true;
  219. this.running = false;
  220. },
  221. /**
  222. * Marks the Operation as successful.
  223. */
  224. setSuccessful: function() {
  225. this.success = true;
  226. },
  227. /**
  228. * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
  229. * @param {String/Object} error (optional) error string/object
  230. */
  231. setException: function(error) {
  232. this.exception = true;
  233. this.success = false;
  234. this.running = false;
  235. this.error = error;
  236. },
  237. /**
  238. * Returns true if this Operation encountered an exception (see also {@link #getError})
  239. * @return {Boolean} True if there was an exception
  240. */
  241. hasException: function() {
  242. return this.exception === true;
  243. },
  244. /**
  245. * Returns the error string or object that was set using {@link #setException}
  246. * @return {String/Object} The error object
  247. */
  248. getError: function() {
  249. return this.error;
  250. },
  251. /**
  252. * Returns the {@link Ext.data.Model record}s associated with this operation. For read operations the records as set by the {@link Ext.data.proxy.Proxy Proxy} will be returned (returns `null` if the proxy has not yet set the records).
  253. * For create, update, and destroy operations the operation's initially configured records will be returned, although the proxy may modify these records' data at some point after the operation is initialized.
  254. * @return {Ext.data.Model[]}
  255. */
  256. getRecords: function() {
  257. var resultSet = this.getResultSet();
  258. return this.records || (resultSet ? resultSet.records : null);
  259. },
  260. /**
  261. * Returns the ResultSet object (if set by the Proxy). This object will contain the {@link Ext.data.Model model}
  262. * instances as well as meta data such as number of instances fetched, number available etc
  263. * @return {Ext.data.ResultSet} The ResultSet object
  264. */
  265. getResultSet: function() {
  266. return this.resultSet;
  267. },
  268. /**
  269. * Returns true if the Operation has been started. Note that the Operation may have started AND completed, see
  270. * {@link #isRunning} to test if the Operation is currently running.
  271. * @return {Boolean} True if the Operation has started
  272. */
  273. isStarted: function() {
  274. return this.started === true;
  275. },
  276. /**
  277. * Returns true if the Operation has been started but has not yet completed.
  278. * @return {Boolean} True if the Operation is currently running
  279. */
  280. isRunning: function() {
  281. return this.running === true;
  282. },
  283. /**
  284. * Returns true if the Operation has been completed
  285. * @return {Boolean} True if the Operation is complete
  286. */
  287. isComplete: function() {
  288. return this.complete === true;
  289. },
  290. /**
  291. * Returns true if the Operation has completed and was successful
  292. * @return {Boolean} True if successful
  293. */
  294. wasSuccessful: function() {
  295. return this.isComplete() && this.success === true;
  296. },
  297. /**
  298. * @private
  299. * Associates this Operation with a Batch
  300. * @param {Ext.data.Batch} batch The batch
  301. */
  302. setBatch: function(batch) {
  303. this.batch = batch;
  304. },
  305. /**
  306. * Checks whether this operation should cause writing to occur.
  307. * @return {Boolean} Whether the operation should cause a write to occur.
  308. */
  309. allowWrite: function() {
  310. return this.action != 'read';
  311. }
  312. });