node-riveter
Version:
Mix-in, inheritance and constructor extend behavior for your JavaScript enjoyment.
159 lines (148 loc) • 4.52 kB
JavaScript
/**
* node-riveter - Mix-in, inheritance and constructor extend behavior for your JavaScript enjoyment.
* © 2012 - Copyright appendTo, LLC
* © 2019 - Copyright Zach Lintz
* © 2020 - Copyright Foo-Foo-MQ
* Author(s): Jim Cowart, Nicholas Cloud, Doug Neiner, Foo-Foo-MQ Team
* Version: v0.2.1
* Url: https://github.com/Foo-Foo-MQ/node-riveter
* License(s): MIT, GPL
*/
const _ = require('lodash')
const { deepExtend } = require('./utils')
const slice = Array.prototype.slice
function riveterImpl () {
const riveter = function () {
const args = slice.call(arguments, 0)
while (args.length) {
riveter.rivet(args.shift())
}
}
riveter.rivet = function (fn) {
if (!Object.prototype.hasOwnProperty.call(fn, 'extend')) {
fn.extend = function (props, ctorProps, options) {
return riveter.extend(fn, props, ctorProps, options)
}
}
if (!Object.prototype.hasOwnProperty.call(fn, 'compose')) {
fn.compose = function () {
return riveter.compose.apply(
this,
[fn].concat(slice.call(arguments, 0))
)
}
}
if (!Object.prototype.hasOwnProperty.call(fn, 'inherits')) {
fn.inherits = function (parent, ctorProps, options) {
return riveter.inherits(fn, parent, ctorProps, options)
}
}
if (!Object.prototype.hasOwnProperty.call(fn, 'mixin')) {
fn.mixin = function () {
return riveter.mixin.apply(this, [fn].concat(slice.call(arguments, 0)))
}
}
if (!Object.prototype.hasOwnProperty.call(fn, 'punch')) {
fn.punch = function () {
return riveter.punch.apply(this, [fn].concat(slice.call(arguments, 0)))
}
}
}
riveter.inherits = function (child, parent, ctorProps, options) {
options = options || {}
let childProto
const TmpCtor = function () {}
let Child = function () {
parent.apply(this, arguments)
}
if (typeof child === 'object') {
if (Object.prototype.hasOwnProperty.call(child, 'constructor')) {
Child = child.constructor
}
childProto = child
} else {
Child = child
childProto = child.prototype
}
riveter.rivet(Child)
if (options.deep) {
deepExtend(Child, parent, ctorProps)
} else {
_.defaults(Child, parent, ctorProps)
}
TmpCtor.prototype = parent.prototype
Child.prototype = new TmpCtor()
if (options.deep) {
deepExtend(Child.prototype, childProto, {
constructor: Child
})
} else {
_.extend(Child.prototype, childProto, {
constructor: Child
})
}
Child.__super = parent
// Next line is all about Backbone compatibility
Child.__super__ = parent.prototype
return Child
}
riveter.extend = function (ctor, props, ctorProps, options) {
return riveter.inherits(props, ctor, ctorProps, options)
}
riveter.compose = function () {
const args = slice.call(arguments, 0)
const ctor = args.shift()
riveter.rivet(ctor)
const mixin = _.reduce(
args,
function (memo, val) {
if (Object.prototype.hasOwnProperty.call(val, '_preInit')) {
memo.preInit.push(val._preInit)
}
if (Object.prototype.hasOwnProperty.call(val, '_postInit')) {
memo.postInit.push(val._postInit)
}
val = val.mixin || val
memo.items.push(val)
return memo
},
{
items: [],
preInit: [],
postInit: []
}
)
const res = ctor.extend({
constructor: function () {
const self = this
const args = slice.call(arguments, 0)
_.each(mixin.preInit, function (initializer) {
initializer.apply(self, args)
})
ctor.prototype.constructor.apply(this, args)
_.each(mixin.postInit, function (initializer) {
initializer.apply(self, args)
})
}
})
riveter.rivet(res)
_.defaults(res.prototype, _.extend.apply(null, [{}].concat(mixin.items)))
return res
}
riveter.mixin = function () {
const args = slice.call(arguments, 0)
const ctor = args.shift()
riveter.rivet(ctor)
_.defaults(ctor.prototype, _.extend.apply(null, [{}].concat(args)))
return ctor
}
riveter.punch = function () {
const args = slice.call(arguments, 0)
const ctor = args.shift()
riveter.rivet(ctor)
_.extend(ctor.prototype, _.extend.apply(null, [{}].concat(args)))
return ctor
}
return riveter
}
module.exports = riveterImpl()