
/**
 * Module dependencies
 */

var _ = require('lodash');
var Bluebird = require('bluebird');
var Model = require('./lib/model');
var defaultMethods = require('./lib/defaultMethods');
var internalMethods = require('./lib/internalMethods');

/**
 * Build Extended Model Prototype
 *
 * @param {Object} context
 * @param {Object} mixins
 * @return {Object}
 * @api public
 */

module.exports = function(context, mixins) {

  /**
   * Extend the model prototype with default instance methods
   */

  var prototypeFns = {

    toObject: function() {
      return new defaultMethods.toObject(context, this);
    },

    save: function(cb) {
      return new defaultMethods.save(context, this, cb);
    },

    destroy: function(cb) {
      return new defaultMethods.destroy(context, this, cb);
    },

    _defineAssociations: function() {
      new internalMethods.defineAssociations(context, this);
    },

    _normalizeAssociations: function() {
      new internalMethods.normalizeAssociations(context, this);
    },

    _cast: function(values) {
      _.keys(context._attributes).forEach(function(key) {
        var type = context._attributes[key].type;

        // Attempt to parse Array or JSON type
        if(type === 'array' || type === 'json') {
          if(!_.isString(values[key])) return;
          try {
            values[key] = JSON.parse(values[key]);
          } catch(e) {
            return;
          }
        }

        // Convert booleans back to true/false
        if(type === 'boolean') {
          var val = values[key];
          if(val === 0) values[key] = false;
          if(val === 1) values[key] = true;
        }

      });
    },

    /**
     * Model.validate()
     *
     * Takes the currently set attributes and validates the model
     * Shorthand for Model.validate({ attributes }, cb)
     *
     * @param {Function} callback - (err)
     * @return {Promise}
     */

    validate: function(cb) {
      // Collect current values
      var values = this.toObject();

      if(cb) {
        context.validate(values, function(err) {
          if(err) return cb(err);
          cb();
        });
        return;
      }

      else {
        return new Bluebird(function (resolve, reject) {
          context.validate(values, function(err) {
            if(err) return reject(err);
            resolve();
          });
        });
      }
    }

  };

  // If any of the attributes are protected, the default toJSON method should
  // remove them.
  var protectedAttributes = _.compact(_.map(context._attributes, function(attr, key) {return attr.protected ? key : undefined;}));
  if (protectedAttributes.length) {
    prototypeFns.toJSON = function() {
      var obj = this.toObject();
      _.each(protectedAttributes, function(key) {
        delete obj[key];
      });
      return obj;
    };
  }
  // Otherwise just return the raw object
  else {
    prototypeFns.toJSON = function() {
      return this.toObject();
    };
  }

  var prototype = _.extend(prototypeFns, mixins);

  var model = Model.extend(prototype);

  // Return the extended model for use in Waterline
  return model;
};

@@ -2615,46 +2615,9 @@
 ));%0A
-  if (protectedAttributes.length) %7B%0A  
+%0A
   pr
@@ -2649,26 +2649,24 @@
 ion() %7B%0A    
-  
 var obj = th
@@ -2675,24 +2675,63 @@
 .toObject();
+%0A%0A    if (protectedAttributes.length) %7B
 %0A      _.eac
@@ -2812,163 +2812,191 @@
     
-  return obj;%0A    %7D;
+%7D%0A
 %0A  
-%7D%0A
   // 
-Otherwise just return 
+Remove toJSON from the result, to prevent infinite recursion with%0A    // msgpack or o
 the
+r
  r
-aw object%0A  else %7B%0A    prototypeFns.toJSON = function() %7B%0A      return this.toObject();%0A    %7D;%0A  %7D
+ecursive object transformation tools.%0A    obj.toJSON = null;%0A%0A    return obj;%0A  %7D;
 %0A%0A  

Remove toJSON function from objects being returned from toJSON, to prevent infinite recursion with msgpack or other recursive object transformation tools