Home Reference Source

src/Dryad.js

import * as _  from 'underscore';

/**
 * >> A dryad (/ˈdraɪ.æd/; Greek: Δρυάδες, sing.: Δρυάς) is a tree nymph, or female tree spirit, in Greek mythology
 *
 * A Dryad is a component for managing the creation and control of something.
 *
 * For instance that 'something' could be a SuperCollider Synth, or a MIDI connection, SVG or Canvas in a webrowser,
 * a datasource, a web resource to fetch or an external process.
 *
 * It is anything that you want to specify parameters for and then create according to those parameters.
 *
 * Dryads have properties and children but do not hold any internal state.
 * Their methods are passed a context object and they perform their functionality
 * based on their properties (what they are supposed to be / to do) and the context
 * which holds the state, temporary variables they need for operation (like supercollider node ids) and gives them access to variables defined by parent nodes in the play graph.
 *
 * They provide functionality by return command objects which are processed by
 * command middleware which is supplied by various Dryadic packages.
 */


export default class Dryad {

  /**
   * Subclasses should not implement constructor.
   * All Dryad classes take properties and children.
   */
  constructor(properties={}, children=[]) {
    this.properties = _.assign({}, this.defaultProperties(), properties || {});
    this.children = children || [];
    this.tag = null;
  }

  /**
   * Defaults properties if none are supplied
   */
  defaultProperties() {
    return {};
  }

  /**
   * Returns a command object or a function that is called with node context and will return a command object.
   *
   * Values of the command objects are functions may return Promises,
   * and may reject those promises which will halt the .add() operation

   * The function is called with the node's context
   *
   * Middleware supplied by layers will match command keys and will be passed the value.
   * Value is either an object that the middleware uses to do whatever it does (launch things, send messages) or is a function that take context and returns the object.
   */
  prepareForAdd(/*player*/) {
    return {};
  }

  /**
   * Add the Dryad, make it play, make it start etc.
   *
   * Returns a command object or a function that is called with node context and will return a command object.
   *
   * Middleware supplied by layers will match command keys and will be passed the value.
   * Value is either an object that the middleware uses to do whatever it does (launch things, send messages) or is a function that take context and returns the object.
   *
   * Command middleware for add may return Promises which resolve on success; ie. when the thing is successfully booted, running etc.
   */
  add(/*player*/) {
    return {};
  }

  /**
   * Remove the Dryad, make it stop etc.
   *
   * Returns a command object or a function that is called with node context and will return a command object.
   *
   * Middleware supplied by layers will match command keys and will be passed the value.
   * Value is either an object that the middleware uses to do whatever it does (launch things, send messages) or is a function that take context and returns the object.
   *
   * Command middleware for run may return Promises which resolve on success; ie. when the thing is successfully stopped, remove etc.
   */
  remove(/*player*/) {
    return {};
  }

  /**
   * Dryad classes may return a subgraph of Dryads to replace itself
   * in the play graph.
   * This lets Dryads compose more complex behavior, add other Dryads that
   * assist. Any Dryads supplied in properties should be included in the subgraph.
   *
   * The subgraph may also contain the Dryad itself in which case its .add .remove
   * will be called. If subgraph is implemented but it does not include itself then
   * .add / .remove will not be called.
   */
  subgraph() {}

  /**
   * When Dryad requires a parent Dryad to be somewhere above it then it
   * may be specified by its class name here and the parent will be injected
   * into the playgraph. This is similar to subgraph() but make it easy to
   * do and is less error prone.
   *
   * Example: SCSynthDef compiles SynthDefs from source code and requires a supercollider SCLang interpreter as a parent to do that compilation. If there is not already an SCLang in the play graph then include one.
   *
   * @returns {String|undefined} - class name of required parent Dryad
   */
  requireParent() {}

  /**
   * Initial context
   *
   * This dryad's context is also the parent object for all children.
   */
  initialContext() {
    return {};
  }

  /**
   * Context for child; used when creating initial context for a node
   *
   * Note that the child already inherits from this context.
   *
   * will deprecate this. nothing is using it
   * @deprecated
   */
  childContext() {
    return {};
  }

  /**
   * This method is never actually called, but merely because its implemented
   * (dryad.isDryad is not undefined) it marks the things as being a Dryad.
   *
   * @returns {Boolean}
   */
  get isDryad() {
    return true;
  }

  /**
   * This method is never actually called, but merely because its implemented
   * (MyDryad.isDryadSubclass is not undefined) it marks the thing as being a Dryad subclass.
   *
   * @returns {Boolean}
   */
  static isDryadSubclass() {
    return true;
  }

  clone() {
    let dup = new this.constructor();
    let cloneValue = (c) => (c && c.isDryad) ? c.clone() : _.clone(c);
    dup.properties = _.mapObject(this.properties, cloneValue);
    dup.children = this.children.map(cloneValue);
    return dup;
  }
}