UNPKG

13.7 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.12.7
2(function() {
3 var cloneStructure, concatInto, defineModule, each, formattedInspect, isBoolean, isFunction, isNumber, isPlainArray, isPlainObject, isString, log, lowerCamelCase, merge, mergeInto, object, ref, upperCamelCase,
4 extend1 = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
5 hasProp = {}.hasOwnProperty,
6 slice = [].slice;
7
8 ref = require('art-standard-lib'), defineModule = ref.defineModule, log = ref.log, object = ref.object, upperCamelCase = ref.upperCamelCase, lowerCamelCase = ref.lowerCamelCase, each = ref.each, isPlainObject = ref.isPlainObject, isPlainArray = ref.isPlainArray, isFunction = ref.isFunction, isNumber = ref.isNumber, isBoolean = ref.isBoolean, cloneStructure = ref.cloneStructure, isString = ref.isString, mergeInto = ref.mergeInto, concatInto = ref.concatInto, formattedInspect = ref.formattedInspect, merge = ref.merge;
9
10
11 /*
12 Todo:
13 validatedDeclarable / validatedExtendableProperty
14 Which use Art.Validation
15
16 TODO:
17 When we switch to ES6, we should make the
18 class API look identical to the current instance API.
19
20 That means declarable API looks like this:
21 @extendableProperty foo: {}
22
23 * extend:
24 @foo: hi: 123
25
26 The differnce is we add a ":".
27
28 The benefit is it's a normal getter/setter pair:
29
30 @foo = hi: 123
31
32 log @foo
33
34 The one diference is the "setter" is really an
35 "extender"
36 */
37
38 defineModule(module, function() {
39 return function(superClass) {
40 var ExtendablePropertyMixin;
41 return ExtendablePropertyMixin = (function(superClass1) {
42 var arrayPropertyExtender, defaultExtender, getOwnProperty, noOptions, objectPropertyExtender, optimizedInitFunction;
43
44 extend1(ExtendablePropertyMixin, superClass1);
45
46 function ExtendablePropertyMixin() {
47 return ExtendablePropertyMixin.__super__.constructor.apply(this, arguments);
48 }
49
50
51 /*
52 IN
53 object: any object
54 property: string, property name
55 init:
56 (object) -> returning initial value for object
57 OR
58 initial value is computed by:
59 cloneStructure object[property] || init
60
61 EFFECT:
62 if object.hasOwnProperty property, return its current value
63 otherwise, initialize and return it with init()
64 */
65
66 ExtendablePropertyMixin.getOwnProperty = getOwnProperty = function(object, internalName, init) {
67 if (object.hasOwnProperty(internalName)) {
68 return object[internalName];
69 } else {
70 return object[internalName] = init(object, internalName);
71 }
72 };
73
74 optimizedInitFunction = function(internalName, init) {
75 switch (false) {
76 case !isFunction(init):
77 return init;
78 case !(isString(init) || isNumber(init) || isBoolean(init)):
79 return function(object) {
80 var ref1;
81 return (ref1 = object[internalName]) != null ? ref1 : init;
82 };
83 default:
84 return function(object) {
85 var ref1;
86 return cloneStructure((ref1 = object[internalName]) != null ? ref1 : init);
87 };
88 }
89 };
90
91
92 /*
93 objectPropertyExtender
94
95 IN: @ is set to the property-value to extend
96
97 API 1:
98 IN: map
99 EFFECT: mergeInto propValue, map
100
101 API 2:
102 IN: key, value
103 EFFECT: propValue[key] = valuee
104
105 OUT: ignore
106 */
107
108 ExtendablePropertyMixin.objectPropertyExtender = objectPropertyExtender = function(toExtend, mapOrKey, value) {
109 if (mapOrKey === void 0 || mapOrKey === null) {
110 return toExtend;
111 }
112 if (isString(mapOrKey)) {
113 toExtend[mapOrKey] = value;
114 } else if (isPlainObject(mapOrKey)) {
115 mergeInto(toExtend, mapOrKey);
116 } else {
117 log({
118 mapOrKey: mapOrKey,
119 value: value,
120 type: mapOrKey != null ? mapOrKey.constructor : void 0
121 });
122 throw new Error("first value argument must be a plain object or string: " + (formattedInspect({
123 key: mapOrKey,
124 value: value
125 })));
126 }
127 return toExtend;
128 };
129
130
131 /*
132 arrayPropertyExtender
133
134 IN: valueToExtend, value
135 value:
136 array: concatInto propValue, array
137 non-array: propValue.push value
138
139 NOTE: if you want to concat an array-as-a-value to the end of propValue, do this:
140 arrayPropertyExtender.call propValue, [arrayAsValue]
141
142 OUT: ignore
143 */
144
145 ExtendablePropertyMixin.arrayPropertyExtender = arrayPropertyExtender = function(toExtend, arrayOrValue) {
146 if (isPlainArray(arrayOrValue)) {
147 concatInto(toExtend, arrayOrValue);
148 } else {
149 toExtend.push(arrayOrValue);
150 }
151 return toExtend;
152 };
153
154
155 /*
156 Extendable Properties
157
158 EXAMPLE:
159 class Foo extends BaseClass
160 @extendableProperty foo: {}
161
162 Extendable properties work like inheritance:
163
164 When any subclass or instance extends an extendable property, they
165 inherit a cloneStructure of the property from up the inheritance tree, and then
166 add their own extensions without effecting the parent copy.
167
168 With Object property types, this can just be a parallel prototype chain.
169 (It isn't currently: if you modify a parent after extending it to a child,
170 the child won't get updates.)
171
172 BUT, you can also have array or other types of extend-properties, which
173 JavaScript doesn't have any built-in mechanisms for inheriting.
174
175 BASIC API:
176 @extendableProperty: (map, options) -> ...
177
178 IN:
179 map: name: defaultValue
180 options:
181 declarable: true/false
182 if true, slightly alters the created functions:
183 for: @extendableProperty foo: ...
184 generates:
185 @foo
186
187 extend:
188 DEFAULTS:
189 switch defaultValue
190 when is Object then objectPropertyExtender
191 when is Array then arrayPropetyExtender
192 else defaultExtender
193
194 (extendable, extendWithValues...) -> newExtendedOwnPropertyValue
195 IN:
196 extendable: the current, extended value, already cloned, so direct mutation is OK
197 extendWithValues: 1 or more values passed into the extend funtion by the client.
198 Ex: for an array, this is either a single value or an array
199 Ex: for an object, this is either a single object or two args: key, value
200 OUT: new property value to set own-property to
201 EFFECT:
202 Can be pure functional and just return the new, extended data.
203 OR
204 Can modify extendable directly, since it is an object/array/atomic value unique to the current class/instance.
205 If modifying extendable directly, be sure to return extendable.
206 Regardless, the returned value becomes the new extendable prop's value.
207
208
209
210 EFFECT: for each {foo: defaultValue} in map, extendableProperty:
211 WARNING:
212 !!! Don't modify the object returned by a getter !!!
213
214 Getters only return the current, most-extended property value. It may not be extended to the
215 current subclass or instance! Instead, call @extendFoo() if you wish to manually modify
216 the extended property.
217
218 declarable:
219 getters:
220 @getFoo:
221 getFoo:
222
223 extenders:
224 @foo:
225 foo:
226
227 non-declarable:
228
229 getters:
230 @getFoo:
231 @getter foo:
232
233 extenders:
234 @foo:
235 @extendFoo:
236 extendFoo:
237
238 IN:
239 0-args: nothing happens beyond the standard EFFECT
240 1+args: passed to the "extend" function
241
242 EFFECT: creates a extension (cloneStructure) of the property for the currnet class, subclass or instance
243 OUT: the current, extendedPropValue
244
245 API 1: IN: 0 args
246 NO ADDITIONAL EFFECT - just returns the extended property
247 API 2: IN: 1 or more args
248 In addition to extending and returning the extended property:
249 calls: propExtender extendedPropValue, args...
250
251 NOTE: gthe prototype getters call the class getter for extension purposes.
252 The result is each instance won't get its own version of the property.
253 E.G. Interitance is done at the Class level, not the Instance level.
254 */
255
256 defaultExtender = function(toExtend, v) {
257 if (v === void 0) {
258 throw new Error("not expecting undefined");
259 }
260 return v;
261 };
262
263 noOptions = {};
264
265 ExtendablePropertyMixin.extendableProperty = function(map, options) {
266 var declarable, extend, noSetter, oldExtender;
267 if (options == null) {
268 options = noOptions;
269 }
270 if (isFunction(oldExtender = options)) {
271 log.error("DEPRICATED customPropertyExtender not supported, use extend: option ");
272 options = {
273 extend: function() {
274 var args, extendable;
275 extendable = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
276 return oldExtender.apply(extendable, args);
277 }
278 };
279 }
280 extend = options.extend, declarable = options.declarable, noSetter = options.noSetter;
281 return each(map, (function(_this) {
282 return function(defaultValue, name) {
283 var extenderName, getterName, instanceExtender, instanceGetter, internalName, propertyExtender, ucProp;
284 name = lowerCamelCase(name);
285 ucProp = upperCamelCase(name);
286 internalName = _this.propInternalName(name);
287 getterName = "get" + ucProp;
288 extenderName = "extend" + ucProp;
289 propertyExtender = (function() {
290 if (extend != null) {
291 return extend;
292 } else if (isPlainObject(defaultValue)) {
293 return objectPropertyExtender;
294 } else if (isPlainArray(defaultValue)) {
295 return arrayPropertyExtender;
296 } else {
297 if (defaultValue === void 0) {
298 throw new Error("defaultValue must not be undefined");
299 }
300 return defaultExtender;
301 }
302 })();
303 _this[getterName] = function() {
304 var ref1;
305 return (ref1 = this.prototype[internalName]) != null ? ref1 : defaultValue;
306 };
307 _this[name] = _this[extenderName] = function(value) {
308 var extendablePropValue;
309 extendablePropValue = getOwnProperty(this.prototype, internalName, optimizedInitFunction(internalName, defaultValue));
310 if (arguments.length > 0 && value !== void 0) {
311 this.prototype[internalName] = propertyExtender.apply(null, [extendablePropValue].concat(slice.call(arguments)));
312 }
313 return extendablePropValue;
314 };
315 instanceGetter = function() {
316 var ref1;
317 return (ref1 = this[internalName]) != null ? ref1 : defaultValue;
318 };
319 instanceExtender = _this.prototype[extenderName] = function(value) {
320 var extendablePropValue;
321 extendablePropValue = getOwnProperty(this, internalName, optimizedInitFunction(internalName, defaultValue));
322 if (arguments.length > 0 && value !== void 0) {
323 this[internalName] = propertyExtender.apply(null, [extendablePropValue].concat(slice.call(arguments)));
324 }
325 return extendablePropValue;
326 };
327 if (declarable) {
328 _this.prototype[getterName] = instanceGetter;
329 return _this.prototype[name] = instanceExtender;
330 } else {
331 if (!noSetter) {
332 _this.addSetter(name, instanceExtender);
333 }
334 return _this.addGetter(name, instanceGetter);
335 }
336 };
337 })(this));
338 };
339
340 ExtendablePropertyMixin.declarable = function(map, options) {
341 return this.extendableProperty(map, merge(options, {
342 declarable: true
343 }));
344 };
345
346 return ExtendablePropertyMixin;
347
348 })(superClass);
349 };
350 });
351
352}).call(this);
353
354//# sourceMappingURL=ExtendablePropertyMixin.js.map