Home Reference Source Test Repository

src/core/decorators/subscribe.js

import postal from "postal/lib/postal.lodash";
import each from "lodash/collection/each";

let _ = { each };

function addSubscribeMethod(target) {
  let subscriptions;
  if (!(subscriptions = target.constructor.subscriptions)) {
    return;
  }

  target.subscribe = function() {
    let subscriptionInstances = [];

    _.each(subscriptions, (topic) => {
      _.each(topic, (sub) => {
        let subDef;
        subscriptionInstances.push(
          (subDef = postal.channel(sub.channel)
          .subscribe(sub.topic, function(data, env) {
            // Call the callback and also pass the sub definition
            this[sub.callback].apply(this, [data, env, subDef]);
          })
          .context(this))
        );
      });
    });
    
    this["_subscriptions"] = subscriptionInstances;
  };

  return;
}

/**
 * Decorator for listening on an event bus (postal).  
 * Will search the class for a `channel`, or use `/` by default.  
 * **NOTE:** You have to call `this.subscribe()` in the constructor in order to have postal actually attatch the listeners correctly.
 * @function
 * @param  {String} topic Topic to listen for
 * @param  {String} channel The channel to listen on
 * @example <caption>Default Channel</caption>
 * import publish from "path/to/core/decorators/publish"
 * 
 * class FooComponent () {
 *   constructor() {
 *     // This is required
 *     this.subscribe();
 *   }
 *   @subscribe("foo.some.message")
 *   someMethod(data, evnelope, subscription) {
 *     
 *   }
 *   @subscribe("foo.some.other")
 *   anotherMethod(data, evnelope, subscription) {
 *     // ...
 *   }
 * }
 * @example <caption>Custom Channel</caption>
 * import publish from "path/to/core/decorators/publish"
 * 
 * class FooComponent () {
 *   constructor() {
 *     // This is required
 *     this.subscribe();
 *   }
 *   @subscribe("foo.some.message", "custom")
 *   someMethod(data, envelope, subscription) {
 *     // ...
 *   }
 * }
 * @example <caption>Channel decorator</caption>
 * import publish from "path/to/core/decorators/publish";
 * import channel from "path/to/core/decorators/channel"
 * 
 * class FooComponent () {
 *   constructor() {
 *     // This is required
 *     this.subscribe();
 *   }
 *   @subscribe("foo.some.message")
 *   @channel("custom")
 *   someMethod(data, envelope, subscription) {
 *     // ...
 *   }
 * }
 */
export default function subscribe(topic, channel) {
  return function(target, name) {
    let ctor = target.constructor;
    ctor.subscriptions = ctor.subscriptions || {};
    let subs = ctor.subscriptions[topic] = ctor.subscriptions[topic] || [];

    subs.push({ topic, channel: channel || target.channel || "/", callback: name });

    if (!target.subscribe) {
      addSubscribeMethod(target);
    }
  };
}