tunnckoCore/compose-emitter

View on GitHub
index.js

Summary

Maintainability
A
1 hr
Test Coverage
/*!
 * compose-emitter <https://github.com/tunnckoCore/compose-emitter>
 *
 * Copyright (c) 2016 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

'use strict'

var extend = require('extend-shallow')
var AppBase = require('app-base').AppBase
var isObject = require('is-extendable')

/**
 * > Initialize ComposeEmitter with `options`. Pass your EventEmitter
 * instance and optionally pass `options.context` to bind to listeners.
 *
 * **Example**
 *
 * ```js
 * var ComposeEmitter = require('compose-emitter').ComposeEmitter
 * var Emitter = require('component-emitter')
 *
 * var ee = new ComposeEmitter({
 *   emitter: new Emitter()
 * })
 *
 * ee
 *   .compose('on')('foo', console.log) // => 1, 2, 3
 *   .compose('emit')('foo', 1, 2, 3)
 * ```
 *
 * @param {Object} `options` Pass event emitter instance to `options.emitter`.
 * @api public
 */

function ComposeEmitter (options) {
  if (!(this instanceof ComposeEmitter)) {
    return new ComposeEmitter(options)
  }
  AppBase.call(this)
  this.options = isObject(options)
    ? options
    : isObject(this.options) ? this.options : {}
}

/**
 * > Extend your application with ComposeEmitter static and prototype methods.
 * See [static-extend][] or [tunnckoCore/app-base](https://github.com/tunnckoCore/app-base)
 * for more info and what's static and prototype methods are added.
 *
 * **Example**
 *
 * ```js
 * var ComposeEmitter = require('compose-emitter').ComposeEmitter
 * var Emitter = require('eventemitter3')
 *
 * function App (options) {
 *    if (!(this instanceof App)) {
 *      return new App(options)
 *    }
 *   ComposeEmitter.call(this, options)
 * }
 *
 * ComposeEmitter.extend(App)
 *
 * App.prototype.on = function on (name, fn, context) {
 *   return this.compose('on')(name, fn, context)
 * }
 *
 * App.prototype.once = function once (name, fn, context) {
 *   return this.compose('once')(name, fn, context)
 * }
 *
 * App.prototype.off = function off (name, fn, context) {
 *   return this.compose('off')(name, fn, context)
 * }
 *
 * App.prototype.emit = function emit () {
 *   return this.compose('emit').apply(null, arguments)
 * }
 *
 * var app = new App({
 *   context: {foo: 'bar'},
 *   emitter: new Emitter()
 * })
 *
 * app
 *   .on('foo', function (a) {
 *     console.log('foo:', a, this) // => 123, {foo: 'bar'}
 *   })
 *   .once('bar', function (b) {
 *     console.log('bar:', b) // => 456
 *   })
 *   .emit('foo', 123)
 *   .emit('bar', 456)
 *   .emit('bar', 789)
 * ```
 *
 * @name ComposeEmitter.extend
 * @param {Function} `Parent` The constructor to extend, using [static-extend][].
 * @api public
 */

AppBase.extend(ComposeEmitter)

/**
 * > Compose different `type` of emitter methods. You can use this to create
 * the usual `.on`, `.emit` and other methods. Pass as `type` name of the method
 * that your emitter have and optional `options` to pass context for the listeners.
 *
 * **Example**
 *
 * ```js
 * var emitter = require('compose-emitter')
 * var Emitter = require('eventemitter3')
 *
 * var on = emitter.compose('on', {
 *   context: {a: 'b'},
 *   emitter: new Emitter()
 * })
 * var emit = emitter.compose('emit')
 *
 * on('foo', function (a, b) {
 *   console.log('foo:', a, b, this) // => 1, 2, {a: 'b', c: 'd'}
 * }, {c: 'd'})
 *
 * emit('foo', 1, 2)
 * ```
 *
 * @name   .compose
 * @param  {String} `type` Name of the emitter method that you want to mirror.
 * @param  {Object} `options` Optionally pass `context` that will be bind to listeners.
 * @return {Function} Method that accept as many arguments as you want or emitter method need.
 * @api public
 */

AppBase.define(ComposeEmitter.prototype, 'compose', function compose (type, options) {
  if (typeof type !== 'string') {
    throw new TypeError('.compose: expect `type` be string')
  }
  // @todo
  var opts = this.options
  var ctx = options ? extend({}, opts.context, options.context) : opts.context
  this.options = options ? extend({}, opts, options) : opts
  this.options.context = ctx

  if (!isObject(this.options.emitter)) {
    throw new TypeError('.compose: expect `options.emitter` be extendable object')
  }
  this.define('emitter', this.options.emitter)

  var self = this
  return function onOffEmitOnce (name, fn, context) {
    if (type !== 'on' && type !== 'once' && type !== 'off') {
      self.emitter[type].apply(self.emitter, arguments)
      return self
    }
    self.options.context = context ? extend({}, self.options.context, context) : self.options.context
    self.options.context = isObject(self.options.context) ? self.options.context : self
    self.emitter[type](name, fn, self.options.context)
    return self
  }
})

/**
 * Expose `ComposeEmitter` instance
 * @type {ComposeEmitter}
 * @private
 */

module.exports = new ComposeEmitter()

/**
 * Expose `ComposeEmitter` constructor
 * @type {ComposeEmitter}
 * @private
 */

module.exports.ComposeEmitter = ComposeEmitter