Files
tubestation/addon-sdk/source/lib/sdk/deprecated/traits.js

188 lines
6.0 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "deprecated"
};
const {
compose: _compose,
override: _override,
resolve: _resolve,
trait: _trait,
//create: _create,
required,
} = require('./traits/core');
const defineProperties = Object.defineProperties,
freeze = Object.freeze,
create = Object.create;
/**
* Work around bug 608959 by defining the _create function here instead of
* importing it from traits/core. For docs on this function, see the create
* function in that module.
*
* FIXME: remove this workaround in favor of importing the function once that
* bug has been fixed.
*/
function _create(proto, trait) {
let properties = {},
keys = Object.getOwnPropertyNames(trait);
for each(let key in keys) {
let descriptor = trait[key];
if (descriptor.required &&
!Object.prototype.hasOwnProperty.call(proto, key))
throw new Error('Missing required property: ' + key);
else if (descriptor.conflict)
throw new Error('Remaining conflicting property: ' + key);
else
properties[key] = descriptor;
}
return Object.create(proto, properties);
}
/**
* Placeholder for `Trait.prototype`
*/
let TraitProto = Object.prototype;
function Get(key) this[key]
function Set(key, value) this[key] = value
/**
* Creates anonymous trait descriptor from the passed argument, unless argument
* is a trait constructor. In later case trait's already existing properties
* descriptor is returned.
* This is module's internal function and is used as a gateway to a trait's
* internal properties descriptor.
* @param {Function} $
* Composed trait's constructor.
* @returns {Object}
* Private trait property of the composition.
*/
function TraitDescriptor(object)
(
'function' == typeof object &&
(object.prototype == TraitProto || object.prototype instanceof Trait)
) ? object._trait(TraitDescriptor) : _trait(object)
function Public(instance, trait) {
let result = {},
keys = Object.getOwnPropertyNames(trait);
for each (let key in keys) {
if ('_' === key.charAt(0) && '__iterator__' !== key )
continue;
let property = trait[key],
descriptor = {
configurable: property.configurable,
enumerable: property.enumerable
};
if (property.get)
descriptor.get = property.get.bind(instance);
if (property.set)
descriptor.set = property.set.bind(instance);
if ('value' in property) {
let value = property.value;
if ('function' === typeof value) {
descriptor.value = property.value.bind(instance);
descriptor.writable = property.writable;
} else {
descriptor.get = Get.bind(instance, key);
descriptor.set = Set.bind(instance, key);
}
}
result[key] = descriptor;
}
return result;
}
/**
* This is private function that composes new trait with privates.
*/
function Composition(trait) {
function Trait() {
let self = _create({}, trait);
self._public = create(Trait.prototype, Public(self, trait));
delete self._public.constructor;
if (Object === self.constructor)
self.constructor = Trait;
else
return self.constructor.apply(self, arguments) || self._public;
return self._public;
}
defineProperties(Trait, {
prototype: { value: freeze(create(TraitProto, {
constructor: { value: constructor, writable: true }
}))}, // writable is `true` to avoid getters in custom ES5
displayName: { value: (trait.constructor || constructor).name },
compose: { value: compose, enumerable: true },
override: { value: override, enumerable: true },
resolve: { value: resolve, enumerable: true },
required: { value: required, enumerable: true },
_trait: { value: function _trait(caller)
caller === TraitDescriptor ? trait : undefined
}
});
return freeze(Trait);
}
/**
* Composes new trait out of itself and traits / property maps passed as an
* arguments. If two or more traits / property maps have properties with the
* same name, the new trait will contain a "conflict" property for that name.
* This is a commutative and associative operation, and the order of its
* arguments is not significant.
* @params {Object|Function}
* List of Traits or property maps to create traits from.
* @returns {Function}
* New trait containing the combined properties of all the traits.
*/
function compose() {
let traits = Array.slice(arguments, 0);
traits.push(this);
return Composition(_compose.apply(null, traits.map(TraitDescriptor)));
}
/**
* Composes a new trait with all of the combined properties of `this` and the
* argument traits. In contrast to `compose`, `override` immediately resolves
* all conflicts resulting from this composition by overriding the properties of
* later traits. Trait priority is from left to right. I.e. the properties of
* the leftmost trait are never overridden.
* @params {Object} trait
* @returns {Object}
*/
function override() {
let traits = Array.slice(arguments, 0);
traits.push(this);
return Composition(_override.apply(null, traits.map(TraitDescriptor)));
}
/**
* Composes new resolved trait, with all the same properties as this
* trait, except that all properties whose name is an own property of
* `resolutions` will be renamed to `resolutions[name]`. If it is
* `resolutions[name]` is `null` value is changed into a required property
* descriptor.
*/
function resolve(resolutions)
Composition(_resolve(resolutions, TraitDescriptor(this)))
/**
* Base Trait, that all the traits are composed of.
*/
const Trait = Composition({
/**
* Internal property holding public API of this instance.
*/
_public: { value: null, configurable: true, writable: true },
toString: { value: function() '[object ' + this.constructor.name + ']' }
});
TraitProto = Trait.prototype;
exports.Trait = Trait;