docs: function (CurrentEntity, directSpecializations) {
    return function () {
      expect(arguments).to.have.length.within(
        1,
        4,
        'Invalid arguments length when specifying an Entity (it has to be ' +
        'passed from 1 to 4 arguments)'
      );
  
      var SpecificEntity = function () {
  
        if (!this.hasOwnProperty('Entity') || !this.Entity) {
          Object.defineProperty(this, 'Entity', {
            value: SpecificEntity,
            enumerable: false,
            writable: false,
            configurable: false
          });
        }
  
        if (SpecificEntity.specification.isAbstract) {
          expect(this).to.be.an(
            'object',
            'The "' + SpecificEntity.specification.name + '"\'s constructor ' +
            'can be only invoked from specialized classes\' constructors'
          );
  
          expect(this.constructor).to.be.a(
            'function',
            'The "' + SpecificEntity.specification.name + '"\'s constructor ' +
            'can be only invoked from specialized classes\' constructors'
          );
  
          expect(this.constructor).to.not.equal(
            SpecificEntity,
            'The "' + SpecificEntity.specification.name + '" is an abstract ' +
            'class and cannot be directly initialized'
          );
  
          expect(this).to.be.instanceof(
            SpecificEntity,
            'The "' + SpecificEntity.specification.name + '"\'s constructor ' +
            'can be only invoked from specialized classes\' constructors'
          );
        }
  
        CurrentEntity.apply(this, Array.prototype.slice.call(arguments));
      };
  
      classes.generalize(CurrentEntity, SpecificEntity);
  
      Object.defineProperty(SpecificEntity, 'General', {
        value: CurrentEntity,
        enumerable: true,
        writable: false,
        configurable: false
      });
  
      var _specificEntitySpecification = null;
  
      if (arguments.length === 1 && typeof arguments[0] !== 'string') {
        var specification = arguments[0];
  
        expect(specification).to.be.an(
          'object',
          'Invalid argument type when specifying an Entity (it has to be an ' +
          'object or a string)'
        );
  
        if (specification instanceof EntitySpecification) {
          _specificEntitySpecification = specification;
        } else {
          _specificEntitySpecification = new EntitySpecification(specification);
        }
      } else {
        var name = arguments[0];
  
        expect(name).to.be.a(
          'string',
          'Invalid argument "name" when specifying an Entity (it has to be a ' +
          'string)'
        );
  
        var attributes = null;
        var methods = null;
        var options = null;
  
        if (arguments.length > 1 && arguments[1]) {
          attributes = arguments[1];
  
          expect(typeof attributes).to.equal(
            'object',
            'Invalid property "attributes" when specifying an Entity (it has ' +
            'to be an object)'
          );
        }
  
        if (arguments.length > 2 && arguments[2]) {
          methods = arguments[2];
  
          expect(methods).to.be.an(
            'object',
            'Invalid property "methods" when specifying an Entity (it has to ' +
            'be an object)'
          );
        }
  
        if (arguments.length > 3 && arguments[3]) {
          options = arguments[3];
  
          expect(options).to.be.an(
            'object',
            'Invalid property "options" when specifying an Entity (it has to ' +
            'be an object)'
          );
        }
  
        _specificEntitySpecification = new EntitySpecification(
          name,
          attributes,
          methods,
          options
        );
      }
  
      if (_specificEntitySpecification.isAbstract) {
        expect(CurrentEntity.specification.isAbstract).to.equal(
          true,
          'It was not possible to specify a new Entity called "' +
            _specificEntitySpecification.name + '" because an abstract Entity ' +
          'cannot be specified by a concrete one'
        );
      }
  
      Object.defineProperty(SpecificEntity, 'specification', {
        value: _specificEntitySpecification,
        enumerable: true,
        writable: false,
        configurable: false
      });
  
      Object.defineProperty(SpecificEntity, 'dataName', {
        value: SpecificEntity.specification.getDataName(
          SpecificEntity.adapterName
        ),
        enumerable: true,
        writable: false,
        configurable: false
      });
  
      Object.defineProperty(SpecificEntity, 'attributes', {
        get: function () {
          var attributesObject = {};
  
          var visitedEntities = [];
          var CurrentEntity = SpecificEntity;
          while (CurrentEntity && visitedEntities.indexOf(CurrentEntity) === -1) {
            for (var attribute in CurrentEntity.specification.attributes) {
              if (!attributesObject.hasOwnProperty(attribute)) {
                attributesObject[attribute] =
                  CurrentEntity.specification.attributes[attribute];
              }
            }
  
            visitedEntities.push(CurrentEntity);
            CurrentEntity = CurrentEntity.General;
          }
  
          return new AttributeDictionary(attributesObject);
        },
        set: function () {
          throw new Error('Attributes of an Entity cannot be changed');
        },
        enumerable: true,
        configurable: false
      });
  
      Object.defineProperty(SpecificEntity, 'methods', {
        get: function () {
          var methodsObject = {};
  
          var visitedEntities = [];
          var CurrentEntity = SpecificEntity;
          while (CurrentEntity && visitedEntities.indexOf(CurrentEntity) === -1) {
            for (var method in CurrentEntity.specification.methods) {
              if (!methodsObject.hasOwnProperty(method)) {
                methodsObject[method] =
                  CurrentEntity.specification.methods[method];
              }
            }
  
            visitedEntities.push(CurrentEntity);
            CurrentEntity = CurrentEntity.General;
          }
  
          return new MethodDictionary(methodsObject);
        },
        set: function () {
          throw new Error('Methods of an Entity cannot be changed');
        },
        enumerable: true,
        configurable: false
      });
  
      var _specificEntityDirectSpecializations = {};
      Object.defineProperty(SpecificEntity, 'directSpecializations', {
        get: function () {
          var specializations = objects.copy(
            _specificEntityDirectSpecializations
          );
  
          Object.preventExtensions(specializations);
          Object.seal(specializations);
          Object.freeze(specializations);
  
          return specializations;
        },
        set: function () {
          throw new Error(
            'Specializations of an Entity cannot be changed'
          );
        },
        enumerable: true,
        configurable: false
      });
  
      Object.defineProperty(SpecificEntity, 'specializations', {
        get: function () {
          var specializations = {};
  
          _visitSpecializations(
            _specificEntityDirectSpecializations,
            specializations
          );
  
          Object.preventExtensions(specializations);
          Object.seal(specializations);
          Object.freeze(specializations);
  
          return specializations;
        },
        set: function () {
          throw new Error(
            'Specializations of an Entity cannot be changed'
          );
        },
        enumerable: true,
        configurable: false
      });
  
      SpecificEntity.specify = _getSpecifyFunction(
        SpecificEntity,
        _specificEntityDirectSpecializations
      );
      SpecificEntity.getSpecialization = _getGetSpecializationFunction(
        SpecificEntity
      );
      SpecificEntity.new = _getNewFunction(SpecificEntity);
      SpecificEntity.create = _getCreateFunction(SpecificEntity);
      SpecificEntity.get = _getGetFunction(SpecificEntity);
      SpecificEntity.find = _getFindFunction(SpecificEntity);
  
      if (_specificEntitySpecification.Entity) {
        expect(_specificEntitySpecification.Entity).to.equal(
          SpecificEntity,
          'The property "Entity" of the EntitySpecification instance should be ' +
          'equal to the Entity that is being specified.'
        );
      } else {
        _specificEntitySpecification.Entity = SpecificEntity;
      }
  
      expect(_specializations).to.not.have.ownProperty(
        _specificEntitySpecification.name,
        'It was not possible to specify a new Entity called "' +
        _specificEntitySpecification.name + '" because duplicates are not allowed'
      );
  
      _specializations[_specificEntitySpecification.name] = SpecificEntity;
      directSpecializations[_specificEntitySpecification.name] = SpecificEntity;
  
      return SpecificEntity;
    };
  }