Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / data / reader / Reader.js
index 25ff12f..4f4f662 100644 (file)
-/**
- * @author Ed Spencer
- * @class Ext.data.reader.Reader
- * @extends Object
- * 
- * <p>Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link Ext.data.Store Store}
- * - usually in response to an AJAX request. This is normally handled transparently by passing some configuration to either the 
- * {@link Ext.data.Model Model} or the {@link Ext.data.Store Store} in question - see their documentation for further details.</p>
- * 
- * <p><u>Loading Nested Data</u></p>
- * 
- * <p>Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association associations}
- * configured on each Model. Below is an example demonstrating the flexibility of these associations in a fictional CRM system which
- * manages a User, their Orders, OrderItems and Products. First we'll define the models:
- * 
-<pre><code>
-Ext.define("User", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'name'
-    ],
-
-    hasMany: {model: 'Order', name: 'orders'},
-
-    proxy: {
-        type: 'rest',
-        url : 'users.json',
-        reader: {
-            type: 'json',
-            root: 'users'
-        }
-    }
-});
+/*
 
-Ext.define("Order", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'total'
-    ],
-
-    hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
-    belongsTo: 'User'
-});
-
-Ext.define("OrderItem", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'price', 'quantity', 'order_id', 'product_id'
-    ],
-
-    belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
-});
-
-Ext.define("Product", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'name'
-    ],
-
-    hasMany: 'OrderItem'
-});
-</code></pre>
- * 
- * <p>This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. Finally,
- * each OrderItem has a single Product. This allows us to consume data like this:</p>
- * 
-<pre><code>
-{
-    "users": [
-        {
-            "id": 123,
-            "name": "Ed",
-            "orders": [
-                {
-                    "id": 50,
-                    "total": 100,
-                    "order_items": [
-                        {
-                            "id"      : 20,
-                            "price"   : 40,
-                            "quantity": 2,
-                            "product" : {
-                                "id": 1000,
-                                "name": "MacBook Pro"
-                            }
-                        },
-                        {
-                            "id"      : 21,
-                            "price"   : 20,
-                            "quantity": 3,
-                            "product" : {
-                                "id": 1001,
-                                "name": "iPhone"
-                            }
-                        }
-                    ]
-                }
-            ]
-        }
-    ]
-}
-</code></pre>
- * 
- * <p>The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the Orders
- * for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), and finally
- * the Product associated with each OrderItem. Now we can read the data and use it as follows:
- * 
-<pre><code>
-var store = new Ext.data.Store({
-    model: "User"
-});
+This file is part of Ext JS 4
 
-store.load({
-    callback: function() {
-        //the user that was loaded
-        var user = store.first();
+Copyright (c) 2011 Sencha Inc
 
-        console.log("Orders for " + user.get('name') + ":")
+Contact:  http://www.sencha.com/contact
 
-        //iterate over the Orders for each User
-        user.orders().each(function(order) {
-            console.log("Order ID: " + order.getId() + ", which contains items:");
+GNU General Public License Usage
+This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
 
-            //iterate over the OrderItems for each Order
-            order.orderItems().each(function(orderItem) {
-                //we know that the Product data is already loaded, so we can use the synchronous getProduct
-                //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
-                var product = orderItem.getProduct();
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
 
-                console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
-            });
-        });
-    }
-});
-</code></pre>
- * 
- * <p>Running the code above results in the following:</p>
+*/
+/**
+ * @author Ed Spencer
+ *
+ * Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link
+ * Ext.data.Store Store} - often in response to an AJAX request. In general there is usually no need to create
+ * a Reader instance directly, since a Reader is almost always used together with a {@link Ext.data.proxy.Proxy Proxy},
+ * and is configured using the Proxy's {@link Ext.data.proxy.Proxy#cfg-reader reader} configuration property:
  * 
-<pre><code>
-Orders for Ed:
-Order ID: 50, which contains items:
-2 orders of MacBook Pro
-3 orders of iPhone
-</code></pre>
+ *     Ext.create('Ext.data.Store', {
+ *         model: 'User',
+ *         proxy: {
+ *             type: 'ajax',
+ *             url : 'users.json',
+ *             reader: {
+ *                 type: 'json',
+ *                 root: 'users'
+ *             }
+ *         },
+ *     });
+ *     
+ * The above reader is configured to consume a JSON string that looks something like this:
+ *  
+ *     {
+ *         "success": true,
+ *         "users": [
+ *             { "name": "User 1" },
+ *             { "name": "User 2" }
+ *         ]
+ *     }
  * 
- * @constructor
- * @param {Object} config Optional config object
+ *
+ * # Loading Nested Data
+ *
+ * Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association
+ * associations} configured on each Model. Below is an example demonstrating the flexibility of these associations in a
+ * fictional CRM system which manages a User, their Orders, OrderItems and Products. First we'll define the models:
+ *
+ *     Ext.define("User", {
+ *         extend: 'Ext.data.Model',
+ *         fields: [
+ *             'id', 'name'
+ *         ],
+ *
+ *         hasMany: {model: 'Order', name: 'orders'},
+ *
+ *         proxy: {
+ *             type: 'rest',
+ *             url : 'users.json',
+ *             reader: {
+ *                 type: 'json',
+ *                 root: 'users'
+ *             }
+ *         }
+ *     });
+ *
+ *     Ext.define("Order", {
+ *         extend: 'Ext.data.Model',
+ *         fields: [
+ *             'id', 'total'
+ *         ],
+ *
+ *         hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
+ *         belongsTo: 'User'
+ *     });
+ *
+ *     Ext.define("OrderItem", {
+ *         extend: 'Ext.data.Model',
+ *         fields: [
+ *             'id', 'price', 'quantity', 'order_id', 'product_id'
+ *         ],
+ *
+ *         belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
+ *     });
+ *
+ *     Ext.define("Product", {
+ *         extend: 'Ext.data.Model',
+ *         fields: [
+ *             'id', 'name'
+ *         ],
+ *
+ *         hasMany: 'OrderItem'
+ *     });
+ *
+ * This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems.
+ * Finally, each OrderItem has a single Product. This allows us to consume data like this:
+ *
+ *     {
+ *         "users": [
+ *             {
+ *                 "id": 123,
+ *                 "name": "Ed",
+ *                 "orders": [
+ *                     {
+ *                         "id": 50,
+ *                         "total": 100,
+ *                         "order_items": [
+ *                             {
+ *                                 "id"      : 20,
+ *                                 "price"   : 40,
+ *                                 "quantity": 2,
+ *                                 "product" : {
+ *                                     "id": 1000,
+ *                                     "name": "MacBook Pro"
+ *                                 }
+ *                             },
+ *                             {
+ *                                 "id"      : 21,
+ *                                 "price"   : 20,
+ *                                 "quantity": 3,
+ *                                 "product" : {
+ *                                     "id": 1001,
+ *                                     "name": "iPhone"
+ *                                 }
+ *                             }
+ *                         ]
+ *                     }
+ *                 ]
+ *             }
+ *         ]
+ *     }
+ *
+ * The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the
+ * Orders for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case),
+ * and finally the Product associated with each OrderItem. Now we can read the data and use it as follows:
+ *
+ *     var store = Ext.create('Ext.data.Store', {
+ *         model: "User"
+ *     });
+ *
+ *     store.load({
+ *         callback: function() {
+ *             //the user that was loaded
+ *             var user = store.first();
+ *
+ *             console.log("Orders for " + user.get('name') + ":")
+ *
+ *             //iterate over the Orders for each User
+ *             user.orders().each(function(order) {
+ *                 console.log("Order ID: " + order.getId() + ", which contains items:");
+ *
+ *                 //iterate over the OrderItems for each Order
+ *                 order.orderItems().each(function(orderItem) {
+ *                     //we know that the Product data is already loaded, so we can use the synchronous getProduct
+ *                     //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
+ *                     var product = orderItem.getProduct();
+ *
+ *                     console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
+ *                 });
+ *             });
+ *         }
+ *     });
+ *
+ * Running the code above results in the following:
+ *
+ *     Orders for Ed:
+ *     Order ID: 50, which contains items:
+ *     2 orders of MacBook Pro
+ *     3 orders of iPhone
  */
 Ext.define('Ext.data.reader.Reader', {
     requires: ['Ext.data.ResultSet'],
     alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
     
     /**
-     * @cfg {String} idProperty Name of the property within a row object
-     * that contains a record identifier value.  Defaults to <tt>The id of the model</tt>.
-     * If an idProperty is explicitly specified it will override that of the one specified
-     * on the model
+     * @cfg {String} idProperty
+     * Name of the property within a row object that contains a record identifier value. Defaults to The id of the
+     * model. If an idProperty is explicitly specified it will override that of the one specified on the model
      */
 
     /**
-     * @cfg {String} totalProperty Name of the property from which to
-     * retrieve the total number of records in the dataset. This is only needed
-     * if the whole dataset is not passed in one go, but is being paged from
-     * the remote server.  Defaults to <tt>total</tt>.
+     * @cfg {String} totalProperty
+     * Name of the property from which to retrieve the total number of records in the dataset. This is only needed if
+     * the whole dataset is not passed in one go, but is being paged from the remote server. Defaults to total.
      */
     totalProperty: 'total',
 
     /**
-     * @cfg {String} successProperty Name of the property from which to
-     * retrieve the success attribute. Defaults to <tt>success</tt>.  See
-     * {@link Ext.data.proxy.Proxy}.{@link Ext.data.proxy.Proxy#exception exception}
-     * for additional information.
+     * @cfg {String} successProperty
+     * Name of the property from which to retrieve the success attribute. Defaults to success. See
+     * {@link Ext.data.proxy.Server}.{@link Ext.data.proxy.Server#exception exception} for additional information.
      */
     successProperty: 'success',
 
     /**
-     * @cfg {String} root <b>Required</b>.  The name of the property
-     * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
-     * An exception will be thrown if the root property is undefined. The data
-     * packet value for this property should be an empty array to clear the data
-     * or show no data.
+     * @cfg {String} root
+     * The name of the property which contains the Array of row objects.  For JSON reader it's dot-separated list
+     * of property names.  For XML reader it's a CSS selector.  For array reader it's not applicable.
+     * 
+     * By default the natural root of the data will be used.  The root Json array, the root XML element, or the array.
+     *
+     * The data packet value for this property should be an empty array to clear the data or show no data.
      */
     root: '',
     
     /**
-     * @cfg {String} messageProperty The name of the property which contains a response message.
-     * This property is optional.
+     * @cfg {String} messageProperty
+     * The name of the property which contains a response message. This property is optional.
      */
     
     /**
-     * @cfg {Boolean} implicitIncludes True to automatically parse models nested within other models in a response
-     * object. See the Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
+     * @cfg {Boolean} implicitIncludes
+     * True to automatically parse models nested within other models in a response object. See the
+     * Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
      */
     implicitIncludes: true,
     
     isReader: true,
     
+    /**
+     * Creates new Reader.
+     * @param {Object} config (optional) Config object.
+     */
     constructor: function(config) {
         var me = this;
         
@@ -244,10 +273,10 @@ Ext.define('Ext.data.reader.Reader', {
     },
 
     /**
-     * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call
-     * this function before running its own logic and returning the Ext.data.ResultSet instance. For most
-     * Readers additional processing should not be needed.
-     * @param {Mixed} data The raw data object
+     * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call this function
+     * before running its own logic and returning the Ext.data.ResultSet instance. For most Readers additional
+     * processing should not be needed.
+     * @param {Object} data The raw data object
      * @return {Ext.data.ResultSet} A ResultSet object
      */
     readRecords: function(data) {
@@ -264,9 +293,8 @@ Ext.define('Ext.data.reader.Reader', {
         }
         
         /**
+         * @property {Object} rawData
          * The raw data object that was last passed to readRecords. Stored for further processing if needed
-         * @property rawData
-         * @type Mixed
          */
         me.rawData = data;
 
@@ -320,7 +348,7 @@ Ext.define('Ext.data.reader.Reader', {
 
     /**
      * Returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
-     * @param {Object[]/Object} data-root from server response
+     * @param {Object[]/Object} root from server response
      * @private
      */
     extractData : function(root) {
@@ -344,8 +372,7 @@ Ext.define('Ext.data.reader.Reader', {
             id     = me.getId(node);
 
             
-            record = new Model(values, id);
-            record.raw = node;
+            record = new Model(values, id, node);
             records.push(record);
                 
             if (me.implicitIncludes) {
@@ -361,7 +388,7 @@ Ext.define('Ext.data.reader.Reader', {
      * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
      * on the record provided.
      * @param {Ext.data.Model} record The record to load associations for
-     * @param {Mixed} data The data object
+     * @param {Object} data The data object
      * @return {String} Return value description
      */
     readAssociated: function(record, data) {
@@ -397,9 +424,9 @@ Ext.define('Ext.data.reader.Reader', {
      * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
      * record, this should return the relevant part of that data for the given association name. This is only really
      * needed to support the XML Reader, which has to do a query to get the associated data object
-     * @param {Mixed} data The raw data object
+     * @param {Object} data The raw data object
      * @param {String} associationName The name of the association to get data for (uses associationKey if present)
-     * @return {Mixed} The root
+     * @return {Object} The root
      */
     getAssociatedDataRoot: function(data, associationName) {
         return data[associationName];
@@ -449,15 +476,16 @@ Ext.define('Ext.data.reader.Reader', {
      * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
      * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
      * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
-     * @param {Mixed} data The data object
-     * @return {Mixed} The same data object
+     * @param {Object} data The data object
+     * @return {Object} The same data object
      */
     getRoot: function(data) {
         return data;
     },
 
     /**
-     * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be implemented by each subclass
+     * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be
+     * implemented by each subclass
      * @param {Object} response The responce object
      * @return {Object} The useful data from the response
      */
@@ -505,7 +533,7 @@ Ext.define('Ext.data.reader.Reader', {
      * @private
      * This builds optimized functions for retrieving record data and meta data from an object.
      * Subclasses may need to implement their own getRoot function.
-     * @param {Boolean} force True to automatically remove existing extractor functions first (defaults to false)
+     * @param {Boolean} [force=false] True to automatically remove existing extractor functions first
      */
     buildExtractors: function(force) {
         var me          = this,
@@ -583,4 +611,4 @@ Ext.define('Ext.data.reader.Reader', {
             success: true
         })
     });
-});
\ No newline at end of file
+});