📄 remotestore.js
字号:
/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml*/dojo.provide("dojo.data.core.RemoteStore");dojo.require("dojo.data.core.Read");dojo.require("dojo.data.core.Write");dojo.require("dojo.data.core.Result");dojo.require("dojo.experimental");dojo.require("dojo.Deferred");dojo.require("dojo.lang.declare");dojo.require("dojo.json");dojo.require("dojo.io.*");/* summary: * RemoteStore is an implemention the dojo.data.core.Read and Write APIs. * It is designed to serve as a base class for dojo.data stores which interact * with stateless web services that can querying and modifying record-oriented * data. Its features include asynchronous and synchronous querying and saving; * caching of queries; transactions; and datatype mapping. *//************************************************************************** Classes derived from RemoteStore should implement the following three methods, which are each described in the documentation below: _setupQueryRequest(result, requestKw) _resultToQueryData(responseData) _setupSaveRequest(saveKeywordArgs, requestKw) Data Consistency Guarantees * if two references to the same item are obtained (e.g. from two different query results) any changes to one item will be reflected in the other item reference. * If an item has changed on the server and the item is retrieved via a new query, any previously obtained references to the item will (silently) reflect these new values. * However, any uncommitted changes will not be "overwritten". * If server queries are made while there are uncommitted changes, no attempt is made to evaluate whether the modifications would change the query result, e.g. add any uncommitted new items that match the query. * However, uncomitted deleted items are removed from the query result. * The transaction isolation level is equivalent to JDBC's "Read Committed": each store instance is treated as separate transaction; since there is no row or table locking so nonrepeatable and phantom reads are possible. Memory Usage Because Javascript doesn't support weak references or user-defined finalize methods, there is a tradeoff between data consistency and memory usage. In order to implement the above consistency guarantees (and to provide caching), RemoteStore remembers all the queries and items retrieved. To reduce memory consumption, use the method forgetResults(query); Store assumptions RemoteStore makes some assumptions about the nature of the remote store, things may break if these aren't true: * that the items contained in a query response include all the attributes of the item (e.g. all the columns of a row). (to fix: changes need to record add and removes and fix self._data[key] = [ attributeDict, refCount]; ) * the query result may contain references to items that are not available to the client; use isItem() to test for the presence of the item. * that modification to an item's attributes won't change it's primary key. **************************************************************************//* dojo.data API issues to resolve: * save should returns a Deferred, might want to add keyword argument with 'sync' */dojo.experimental("dojo.data.core.RemoteStore");dojo.lang.declare("dojo.data.core.RemoteStore", [dojo.data.core.Read, dojo.data.core.Write], { _datatypeMap: { //map datatype strings to constructor function }, //set to customize json serialization _jsonRegistry: dojo.json.jsonRegistry, initializer: function(/* object */ kwArgs) { if (!kwArgs) { kwArgs = {}; } this._serverQueryUrl = kwArgs.queryUrl || ""; this._serverSaveUrl = kwArgs.saveUrl || ""; this._deleted = {}; // deleted items {id: 1} this._changed = {}; // {id: {attr: [new values]}} // [] if attribute is removed this._added = {}; // {id: 1} list of added items this._results = {}; // {query: [ id1, ]}; // todo: make MRUDict of queries /* data is a dictionary that conforms to this format: { id-string: { attribute-string: [ value1, value2 ] } } where value is either an atomic JSON data type or { 'id': string } for references to items or { 'type': 'name', 'value': 'value' } for user-defined datatypes */ this._data = {}; // {id: [values, refcount]} // todo: handle refcount this._numItems = 0; }, _setupQueryRequest: function(/* dojo.data.core.Result */ result, /* object */ requestKw) { /* summary: * Classes derived from RemoteStore should override this method to * provide their own implementations. * This function prepares the query request by populating requestKw, * an associative array that will be passed to dojo.io.bind. */ result.query = result.query || ""; requestKw.url = this._serverQueryUrl + encodeURIComponent(result.query); requestKw.method = 'get'; requestKw.mimetype = "text/json"; }, _resultToQueryMetadata: function(/* varies */ serverResponseData) { /* summary: * Classes derived from RemoteStore should override this method to * provide their own implementations. * Converts the server response data into the resultMetadata object * that will be returned to the caller. * returns: * This simple default implementation just returns the entire raw * serverResponseData, allowing the caller complete access to the * raw response data and metadata. */ return serverResponseData; }, _resultToQueryData: function(/* varies */ serverResponseData) { /* summary: * Classes derived from RemoteStore should override this method to * provide their own implementations. * Converts the server response data into the internal data structure * used by RemoteStore. * returns: * The RemoteStore implementation requires _resultToQueryData() to * return an object that looks like: * {item1-identifier-string: { * attribute1-string: [ value1, value2, ... ], * attribute2-string: [ value3, value4, ... ], * ... * }, * item2-identifier-string: { * attribute1-string: [ value10, value11, ... ], * attribute2-string: [ value12, value13, ... ], * ... * } * } * where value is either an atomic JSON data type or * {'id': string } for references to items * or * {'type': 'name', 'value': 'value' } for user-defined datatypes * data: * This simple default implementation assumes that the *serverResponseData* * argument is an object that looks like: * { data:{ ... }, format:'format identifier', other metadata } * */ return serverResponseData.data; }, _remoteToLocalValues: function(/* object */ attributes) { for (var key in attributes) { var values = attributes[key]; for (var i = 0; i < values.length; i++) { var value = values[i]; var type = value.datatype || value.type; if (type) { // todo: better error handling? var localValue = value.value; if (this._datatypeMap[type]) localValue = this._datatypeMap[type](value); values[i] = localValue; } } } return attributes; // object (attributes argument, modified in-place) }, _queryToQueryKey: function(query) { /* summary: * Convert the query to a string that uniquely represents this query. * (Used by the query cache.) */ if (typeof query == "string") return query; else return dojo.json.serialize(query); }, _assertIsItem: function(/* item */ item) { if (!this.isItem(item)) { throw new Error("dojo.data.RemoteStore: a function was passed an item argument that was not an item"); } }, get: function(/* item */ item, /* attribute || string */ attribute, /* value? */ defaultValue) { // summary: See dojo.data.core.Read.get() var valueArray = this.getValues(item, attribute); if (valueArray.length == 0) { return defaultValue; } return valueArray[0]; // value }, getValues: function(/* item */ item, /* attribute || string */ attribute) { // summary: See dojo.data.core.Read.getValues() var itemIdentity = this.getIdentity(item); this._assertIsItem(itemIdentity); var changes = this._changed[itemIdentity]; if (changes) { var newvalues = changes[attribute]; if (newvalues !== undefined) { return newvalues; // Array } else { return []; // Array } } // return item.atts[attribute]; return this._data[itemIdentity][0][attribute]; // Array }, getAttributes: function(/* item */ item) { // summary: See dojo.data.core.Read.getAttributes() var itemIdentity = this.getIdentity(item); if (!itemIdentity) return undefined; //todo: raise exception var atts = []; //var attrDict = item.attrs; var attrDict = this._data[itemIdentity][0]; for (var att in attrDict) { atts.push(att); } return atts; // Array }, hasAttribute: function(/* item */ item, /* attribute || string */ attribute) { // summary: See dojo.data.core.Read.hasAttribute() var valueArray = this.getValues(item, attribute); return valueArray.length ? true : false; // Boolean }, containsValue: function(/* item */ item, /* attribute || string */ attribute, /* value */ value) { // summary: See dojo.data.core.Read.containsValue() var valueArray = this.getValues(item, attribute); for (var i=0; i < valueArray.length; i++) { if (valueArray[i] == value) { return true; // Boolean } } return false; // Boolean }, isItem: function(/* anything */ something) { // summary: See dojo.data.core.Read.isItem() if (!something) { return false; } var itemIdentity = something; // var id = something.id ? something.id : something; // if (!id) { return false; } if (this._deleted[itemIdentity]) { return false; } //todo: do this? if (this._data[itemIdentity]) { return true; } if (this._added[itemIdentity]) { return true; } return false; // Boolean }, find: function(/* object? || dojo.data.core.Result */ keywordArgs) { // summary: See dojo.data.core.Read.find() /* description: * In addition to the keywordArgs parameters described in the * dojo.data.core.Read.find() documentation, the keywordArgs for * the RemoteStore find() method may include a bindArgs parameter, * which the RemoteStore will pass to dojo.io.bind when it sends * the query. The bindArgs parameter should be a keyword argument * object, as described in the dojo.io.bind documentation. */ var result = null; if (keywordArgs instanceof dojo.data.core.Result) { result = keywordArgs; result.store = this; } else { result = new dojo.data.core.Result(keywordArgs, this); } var query = result.query; //todo: use this._results to implement caching var self = this; var bindfunc = function(type, data, evt) { var scope = result.scope || dj_global; if(type == "load") { //dojo.debug("loaded 1 " + dojo.json.serialize(data) ); result.resultMetadata = self._resultToQueryMetadata(data); var dataDict = self._resultToQueryData(data); //dojo.debug("loaded 2 " + dojo.json.serialize(dataDict) ); if (result.onbegin) { result.onbegin.call(scope, result); } var count = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -