    /**
     * @author Ed Spencer
     *
     * In-memory proxy. This proxy simply uses a local variable for data storage/retrieval, so its contents are lost on
     * every page refresh.
     *
     * Usually this Proxy isn't used directly, serving instead as a helper to a {@link Ext.data.Store Store} where a reader
     * is required to load data. For example, say we have a Store for a User model and have some inline data we want to
     * load, but this data isn't in quite the right format: we can use a MemoryProxy with a JsonReader to read it into our
     * Store:
     *
     *     //this is the model we will be using in the store
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *         fields: [
     *             {name: 'id',    type: 'int'},
     *             {name: 'name',  type: 'string'},
     *             {name: 'phone', type: 'string', mapping: 'phoneNumber'}
     *         ]
     *     });
     *
     *     //this data does not line up to our model fields - the phone field is called phoneNumber
     *     var data = {
     *         users: [
     *             {
     *                 id: 1,
     *                 name: 'Ed Spencer',
     *                 phoneNumber: '555 1234'
     *             },
     *             {
     *                 id: 2,
     *                 name: 'Abe Elias',
     *                 phoneNumber: '666 1234'
     *             }
     *         ]
     *     };
     *
     *     //note how we set the 'root' in the reader to match the data structure above
     *     var store = Ext.create('Ext.data.Store', {
     *         autoLoad: true,
     *         model: 'User',
     *         data : data,
     *         proxy: {
     *             type: 'memory',
     *             reader: {
     *                 type: 'json',
     *                 root: 'users'
     *             }
     *         }
     *     });
     */
    Ext.define('Ext.data.proxy.Memory', {
        extend: 'Ext.data.proxy.Client',
        alias: 'proxy.memory',
        alternateClassName: 'Ext.data.MemoryProxy',

        /**
         * @cfg {Boolean} [enablePaging=false]
         * Configure as `true` to enable this MemoryProxy to honour a read operation's `start` and `limit` options.
         *
         * When `true`, read operations will be able to read *pages* of records from the data object.
         */

        /**
         * @cfg {Object} data
         * Optional data to pass to configured Reader.
         */

        constructor: function(config) {
            this.callParent([config]);

            //ensures that the reader has been instantiated properly
            this.setReader(this.reader);
        },

        /**
         * @private
         * Fake processing function to commit the records, set the current operation
         * to successful and call the callback if provided. This function is shared
         * by the create, update and destroy methods to perform the bare minimum
         * processing required for the proxy to register a result from the action.
         */
        updateOperation: function(operation, callback, scope) {
            var i = 0,
                recs = operation.getRecords(),
                len = recs.length;

            for (i; i < len; i++) {
                recs[i].commit();
            }
            operation.setCompleted();
            operation.setSuccessful();

            Ext.callback(callback, scope || this, [operation]);
        },

        /**
         * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
         * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
         * there is no real back end in this case there's not much else to do. This method can be easily overridden to
         * implement more complex logic if needed.
         * @param {Ext.data.Operation} operation The Operation to perform
         * @param {Function} callback Callback function to be called when the Operation has completed (whether
         * successful or not)
         * @param {Object} scope Scope to execute the callback function in
         * @method
         */
        create: function() {
            this.updateOperation.apply(this, arguments);
        },

        /**
         * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
         * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
         * there is no real back end in this case there's not much else to do. This method can be easily overridden to
         * implement more complex logic if needed.
         * @param {Ext.data.Operation} operation The Operation to perform
         * @param {Function} callback Callback function to be called when the Operation has completed (whether
         * successful or not)
         * @param {Object} scope Scope to execute the callback function in
         * @method
         */
        update: function() {
            this.updateOperation.apply(this, arguments);
        },

        /**
         * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
         * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
         * there is no real back end in this case there's not much else to do. This method can be easily overridden to
         * implement more complex logic if needed.
         * @param {Ext.data.Operation} operation The Operation to perform
         * @param {Function} callback Callback function to be called when the Operation has completed (whether
         * successful or not)
         * @param {Object} scope Scope to execute the callback function in
         * @method
         */
        destroy: function() {
            this.updateOperation.apply(this, arguments);
        },

        /**
         * Reads data from the configured {@link #data} object. Uses the Proxy's {@link #reader}, if present.
         * @param {Ext.data.Operation} operation The read Operation
         * @param {Function} callback The callback to call when reading has completed
         * @param {Object} scope The scope to call the callback function in
         */
        read: function(operation, callback, scope) {
            var me = this,
                resultSet = operation.resultSet = me.getReader().read(me.data),
                records = resultSet.records,
                sorters = operation.sorters,
                groupers = operation.groupers,
                filters = operation.filters;

            operation.setCompleted();

            // Apply filters, sorters, and start/limit options
            if (resultSet.success) {

                // Filter the resulting array of records
                if (filters && filters.length) {
                    records = resultSet.records = Ext.Array.filter(records, Ext.util.Filter.createFilterFn(filters));
                }

                // Remotely, groupers just mean top priority sorters
                if (groupers && groupers.length) {
                    // Must concat so as not to mutate passed sorters array which could be the items property of the sorters collection
                    sorters = sorters ? sorters.concat(groupers) : sorters;
                }

                // Sort by the specified groupers and sorters
                if (sorters && sorters.length) {
                    resultSet.records = Ext.Array.sort(records, Ext.util.Sortable.createComparator(sorters));
                }

                // Reader reads the whole passed data object.
                // If successful and we were given a start and limit, slice the result.
                if (me.enablePaging && operation.start !== undefined && operation.limit !== undefined) {

                    // Attempt to read past end of memory dataset - convert to failure
                    if (operation.start >= resultSet.total) {
                        resultSet.success = false;
                        resultSet.count = 0;
                        resultSet.records = [];
                    }
                    // Range is valid, slice it up.
                    else {
                        resultSet.records = Ext.Array.slice(resultSet.records, operation.start, operation.start + operation.limit);
                        resultSet.count = resultSet.records.length;
                    }
                }
            }

            if (resultSet.success) {
                operation.setSuccessful();
            } else {
                me.fireEvent('exception', me, null, operation);
            }
            Ext.callback(callback, scope || me, [operation]);
        },

        clear: Ext.emptyFn
    });