Home Reference Source

src/kindergarten/Perimeter.js

import extend from 'lodash/extend';
import isObject from 'lodash/isObject';
import isString from 'lodash/isString';
import omit from 'lodash/omit';

import AllowedMethodsService from './utils/AllowedMethodsService';
import BaseObject from './BaseObject';
import HeadGoverness from './governesses/HeadGoverness';
import {
  isGoverness,
  isSandbox
} from './utils';
import {
  NoPurposeError,
  NoSandboxError
} from './errors';

const allowedMethodsService = new AllowedMethodsService({});

/**
 * A Perimeter is used to define the places where child can play.
 */
export default class Perimeter extends BaseObject {
  /**
   * Create new perimeter
   */
  constructor(purpose, opts = {}) {
    super();

    if (isObject(purpose) && isString(purpose.purpose)) {
      opts = purpose;
      this.purpose = purpose.purpose;
    }

    this.purpose = this.purpose || purpose;
    this.govern = opts.govern || {};
    this.expose = opts.expose || [];

    if (!isGoverness(opts.governess)) {
      try {
        const Governess = opts.governess;
        this.governess = new Governess();
      } catch (ignore) {
        // ignore...
      }
    }

    this.governess = this.governess || opts.governess;

    // Perimeter doesn't require governess
    if (isGoverness(this.governess)) {
      this.governess.learnRules(this);
    }

    extend(this, omit(opts, ['purpose', 'govern', 'expose', 'governess']));
  }

  /**
   * The getter of the purpose.
   */
  get purpose() {
    return this._purpose;
  }

  /**
   * The setter of the purpose. Make sure that name of the purpose is not
   * restricted.
   */
  set purpose(value) {
    if (!isString(value) || allowedMethodsService.isRestricted(value)) {
      throw new NoPurposeError();
    }

    this._purpose = value;

    return value;
  }

  /**
   * Returns purpose of the perimeter.
   */
  getPurpose() {
    return this.purpose;
  }

  /**
   * The getter of the sandbox.
   */
  get sandbox() {
    return this._sandbox;
  }

  /**
   * The setter of the sandbox.
   * Make sure that given sandbox is an instance of Sandbox class.
   */
  set sandbox(value) {
    if (!isSandbox(value)) {
      throw new NoSandboxError();
    }

    this._sandbox = value;
    this.child = value.child;

    return value;
  }

  /**
   * Returns sandbox of the perimeter
   */
  getSandbox() {
    return this.sandbox;
  }

  /**
   * The getter of the governess.
   */
  get governess() {
    return isGoverness(this._governess) ?
      this._governess : (() => (
        isSandbox(this.sandbox) ? this.sandbox.governess : null
      ))();
  }

  /**
   * The setter of the governess.
   */
  set governess(value) {
    // if governess is null perimeter will use the governess of it's sandbox
    this._governess = (isGoverness(value)) ?
      value : (() => (
        isSandbox(this.sandbox) ? this.sandbox.governess : null
    ))();

    // Make sure governess know all the rules
    if (
      isObject(this._governess) && this._governess instanceof HeadGoverness
    ) {
      this._governess.learnRules(this);
    }

    return value;
  }

  /**
   * Return the governess of the perimeter or the governess of it's sandbox
   */
  getGoverness() {
    return this.governess;
  }

  /**
   * Forward guard call to governess.
   */
  guard(...args) {
    return this.governess.guard.call(this.governess, ...args);
  }

  /**
   * Forward governed call to governess.
   */
  governed(...args) {
    return this.governess.governed.call(this.governess, ...args);
  }

  /**
   * Forward isAllowed call to governess.
   */
  isAllowed(...args) {
    return this.governess.isAllowed.call(this.governess, ...args);
  }

  /**
   * Forward isNotAllowed call to governess.
   */
  isNotAllowed(...args) {
    return this.governess.isNotAllowed.call(this.governess, ...args);
  }
}