define('ember-cli-mirage/serializer', ['exports', 'ember-cli-mirage/orm/model', 'ember-cli-mirage/orm/collection', 'ember-cli-mirage/orm/polymorphic-collection', 'ember-cli-mirage/utils/extend', 'ember-cli-mirage/utils/inflector', 'lodash/isFunction', 'lodash/isArray', 'lodash/isEmpty', 'lodash/includes', 'lodash/assign', 'lodash/get', 'lodash'], function (exports, _model, _collection, _polymorphicCollection, _extend, _inflector, _isFunction2, _isArray2, _isEmpty2, _includes2, _assign2, _get2, _lodash) {
  'use strict';

  Object.defineProperty(exports, "__esModule", {
    value: true
  });

  function _defineProperty(obj, key, value) {
    if (key in obj) {
      Object.defineProperty(obj, key, {
        value: value,
        enumerable: true,
        configurable: true,
        writable: true
      });
    } else {
      obj[key] = value;
    }

    return obj;
  }

  var _slicedToArray = function () {
    function sliceIterator(arr, i) {
      var _arr = [];
      var _n = true;
      var _d = false;
      var _e = undefined;

      try {
        for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
          _arr.push(_s.value);

          if (i && _arr.length === i) break;
        }
      } catch (err) {
        _d = true;
        _e = err;
      } finally {
        try {
          if (!_n && _i["return"]) _i["return"]();
        } finally {
          if (_d) throw _e;
        }
      }

      return _arr;
    }

    return function (arr, i) {
      if (Array.isArray(arr)) {
        return arr;
      } else if (Symbol.iterator in Object(arr)) {
        return sliceIterator(arr, i);
      } else {
        throw new TypeError("Invalid attempt to destructure non-iterable instance");
      }
    };
  }();

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  var _createClass = function () {
    function defineProperties(target, props) {
      for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
      }
    }

    return function (Constructor, protoProps, staticProps) {
      if (protoProps) defineProperties(Constructor.prototype, protoProps);
      if (staticProps) defineProperties(Constructor, staticProps);
      return Constructor;
    };
  }();

  var Serializer = function () {
    function Serializer(registry, type, request) {
      _classCallCheck(this, Serializer);

      this.registry = registry;
      this.type = type;
      this.request = request;
    }

    /**
     * Override this method to implement your own custom
     * serialize function. `primaryResource` is whatever was returned
     * from your route handler, and request is the Pretender
     * request object. Returns a plain JavaScript object or
     * array, which Mirage uses as the response data to your
     * Ember app’s XHR request. You can also override this method,
     * call super, and manipulate the data before Mirage responds
     * with it. This is a great place to add metadata, or for
     * one-off operations that don’t fit neatly into any of
     * Mirage’s other abstractions.
     * @method serialize
     * @param response
     * @param request
     * @public
     */


    _createClass(Serializer, [{
      key: 'serialize',
      value: function serialize(primaryResource /* , request */) {
        return this.buildPayload(primaryResource);
      }
    }, {
      key: 'buildPayload',
      value: function buildPayload(primaryResource, toInclude, didSerialize, json) {
        if (!primaryResource && (0, _isEmpty2.default)(toInclude)) {
          return json;
        } else if (primaryResource) {
          var _getHashForPrimaryRes = this.getHashForPrimaryResource(primaryResource),
              _getHashForPrimaryRes2 = _slicedToArray(_getHashForPrimaryRes, 2),
              resourceHash = _getHashForPrimaryRes2[0],
              newIncludes = _getHashForPrimaryRes2[1];

          var newDidSerialize = this.isCollection(primaryResource) ? primaryResource.models : [primaryResource];

          return this.buildPayload(undefined, newIncludes, newDidSerialize, resourceHash);
        } else {
          var nextIncludedResource = toInclude.shift();

          var _getHashForIncludedRe = this.getHashForIncludedResource(nextIncludedResource),
              _getHashForIncludedRe2 = _slicedToArray(_getHashForIncludedRe, 2),
              _resourceHash = _getHashForIncludedRe2[0],
              _newIncludes = _getHashForIncludedRe2[1];

          var newToInclude = _newIncludes.filter(function (resource) {
            return !(0, _includes2.default)(didSerialize.map(function (m) {
              return m.toString();
            }), resource.toString());
          }).concat(toInclude);
          var _newDidSerialize = (this.isCollection(nextIncludedResource) ? nextIncludedResource.models : [nextIncludedResource]).concat(didSerialize);
          var newJson = this.mergePayloads(json, _resourceHash);

          return this.buildPayload(undefined, newToInclude, _newDidSerialize, newJson);
        }
      }
    }, {
      key: 'getHashForPrimaryResource',
      value: function getHashForPrimaryResource(resource) {
        var _getHashForResource = this.getHashForResource(resource),
            _getHashForResource2 = _slicedToArray(_getHashForResource, 2),
            hash = _getHashForResource2[0],
            addToIncludes = _getHashForResource2[1];

        var hashWithRoot = void 0;

        if (this.root) {
          var serializer = this.serializerFor(resource.modelName);
          var rootKey = serializer.keyForResource(resource);
          hashWithRoot = _defineProperty({}, rootKey, hash);
        } else {
          hashWithRoot = hash;
        }

        return [hashWithRoot, addToIncludes];
      }
    }, {
      key: 'getHashForIncludedResource',
      value: function getHashForIncludedResource(resource) {
        var hashWithRoot = void 0,
            addToIncludes = void 0;

        if (resource instanceof _polymorphicCollection.default) {
          hashWithRoot = {};
          addToIncludes = resource.models;
        } else {
          var serializer = this.serializerFor(resource.modelName);

          var _serializer$getHashFo = serializer.getHashForResource(resource),
              _serializer$getHashFo2 = _slicedToArray(_serializer$getHashFo, 2),
              hash = _serializer$getHashFo2[0],
              newModels = _serializer$getHashFo2[1];

          // Included resources always have a root, and are always pushed to an array.
          var rootKey = serializer.keyForRelationship(resource.modelName);
          hashWithRoot = (0, _isArray2.default)(hash) ? _defineProperty({}, rootKey, hash) : _defineProperty({}, rootKey, [hash]);
          addToIncludes = newModels;
        }

        return [hashWithRoot, addToIncludes];
      }
    }, {
      key: 'getHashForResource',
      value: function getHashForResource(resource) {
        var removeForeignKeys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

        var _this = this;

        var didSerialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
        var lookupSerializer = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

        var hash = void 0;
        var serializer = lookupSerializer ? this.serializerFor(resource.modelName) : this; // this is used for embedded responses

        if (this.isModel(resource)) {
          hash = serializer._hashForModel(resource, removeForeignKeys, didSerialize);
        } else {
          hash = resource.models.map(function (m) {
            return serializer._hashForModel(m, removeForeignKeys, didSerialize);
          });
        }

        if (this.embed) {
          return [hash, []];
        } else {
          var addToIncludes = (0, _lodash.default)(serializer.getKeysForIncluded()).map(function (key) {
            if (_this.isCollection(resource)) {
              return resource.models.map(function (m) {
                return m[key];
              });
            } else {
              return resource[key];
            }
          }).flatten().compact().uniqBy(function (m) {
            return m.toString();
          }).value();

          return [hash, addToIncludes];
        }
      }
    }, {
      key: 'mergePayloads',
      value: function mergePayloads(json, resourceHash) {
        var newJson = void 0;

        var _Object$keys = Object.keys(resourceHash),
            _Object$keys2 = _slicedToArray(_Object$keys, 1),
            resourceHashKey = _Object$keys2[0];

        if (json[resourceHashKey]) {
          newJson = json;
          newJson[resourceHashKey] = json[resourceHashKey].concat(resourceHash[resourceHashKey]);
        } else {
          newJson = (0, _assign2.default)(json, resourceHash);
        }

        return newJson;
      }
    }, {
      key: 'keyForResource',
      value: function keyForResource(resource) {
        var modelName = resource.modelName;

        return this.isModel(resource) ? this.keyForModel(modelName) : this.keyForCollection(modelName);
      }
    }, {
      key: 'keyForModel',
      value: function keyForModel(modelName) {
        return (0, _inflector.camelize)(modelName);
      }
    }, {
      key: 'keyForCollection',
      value: function keyForCollection(modelName) {
        return (0, _inflector.pluralize)(this.keyForModel(modelName));
      }
    }, {
      key: '_hashForModel',
      value: function _hashForModel(model, removeForeignKeys) {
        var _this2 = this;

        var didSerialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

        var attrs = this._attrsForModel(model);

        if (removeForeignKeys) {
          model.fks.forEach(function (fk) {
            delete attrs[fk];
          });
        }

        if (this.embed) {
          var newDidSerialize = (0, _assign2.default)({}, didSerialize);
          newDidSerialize[model.modelName] = newDidSerialize[model.modelName] || {};
          newDidSerialize[model.modelName][model.id] = true;

          this.getKeysForIncluded().forEach(function (key) {
            var associatedResource = model[key];
            if (associatedResource && !(0, _get2.default)(newDidSerialize, associatedResource.modelName + '.' + associatedResource.id)) {
              var _getHashForResource3 = _this2.getHashForResource(associatedResource, true, newDidSerialize, true),
                  _getHashForResource4 = _slicedToArray(_getHashForResource3, 1),
                  associatedResourceHash = _getHashForResource4[0];

              var formattedKey = _this2.keyForEmbeddedRelationship(key);
              attrs[formattedKey] = associatedResourceHash;

              if (_this2.isModel(associatedResource)) {
                var fk = (0, _inflector.camelize)(key) + 'Id';
                delete attrs[fk];
              }
            }
          });

          return attrs;
        } else {
          return this._maybeAddAssociationIds(model, attrs);
        }
      }
    }, {
      key: '_attrsForModel',
      value: function _attrsForModel(model) {
        var attrs = {};

        if (this.attrs) {
          attrs = this.attrs.reduce(function (memo, attr) {
            memo[attr] = model[attr];
            return memo;
          }, {});
        } else {
          attrs = (0, _assign2.default)(attrs, model.attrs);
        }

        // Remove fks
        model.fks.forEach(function (key) {
          return delete attrs[key];
        });

        return this._formatAttributeKeys(attrs);
      }
    }, {
      key: '_maybeAddAssociationIds',
      value: function _maybeAddAssociationIds(model, attrs) {
        var _this3 = this;

        var newHash = (0, _assign2.default)({}, attrs);

        if (this.serializeIds === 'always') {
          model.associationKeys.forEach(function (key) {
            var association = model[key];
            if (_this3.isCollection(association)) {
              var formattedKey = _this3.keyForRelationshipIds(key);
              newHash[formattedKey] = model[key].models.map(function (m) {
                return m.id;
              });
            } else if (association) {
              var _formattedKey = _this3.keyForForeignKey(key);
              newHash[_formattedKey] = model[key + 'Id'];
            }
          });
        } else if (this.serializeIds === 'included') {
          this.getKeysForIncluded().forEach(function (key) {
            var association = model[key];

            if (model.associationFor(key).isPolymorphic) {
              if (association instanceof _polymorphicCollection.default) {
                var formattedKey = _this3.keyForRelationship(key);

                newHash[formattedKey] = model[(0, _inflector.singularize)(key) + 'Ids'];
              } else if (association instanceof _collection.default) {
                var _formattedKey2 = _this3.keyForRelationshipIds(key);

                newHash[_formattedKey2] = model[key].models.map(function (m) {
                  return m.id;
                });
              } else {
                var formattedTypeKey = _this3.keyForPolymorphicForeignKeyType(key);
                var formattedIdKey = _this3.keyForPolymorphicForeignKeyId(key);

                newHash[formattedTypeKey] = model[key + 'Id'].type;
                newHash[formattedIdKey] = model[key + 'Id'].id;
              }
            } else {
              if (_this3.isCollection(association)) {
                var _formattedKey3 = _this3.keyForRelationshipIds(key);

                newHash[_formattedKey3] = model[key].models.map(function (m) {
                  return m.id;
                });
              } else if (association) {
                var _formattedKey4 = _this3.keyForForeignKey(key);

                newHash[_formattedKey4] = model[key + 'Id'];
              }
            }
          });
        }

        return newHash;
      }
    }, {
      key: 'keyForAttribute',
      value: function keyForAttribute(attr) {
        return attr;
      }
    }, {
      key: 'keyForRelationship',
      value: function keyForRelationship(modelName) {
        return (0, _inflector.camelize)((0, _inflector.pluralize)(modelName));
      }
    }, {
      key: 'keyForEmbeddedRelationship',
      value: function keyForEmbeddedRelationship(attributeName) {
        return (0, _inflector.camelize)(attributeName);
      }
    }, {
      key: 'keyForRelationshipIds',
      value: function keyForRelationshipIds(relationshipName) {
        return (0, _inflector.singularize)((0, _inflector.camelize)(relationshipName)) + 'Ids';
      }
    }, {
      key: 'keyForForeignKey',
      value: function keyForForeignKey(relationshipName) {
        return (0, _inflector.camelize)(relationshipName) + 'Id';
      }
    }, {
      key: 'keyForPolymorphicForeignKeyId',
      value: function keyForPolymorphicForeignKeyId(relationshipName) {
        return (0, _inflector.camelize)(relationshipName) + 'Id';
      }
    }, {
      key: 'keyForPolymorphicForeignKeyType',
      value: function keyForPolymorphicForeignKeyType(relationshipName) {
        return (0, _inflector.camelize)(relationshipName) + 'Type';
      }
    }, {
      key: 'normalize',
      value: function normalize(json) {
        return json;
      }
    }, {
      key: 'isModel',
      value: function isModel(object) {
        return object instanceof _model.default;
      }
    }, {
      key: 'isCollection',
      value: function isCollection(object) {
        return object instanceof _collection.default || object instanceof _polymorphicCollection.default;
      }
    }, {
      key: 'isModelOrCollection',
      value: function isModelOrCollection(object) {
        return this.isModel(object) || this.isCollection(object);
      }
    }, {
      key: 'serializerFor',
      value: function serializerFor(type) {
        return this.registry.serializerFor(type);
      }
    }, {
      key: 'getKeysForIncluded',
      value: function getKeysForIncluded() {
        return (0, _isFunction2.default)(this.include) ? this.include(this.request) : this.include;
      }
    }, {
      key: '_formatAttributeKeys',
      value: function _formatAttributeKeys(attrs) {
        var formattedAttrs = {};

        for (var key in attrs) {
          var formattedKey = this.keyForAttribute(key);
          formattedAttrs[formattedKey] = attrs[key];
        }

        return formattedAttrs;
      }
    }, {
      key: 'getCoalescedIds',
      value: function getCoalescedIds() /* request */{}
    }, {
      key: 'schema',
      get: function get() {
        return this.registry.schema;
      }
    }]);

    return Serializer;
  }();

  // Defaults
  Serializer.prototype.include = [];
  Serializer.prototype.root = true;
  Serializer.prototype.embed = false;
  Serializer.prototype.serializeIds = 'included'; // can be 'included', 'always', or 'never'

  Serializer.extend = _extend.default;

  exports.default = Serializer;
});