# role.js

    const _ = require('underscore');
    const AVError = require('./error');

    module.exports = function(AV) {
      AV.Role = AV.Object.extend("_Role", /** @lends AV.Role.prototype */ {
        // Instance Methods

        /**
        * Represents a Role on the AV server. Roles represent groupings of
        * Users for the purposes of granting permissions (e.g. specifying an ACL
        * for an Object). Roles are specified by their sets of child users and
        * child roles, all of which are granted any permissions that the parent
        * role has.
        *
        * <p>Roles must have a name (which cannot be changed after creation of the
        * role), and must specify an ACL.</p>
        * An AV.Role is a local representation of a role persisted to the AV
        * cloud.
         * @class AV.Role
         * @param {String} name The name of the Role to create.
         * @param {AV.ACL} acl The ACL for this role.
         */
        constructor: function(name, acl) {
          if (_.isString(name)) {
            AV.Object.prototype.constructor.call(this, null, null);
            this.setName(name);
          } else {
            AV.Object.prototype.constructor.call(this, name, acl);
          }
          if (acl) {
            if (!(acl instanceof AV.ACL)) {
              throw new TypeError('acl must be an instance of AV.ACL');
            } else {
              this.setACL(acl);
            }
          }
        },

        /**
         * Gets the name of the role.  You can alternatively call role.get("name")
         *
         * @return {String} the name of the role.
         */
        getName: function() {
          return this.get("name");
        },

        /**
         * Sets the name for a role. This value must be set before the role has
         * been saved to the server, and cannot be set once the role has been
         * saved.
         *
         * <p>
         *   A role's name can only contain alphanumeric characters, _, -, and
         *   spaces.
         * </p>
         *
         * <p>This is equivalent to calling role.set("name", name)</p>
         *
         * @param {String} name The name of the role.
         */
        setName: function(name, options) {
          return this.set("name", name, options);
        },

        /**
         * Gets the AV.Relation for the AV.Users that are direct
         * children of this role. These users are granted any privileges that this
         * role has been granted (e.g. read or write access through ACLs). You can
         * add or remove users from the role through this relation.
         *
         * <p>This is equivalent to calling role.relation("users")</p>
         *
         * @return {AV.Relation} the relation for the users belonging to this
         *     role.
         */
        getUsers: function() {
          return this.relation("users");
        },

        /**
         * Gets the AV.Relation for the AV.Roles that are direct
         * children of this role. These roles' users are granted any privileges that
         * this role has been granted (e.g. read or write access through ACLs). You
         * can add or remove child roles from this role through this relation.
         *
         * <p>This is equivalent to calling role.relation("roles")</p>
         *
         * @return {AV.Relation} the relation for the roles belonging to this
         *     role.
         */
        getRoles: function() {
          return this.relation("roles");
        },

        /**
         * @ignore
         */
        validate: function(attrs, options) {
          if ("name" in attrs && attrs.name !== this.getName()) {
            var newName = attrs.name;
            if (this.id && this.id !== attrs.objectId) {
              // Check to see if the objectId being set matches this.id.
              // This happens during a fetch -- the id is set before calling fetch.
              // Let the name be set in this case.
              return new AVError(AVError.OTHER_CAUSE,
                  "A role's name can only be set before it has been saved.");
            }
            if (!_.isString(newName)) {
              return new AVError(AVError.OTHER_CAUSE,
                  "A role's name must be a String.");
            }
            if (!(/^[0-9a-zA-Z\-_ ]+$/).test(newName)) {
              return new AVError(AVError.OTHER_CAUSE,
                  "A role's name can only contain alphanumeric characters, _," +
                  " -, and spaces.");
            }
          }
          if (AV.Object.prototype.validate) {
            return AV.Object.prototype.validate.call(this, attrs, options);
          }
          return false;
        }
      });
    };