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

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

mercurial