traits.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "deprecated"
  7. };
  8. const {
  9. compose: _compose,
  10. override: _override,
  11. resolve: _resolve,
  12. trait: _trait,
  13. //create: _create,
  14. required,
  15. } = require('./traits/core');
  16. const defineProperties = Object.defineProperties,
  17. freeze = Object.freeze,
  18. create = Object.create;
  19. /**
  20. * Work around bug 608959 by defining the _create function here instead of
  21. * importing it from traits/core. For docs on this function, see the create
  22. * function in that module.
  23. *
  24. * FIXME: remove this workaround in favor of importing the function once that
  25. * bug has been fixed.
  26. */
  27. function _create(proto, trait) {
  28. let properties = {},
  29. keys = Object.getOwnPropertyNames(trait);
  30. for each(let key in keys) {
  31. let descriptor = trait[key];
  32. if (descriptor.required &&
  33. !Object.prototype.hasOwnProperty.call(proto, key))
  34. throw new Error('Missing required property: ' + key);
  35. else if (descriptor.conflict)
  36. throw new Error('Remaining conflicting property: ' + key);
  37. else
  38. properties[key] = descriptor;
  39. }
  40. return Object.create(proto, properties);
  41. }
  42. /**
  43. * Placeholder for `Trait.prototype`
  44. */
  45. let TraitProto = Object.prototype;
  46. function Get(key) this[key]
  47. function Set(key, value) this[key] = value
  48. /**
  49. * Creates anonymous trait descriptor from the passed argument, unless argument
  50. * is a trait constructor. In later case trait's already existing properties
  51. * descriptor is returned.
  52. * This is module's internal function and is used as a gateway to a trait's
  53. * internal properties descriptor.
  54. * @param {Function} $
  55. * Composed trait's constructor.
  56. * @returns {Object}
  57. * Private trait property of the composition.
  58. */
  59. function TraitDescriptor(object)
  60. (
  61. 'function' == typeof object &&
  62. (object.prototype == TraitProto || object.prototype instanceof Trait)
  63. ) ? object._trait(TraitDescriptor) : _trait(object)
  64. function Public(instance, trait) {
  65. let result = {},
  66. keys = Object.getOwnPropertyNames(trait);
  67. for each (let key in keys) {
  68. if ('_' === key.charAt(0) && '__iterator__' !== key )
  69. continue;
  70. let property = trait[key],
  71. descriptor = {
  72. configurable: property.configurable,
  73. enumerable: property.enumerable
  74. };
  75. if (property.get)
  76. descriptor.get = property.get.bind(instance);
  77. if (property.set)
  78. descriptor.set = property.set.bind(instance);
  79. if ('value' in property) {
  80. let value = property.value;
  81. if ('function' === typeof value) {
  82. descriptor.value = property.value.bind(instance);
  83. descriptor.writable = property.writable;
  84. } else {
  85. descriptor.get = Get.bind(instance, key);
  86. descriptor.set = Set.bind(instance, key);
  87. }
  88. }
  89. result[key] = descriptor;
  90. }
  91. return result;
  92. }
  93. /**
  94. * This is private function that composes new trait with privates.
  95. */
  96. function Composition(trait) {
  97. function Trait() {
  98. let self = _create({}, trait);
  99. self._public = create(Trait.prototype, Public(self, trait));
  100. delete self._public.constructor;
  101. if (Object === self.constructor)
  102. self.constructor = Trait;
  103. else
  104. return self.constructor.apply(self, arguments) || self._public;
  105. return self._public;
  106. }
  107. defineProperties(Trait, {
  108. prototype: { value: freeze(create(TraitProto, {
  109. constructor: { value: constructor, writable: true }
  110. }))}, // writable is `true` to avoid getters in custom ES5
  111. displayName: { value: (trait.constructor || constructor).name },
  112. compose: { value: compose, enumerable: true },
  113. override: { value: override, enumerable: true },
  114. resolve: { value: resolve, enumerable: true },
  115. required: { value: required, enumerable: true },
  116. _trait: { value: function _trait(caller)
  117. caller === TraitDescriptor ? trait : undefined
  118. }
  119. });
  120. return freeze(Trait);
  121. }
  122. /**
  123. * Composes new trait out of itself and traits / property maps passed as an
  124. * arguments. If two or more traits / property maps have properties with the
  125. * same name, the new trait will contain a "conflict" property for that name.
  126. * This is a commutative and associative operation, and the order of its
  127. * arguments is not significant.
  128. * @params {Object|Function}
  129. * List of Traits or property maps to create traits from.
  130. * @returns {Function}
  131. * New trait containing the combined properties of all the traits.
  132. */
  133. function compose() {
  134. let traits = Array.slice(arguments, 0);
  135. traits.push(this);
  136. return Composition(_compose.apply(null, traits.map(TraitDescriptor)));
  137. }
  138. /**
  139. * Composes a new trait with all of the combined properties of `this` and the
  140. * argument traits. In contrast to `compose`, `override` immediately resolves
  141. * all conflicts resulting from this composition by overriding the properties of
  142. * later traits. Trait priority is from left to right. I.e. the properties of
  143. * the leftmost trait are never overridden.
  144. * @params {Object} trait
  145. * @returns {Object}
  146. */
  147. function override() {
  148. let traits = Array.slice(arguments, 0);
  149. traits.push(this);
  150. return Composition(_override.apply(null, traits.map(TraitDescriptor)));
  151. }
  152. /**
  153. * Composes new resolved trait, with all the same properties as this
  154. * trait, except that all properties whose name is an own property of
  155. * `resolutions` will be renamed to `resolutions[name]`. If it is
  156. * `resolutions[name]` is `null` value is changed into a required property
  157. * descriptor.
  158. */
  159. function resolve(resolutions)
  160. Composition(_resolve(resolutions, TraitDescriptor(this)))
  161. /**
  162. * Base Trait, that all the traits are composed of.
  163. */
  164. const Trait = Composition({
  165. /**
  166. * Internal property holding public API of this instance.
  167. */
  168. _public: { value: null, configurable: true, writable: true },
  169. toString: { value: function() '[object ' + this.constructor.name + ']' }
  170. });
  171. TraitProto = Trait.prototype;
  172. exports.Trait = Trait;