docs: function Attribute() {
    /**
     * This is the attribute' name.
     * @name module:back4app-entity/models/attributes.Attribute#name
     * @type {!string}
     * @readonly
     * @example
     * Attribute.call(
     *   this,
     *   'attribute',
     *   '0..1',
     *   'default'
     * );
     * console.log(this.name); // Logs "attribute"
     */
    this.name = null;
    /**
     * This is the attribute type.
     * @name module:back4app-entity/models/attributes.Attribute#type
     * @type {!Class}
     * @readonly
     * @example
     * var attribute = Attribute.resolve(
     *   'attribute',
     *   'String',
     *   '0..1',
     *   'default'
     * );
     * console.log(attribute.type == StringAttribute); // Logs "true"
     */
    this.type = null;
    /**
     * This is the attribute's multiplicity.
     * @name module:back4app-entity/models/attributes.Attribute#multiplicity
     * @type {!string}
     * @readonly
     * @example
     * Attribute.call(
     *   this,
     *   'attribute',
     *   '0..1',
     *   'default'
     * );
     * console.log(this.multiplicity); // Logs "0..1"
     */
    this.multiplicity = null;
    /**
     * This is the attribute's default expression.
     * @name module:back4app-entity/models/attributes.Attribute#default
     * @type {?(boolean|number|string|Object|function)}
     * @readonly
     * @example
     * Attribute.call(
     *   this,
     *   'attribute',
     *   '0..1',
     *   'default'
     * );
     * console.log(this.default); // Logs "default"
     */
    this.default = null;
    /**
     * This is is the name to be used to stored the attribute data in the
     * repository. It can be given as a string that will be used by all adapters
     * or as a dictionary specifying the data name for each adapter. If dataName
     * is not given, the attribute's name will be used instead.
     * @name module:back4app-entity/models/attributes.Attribute#dataName
     * @type {?(string|Object.<!string, !string>)}
     * @readonly
     * @example
     * Attribute.call(
     *   this,
     *   'attribute',
     *   '0..1',
     *   'default',
     *   {
     *     mongodb: 'mongodbAttribute'
     *   }
     * );
     * console.log(this.dataName.mongodb); // Logs "mongodbAttribute"
     * @example
     * Attribute.call(
     *   this,
     *   'attribute',
     *   '0..1',
     *   'default',
     *   'dataAttribute'
     * );
     * console.log(this.dataName); // Logs "dataAttribute"
     */
    this.dataName = null;
  
    expect(this).to.be.an(
      'object',
      'The Attribute\'s constructor can be only invoked from specialized' +
      'classes\' constructors'
    );
  
    expect(this.constructor).to.be.a(
      'function',
      'The Attribute\'s constructor can be only invoked from specialized' +
      'classes\' constructors'
    );
  
    expect(this.constructor).to.not.equal(
      Attribute,
      'The Attribute is an abstract class and cannot be directly initialized'
    );
  
    expect(this).to.be.instanceof(
      Attribute,
      'The Attribute\'s constructor can be only invoked from specialized' +
      'classes\' constructors'
    );
  
    var _name = null;
    var _type = this.constructor;
    var _multiplicity = '1';
    var _default = null;
    var _dataName = null;
    var dataName = null;
    var _reservedNames =
      ['save', 'validate', 'isValid', 'delete', 'adapter', 'Entity', 'General',
      'adapterName', 'isNew', 'isDirty', 'clean'];
  
    expect(arguments).to.have.length.within(
      1,
      4,
      'Invalid arguments length when creating an Attribute (it has to be ' +
      'passed from 1 to 4 arguments)'
    );
  
    if (arguments.length === 1 && typeof arguments[0] !== 'string') {
      var attribute = arguments[0];
  
      expect(attribute).to.be.an(
        'object',
        'Invalid argument type when creating an Attribute (it has to be an ' +
        'object or a string)'
      );
  
      expect(attribute).to.have.ownProperty(
        'name',
        'Property "name" is required when creating an Attribute'
      );
      expect(attribute.name).to.be.a(
        'string',
        'Invalid property "name" when creating an Attribute (it has to be a ' +
        'string)'
      );
      expect(_reservedNames).to.not.include(
        attribute.name,
        'Invalid attribute "name" (it must not be equal to any of the ' +
        'reserved names)'
      );
  
      _name = attribute.name;
  
      for (var property in attribute) {
        expect(['name', 'multiplicity', 'default', 'dataName']).to.include(
          property,
          'Invalid property "' + property + '" when creating an Attribute ' +
          'called "' + _name + '" (valid properties are "name", "type", ' +
          '"multiplicity", "default" and "dataName")'
        );
      }
  
      if (attribute.hasOwnProperty('multiplicity')) {
        expect(attribute.multiplicity).to.be.a(
          'string',
          'Invalid property "multiplicity" when creating an Attribute called "' +
          _name + '" (it has to be a string)'
        );
  
        expect(['1', '0..1', '1..*', '*']).to.contain(
          attribute.multiplicity,
          'Invalid property "multiplicity" when creating an Attribute called "' +
          _name + '" (valid values are "1", "0..1", "1..*", "*")'
        );
  
        _multiplicity = attribute.multiplicity;
      }
  
      if (attribute.hasOwnProperty('default')) {
        _default = attribute.default;
      }
  
      if (attribute.dataName) {
        if (typeof attribute.dataName === 'string') {
          _dataName = attribute.dataName;
        } else {
          expect(attribute.dataName).to.be.an(
            'object',
            'Invalid property "dataName" when creating an Attribute called "' +
            _name + '" (it has to be a string or an object)'
          );
  
          _dataName = {};
          for (dataName in attribute.dataName) {
            expect(attribute.dataName[dataName]).to.be.a(
              'string',
              'Invalid property "dataName" for adapter "' + dataName + '" when ' +
              'creating an Attribute called "' + _name + '" (it has to be a ' +
              'string)'
            );
  
            _dataName[dataName] = attribute.dataName[dataName];
          }
  
          Object.preventExtensions(_dataName);
          Object.seal(_dataName);
          Object.freeze(_dataName);
        }
      }
    } else {
      expect(arguments[0]).to.be.a(
        'string',
        'Invalid argument "name" when creating an Attribute (it has to be a ' +
        'string)'
      );
      expect(_reservedNames).to.not.include(
        arguments[0],
        'Invalid attribute "name" (it must not be equal to any of the ' +
        'reserved names)'
      );
  
      _name = arguments[0];
  
      if (arguments.length > 1) {
        expect(arguments[1]).to.be.a(
          'string',
          'Invalid argument "multiplicity" when creating an Attribute called "' +
          _name + '" (it has to be a string)'
        );
  
        expect(['1', '0..1', '1..*', '*']).to.contain(
          arguments[1],
          'Invalid argument "multiplicity" when creating an Attribtue called "' +
            _name + '" (valid values are "1", "0..1", "1..*", "*")'
        );
  
        _multiplicity = arguments[1];
      }
  
      if (arguments.length > 2) {
        _default = arguments[2];
      }
  
      if (arguments.length > 3 && arguments[3]) {
        if (typeof arguments[3] === 'string') {
          _dataName = arguments[3];
        } else {
          expect(arguments[3]).to.be.an(
            'object',
            'Invalid argument "dataName" when creating an Attribute called "' +
            _name + '" (it has to be a string or an object)'
          );
  
          _dataName = {};
          for (dataName in arguments[3]) {
            expect(arguments[3][dataName]).to.be.a(
              'string',
              'Invalid argument "dataName" for adapter "' + dataName + '" when ' +
              'creating an Attribute called "' + _name + '" (it has to be a ' +
              'string)'
            );
  
            _dataName[dataName] = arguments[3][dataName];
          }
  
          Object.preventExtensions(_dataName);
          Object.seal(_dataName);
          Object.freeze(_dataName);
        }
      }
    }
  
    Object.defineProperty(this, 'name', {
      value: _name,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'type', {
      value: _type,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'multiplicity', {
      value: _multiplicity,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'default', {
      value: _default,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.defineProperty(this, 'dataName', {
      value: _dataName,
      enumerable: true,
      writable: false,
      configurable: false
    });
  
    Object.preventExtensions(this);
    Object.seal(this);
  }