docs: function save(key, val, opts) {
      var _this = this;
  
      var options = {};
      var attrs = {};
      var method = '';
      var sync = null;
  
      function wrapError(model, modelOptions) {
        var error = modelOptions.error;
        modelOptions.error = function (resp) {
          if (error) {
            error(model, resp, modelOptions);
          }
          model.trigger('error', model, resp, modelOptions);
        };
      }
  
      // Handle both `"key", value` and `{key: value}` -style arguments.
      if (key == null || (typeof key === 'undefined' ? 'undefined' : _typeof(key)) === 'object') {
        attrs = key;
        options = val;
      } else {
        attrs[key] = val;
        options = opts;
      }
  
      options = (0, _lodash2.default)({ validate: true }, options);
  
      // If we're not waiting and attributes exist, save acts as
      // `set(attr).save(null, opts)` with validation. Otherwise, check if
      // the model will be valid when the attributes, if any, are set.
      if (attrs && !options.wait) {
        if (!this.set(attrs, options)) {
          return false;
        }
      } else if (!this._validate(attrs, options)) {
        return false;
      }
  
      // After a successful server-side save, the client is (optionally)
      // updated with the server-side state.
      if (options.parse == null) {
        options.parse = true;
      }
      var success = options.success;
      options.success = function (resp) {
        var serverAttrs = _this.parse(resp, options);
        if (options.wait) {
          serverAttrs = (0, _lodash2.default)(attrs || {}, serverAttrs);
        }
        if ((0, _lodash6.default)(serverAttrs) && !_this.set(serverAttrs, options)) {
          return false;
        }
        if (success) {
          success(_this, resp, options);
        }
        _this.trigger('sync', _this, resp, options);
      };
  
      wrapError(this, options);
  
      method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
      if (method === 'patch') {
        options.attrs = transformForPatch(this.serialize(), attrs);
      }
      // if we're waiting we haven't actually set our attributes yet so
      // we need to do make sure we send right data
      if (options.wait && method !== 'patch') {
        var clonedModel = new this.constructor(this.getAttributes({
          props: true
        }));
        clonedModel.set(attrs);
  
        options.attrs = clonedModel.serialize();
      }
  
      sync = this.sync(method, this, options);
  
      // Make the request available on the options object so it can be accessed
      // further down the line by `parse`, attached listeners, etc
      // Same thing is done for fetch and destroy
      options.xhr = sync;
  
      return sync;
    }