UNPKG

@leansdk/leanrc

Version:

LeanRC is a MVC framework for creating graceful applications

995 lines (904 loc) 33.7 kB
(function() { // This file is part of LeanRC. // LeanRC is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // LeanRC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // You should have received a copy of the GNU Lesser General Public License // along with LeanRC. If not, see <https://www.gnu.org/licenses/>. var hasProp = {}.hasOwnProperty; /* ```coffee module.exports = (Module)-> Module.defineMixin Module::Record, (BaseClass) -> class TomatoEntryMixin extends BaseClass @inheritProtected() * Place for attributes and computeds definitions @attribute title: String, validate: -> joi.string() # !!! нужен для сложной валидации данных * transform указывать не надо, т.к. стандартный тип, Module::StringTransform @attribute nameObj: Module::NameObj, validate: -> joi.object().required().start().end().default({}) transform: -> Module::NameObjTransform # or some record class Module::OnionRecord @attribute description: String @attribute registeredAt: Date, validate: -> joi.date().iso() transform: -> Module::MyDateTransform TomatoEntryMixin.initializeMixin() ``` ```coffee module.exports = (Module)-> { Record TomatoEntryMixin } = Module:: class TomatoRecord extends Record @inheritProtected() @include TomatoEntryMixin @module Module * business logic and before-, after- colbacks TomatoRecord.initialize() ``` */ module.exports = function(Module) { var AnyT, AttributeConfigT, AttributeOptionsT, ChainsMixin, CollectionInterface, ComputedConfigT, ComputedOptionsT, CoreObject, DictG, FuncG, JoiT, ListG, MaybeG, PointerT, PropertyDefinitionT, Record, RecordInterface, SubsetG, TupleG, UnionG, _, inflect, joi; ({ AnyT, PointerT, JoiT, PropertyDefinitionT, AttributeOptionsT, ComputedOptionsT, AttributeConfigT, ComputedConfigT, FuncG, TupleG, MaybeG, SubsetG, DictG, ListG, UnionG, RecordInterface, CollectionInterface, CoreObject, ChainsMixin, Utils: {_, inflect, joi} } = Module.prototype); return Record = (function() { var ipoInternalRecord, ipoSchemas; class Record extends CoreObject {}; Record.inheritProtected(); Record.include(ChainsMixin); Record.implements(RecordInterface); Record.module(Module); ipoInternalRecord = PointerT(Record.protected({ internalRecord: MaybeG(Object) })); ipoSchemas = PointerT(Record.protected(Record.static({ schemas: DictG(String, JoiT) }, { default: {} }))); Record.public({ collection: CollectionInterface }); Record.public(Record.static({ schema: JoiT }, { get: function() { var base, name1; if ((base = this[ipoSchemas])[name1 = this.name] == null) { base[name1] = (() => { var ahValue, asAttr, ref, ref1, vhAttrs; vhAttrs = {}; ref = this.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ahValue = ref[asAttr]; vhAttrs[asAttr] = ((asAttr, ahValue) => { if (_.isFunction(ahValue.validate)) { return ahValue.validate.call(this); } else { return ahValue.validate; } })(asAttr, ahValue); } ref1 = this.computeds; for (asAttr in ref1) { if (!hasProp.call(ref1, asAttr)) continue; ahValue = ref1[asAttr]; vhAttrs[asAttr] = ((asAttr, ahValue) => { if (_.isFunction(ahValue.validate)) { return ahValue.validate.call(this); } else { return ahValue.validate; } })(asAttr, ahValue); } return joi.object(vhAttrs); })(); } return this[ipoSchemas][this.name]; } })); Record.public(Record.static(Record.async({ normalize: FuncG([MaybeG(Object), CollectionInterface], RecordInterface) }, { default: function*(ahPayload, aoCollection) { var RecordClass, asAttr, ref, transform, vhAttributes, voRecord; if (ahPayload == null) { return null; } vhAttributes = {}; if (ahPayload.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } RecordClass = this.name === ahPayload.type.split('::')[1] ? this : this.findRecordByName(ahPayload.type); ref = RecordClass.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ({transform} = ref[asAttr]); vhAttributes[asAttr] = (yield transform.call(RecordClass).normalize(ahPayload[asAttr])); } vhAttributes.type = ahPayload.type; // NOTE: vhAttributes processed before new - it for StateMachine in record (when it has) voRecord = RecordClass.new(vhAttributes, aoCollection); voRecord[ipoInternalRecord] = voRecord.constructor.makeSnapshot(voRecord); return voRecord; } }))); Record.public(Record.static(Record.async({ serialize: FuncG([MaybeG(RecordInterface)], MaybeG(Object)) }, { default: function*(aoRecord) { var asAttr, ref, transform, vhResult; if (aoRecord == null) { return null; } if (aoRecord.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } vhResult = {}; ref = aoRecord.constructor.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ({transform} = ref[asAttr]); vhResult[asAttr] = (yield transform.call(this).serialize(aoRecord[asAttr])); } return vhResult; } }))); Record.public(Record.static(Record.async({ recoverize: FuncG([MaybeG(Object), CollectionInterface], MaybeG(RecordInterface)) }, { default: function*(ahPayload, aoCollection) { var RecordClass, asAttr, ref, transform, vhAttributes, voRecord; if (ahPayload == null) { return null; } vhAttributes = {}; if (ahPayload.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } RecordClass = this.name === ahPayload.type.split('::')[1] ? this : this.findRecordByName(ahPayload.type); ref = RecordClass.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ({transform} = ref[asAttr]); if (asAttr in ahPayload) { vhAttributes[asAttr] = (yield transform.call(RecordClass).normalize(ahPayload[asAttr])); } } vhAttributes.type = ahPayload.type; // NOTE: vhAttributes processed before new - it for StateMachine in record (when it has) voRecord = RecordClass.new(vhAttributes, aoCollection); return voRecord; } }))); Record.public(Record.static({ objectize: FuncG([MaybeG(RecordInterface), MaybeG(Object)], MaybeG(Object)) }, { default: function(aoRecord) { var asAttr, ref, ref1, transform, vhResult; if (aoRecord == null) { return null; } if (aoRecord.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } vhResult = {}; ref = aoRecord.constructor.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ({transform} = ref[asAttr]); vhResult[asAttr] = transform.call(this).objectize(aoRecord[asAttr]); } ref1 = aoRecord.constructor.computeds; for (asAttr in ref1) { if (!hasProp.call(ref1, asAttr)) continue; ({transform} = ref1[asAttr]); vhResult[asAttr] = transform.call(this).objectize(aoRecord[asAttr]); } return vhResult; } })); Record.public(Record.static({ makeSnapshot: FuncG([MaybeG(RecordInterface)], MaybeG(Object)) }, { default: function(aoRecord) { var asAttr, ref, transform, vhResult; if (aoRecord == null) { return null; } if (aoRecord.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } vhResult = {}; ref = aoRecord.constructor.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; ({transform} = ref[asAttr]); vhResult[asAttr] = transform.call(this).objectize(aoRecord[asAttr]); } return vhResult; } })); Record.public(Record.static({ parseRecordName: FuncG(String, TupleG(String, String)) }, { default: function(asName) { var vsModuleName, vsRecordName; if (/.*[:][:].*/.test(asName)) { [vsModuleName, vsRecordName] = asName.split('::'); } else { [vsModuleName, vsRecordName] = [this.moduleName(), inflect.camelize(inflect.underscore(inflect.singularize(asName)))]; } if (!/(Record$)|(Migration$)/.test(vsRecordName)) { vsRecordName += 'Record'; } return [vsModuleName, vsRecordName]; } })); Record.public({ parseRecordName: FuncG(String, TupleG(String, String)) }, { default: function(...args) { return this.constructor.parseRecordName(...args); } }); Record.public(Record.static({ findRecordByName: FuncG(String, SubsetG(RecordInterface)) }, { default: function(asName) { var ref, ref1, vsModuleName, vsRecordName; [vsModuleName, vsRecordName] = this.parseRecordName(asName); return (ref = ((ref1 = this.Module.NS) != null ? ref1 : this.Module.prototype)[vsRecordName]) != null ? ref : this; } })); Record.public({ findRecordByName: FuncG(String, SubsetG(RecordInterface)) }, { default: function(asName) { return this.constructor.findRecordByName(asName); } }); /* @customFilter -> reason: '$eq': (value)-> * string of some aql code for example '$neq': (value)-> * string of some aql code for example */ Record.public(Record.static({ customFilters: Object }, { get: function() { return this.metaObject.getGroup('customFilters', false); } })); Record.public(Record.static({ customFilter: FuncG(Function) }, { default: function(amStatementFunc) { var aoStatement, asFilterName, config; config = amStatementFunc.call(this); for (asFilterName in config) { if (!hasProp.call(config, asFilterName)) continue; aoStatement = config[asFilterName]; this.metaObject.addMetaData('customFilters', asFilterName, aoStatement); } } })); Record.public(Record.static({ parentClassNames: FuncG([MaybeG(SubsetG(RecordInterface))], ListG(String)) }, { default: function(AbstractClass = null) { var SuperClass, fromSuper; if (AbstractClass == null) { AbstractClass = this; } SuperClass = Reflect.getPrototypeOf(AbstractClass); fromSuper = !_.isEmpty(SuperClass != null ? SuperClass.name : void 0) ? this.parentClassNames(SuperClass) : void 0; return _.uniq([].concat(fromSuper != null ? fromSuper : [])).concat([AbstractClass.name]); } })); Record.public(Record.static({ attributes: DictG(String, AttributeConfigT) }, { get: function() { return this.metaObject.getGroup('attributes', false); } })); Record.public(Record.static({ computeds: DictG(String, ComputedConfigT) }, { get: function() { return this.metaObject.getGroup('computeds', false); } })); Record.public(Record.static({ attribute: FuncG([PropertyDefinitionT, AttributeOptionsT]) }, { default: function() { this.attr(...arguments); } })); Record.public(Record.static({ attr: FuncG([PropertyDefinitionT, AttributeOptionsT]) }, { default: function(typeDefinition, opts = {}) { var set, vcAttrType, vsAttr; [vsAttr] = Object.keys(typeDefinition); vcAttrType = typeDefinition[vsAttr]; // NOTE: это всего лишь автоматическое применение трансформа, если он не указан явно. здесь НЕ надо автоматически подставить нужный рекорд или кастомный трансформ - они если должны использоваться, должны быть указаны вручную в схеме рекорда программистом. if (opts.transform == null) { opts.transform = (function() { switch (vcAttrType) { case String: case Date: case Number: case Boolean: case Array: case Object: return function() { return Module.prototype[`${vcAttrType.name}Transform`]; }; default: return function() { return Module.prototype.Transform; }; } })(); } if (opts.validate == null) { opts.validate = function() { return opts.transform.call(this).schema; }; } ({set} = opts); opts.set = function(aoData) { var voData; ({ value: voData } = opts.validate.call(this).validate(aoData)); if (_.isFunction(set)) { return set.apply(this, [voData]); } else { return voData; } }; if (this.attributes[vsAttr] != null) { throw new Error(`attribute \`${vsAttr}\` has been defined previously`); } else { this.metaObject.addMetaData('attributes', vsAttr, opts); } this.public({ [vsAttr]: Module.prototype.MaybeG(vcAttrType) }, opts); } })); Record.public(Record.static({ computed: FuncG([PropertyDefinitionT, ComputedOptionsT]) }, { default: function() { this.comp(...arguments); } })); // NOTE: изначальная задумка была в том, чтобы определять вычисляемые значения - НЕ ПРОМИСЫ! (т.е. некоторое значение, которое отправляется в респонзе реально не хранится в базе, но вычисляется НЕ асинхронной функцией-гетером) Record.public(Record.static({ comp: FuncG([PropertyDefinitionT, ComputedOptionsT]) }, { default: function(typeDefinition, opts) { var vcAttrType, vsAttr; // [typeDefinition, ..., opts] = args // if typeDefinition is opts // typeDefinition = "#{opts.attr}": opts.attrType [vsAttr] = Object.keys(typeDefinition); vcAttrType = typeDefinition[vsAttr]; // NOTE: это всего лишь автоматическое применение трансформа, если он не указан явно. здесь не надо автоматически подставить нужный рекорд или кастомный трансформ - они если должны использоваться, должны быть указаны вручную в схеме рекорда программистом. if (opts.transform == null) { opts.transform = (function() { switch (vcAttrType) { case String: case Date: case Number: case Boolean: case Array: case Object: return function() { return Module.prototype[`${vcAttrType.name}Transform`]; }; default: return function() { return Module.prototype.Transform; }; } })(); } if (opts.validate == null) { opts.validate = function() { return opts.transform.call(this).schema.strip(); }; } if (opts.get == null) { throw new Error('getter `lambda` options is required'); } if (opts.set != null) { throw new Error('setter `lambda` options is forbidden'); } if (this.computeds[vsAttr] != null) { throw new Error(`computed \`${vsAttr}\` has been defined previously`); } else { this.metaObject.addMetaData('computeds', vsAttr, opts); } this.public({ [vsAttr]: Module.prototype.MaybeG(vcAttrType) }, opts); } })); Record.public(Record.static({ new: FuncG([Object, CollectionInterface], RecordInterface) }, { default: function(aoAttributes, aoCollection) { var RecordClass; if (aoAttributes == null) { aoAttributes = {}; } if (aoAttributes.type == null) { throw new Error("Attribute `type` is required and format 'ModuleName::RecordClassName'"); } if (this.name === aoAttributes.type.split('::')[1]) { return this.super(aoAttributes, aoCollection); } else { RecordClass = this.findRecordByName(aoAttributes.type); if (RecordClass === this) { return this.super(aoAttributes, aoCollection); } else { return RecordClass.new(aoAttributes, aoCollection); } } } })); Record.public(Record.async({ save: FuncG([], RecordInterface) }, { default: function*() { var result; result = (yield this.isNew()) ? (yield this.create()) : (yield this.update()); return result; } })); Record.public(Record.async({ create: FuncG([], RecordInterface) }, { default: function*() { var response; // console.log '>>??? create push ', @, @collection response = (yield this.collection.push(this)); // response = yield @collection.push.body.call @collection, @ // console.log '>>>>?????????????????????', response, response.collection // console.log '>>>>????????????????????? is', CollectionInterface.is response.collection yield this.reloadRecord(response); return this; } })); Record.public(Record.async({ update: FuncG([], RecordInterface) }, { default: function*() { var response; response = (yield this.collection.override(this.id, this)); yield this.reloadRecord(response); return this; } })); Record.public(Record.async({ delete: FuncG([], RecordInterface) }, { default: function*() { if ((yield this.isNew())) { throw new Error('Document is not exist in collection'); } this.isHidden = true; this.updatedAt = new Date(); return (yield this.save()); } })); Record.public(Record.async({ destroy: Function }, { default: function*() { if ((yield this.isNew())) { throw new Error('Document is not exist in collection'); } yield this.collection.remove(this.id); } })); Record.attribute({ id: UnionG(String, Number) }, { transform: function() { return Module.prototype.StringTransform; } }); Record.attribute({ rev: String }); Record.attribute({ type: String }); Record.attribute({ isHidden: Boolean }, { validate: function() { return joi.boolean().empty(null).default(false, 'false by default'); }, default: false }); Record.attribute({ createdAt: Date }); Record.attribute({ updatedAt: Date }); Record.attribute({ deletedAt: Date }); Record.chains(['create', 'update', 'delete', 'destroy']); Record.beforeHook('beforeUpdate', { only: ['update'] }); Record.beforeHook('beforeCreate', { only: ['create'] }); Record.afterHook('afterUpdate', { only: ['update'] }); Record.afterHook('afterCreate', { only: ['create'] }); Record.beforeHook('beforeDelete', { only: ['delete'] }); Record.afterHook('afterDelete', { only: ['delete'] }); Record.afterHook('afterDestroy', { only: ['destroy'] }); Record.public(Record.async({ afterCreate: FuncG(RecordInterface, RecordInterface) }, { default: function*(aoRecord) { this.collection.recordHasBeenChanged('createdRecord', aoRecord); return this; } })); Record.public(Record.async({ beforeUpdate: Function }, { default: function*(...args) { this.updatedAt = new Date(); return args; } })); Record.public(Record.async({ beforeCreate: Function }, { default: function*(...args) { var now; if (this.id == null) { this.id = (yield this.collection.generateId(this)); } now = new Date(); if (this.createdAt == null) { this.createdAt = now; } if (this.updatedAt == null) { this.updatedAt = now; } return args; } })); Record.public(Record.async({ afterUpdate: FuncG(RecordInterface, RecordInterface) }, { default: function*(aoRecord) { this.collection.recordHasBeenChanged('updatedRecord', aoRecord); return this; } })); Record.public(Record.async({ beforeDelete: Function }, { default: function*(...args) { var now; this.isHidden = true; now = new Date(); this.updatedAt = now; this.deletedAt = now; return args; } })); Record.public(Record.async({ afterDelete: FuncG(RecordInterface, RecordInterface) }, { default: function*(aoRecord) { this.collection.recordHasBeenChanged('deletedRecord', aoRecord); return this; } })); Record.public(Record.async({ afterDestroy: FuncG([]) }, { default: function*() { this.collection.recordHasBeenChanged('destroyedRecord', this); } })); // NOTE: метод должен вернуть список атрибутов данного рекорда. Record.public({ attributes: FuncG([], Object) }, { default: function() { return Object.keys(this.constructor.attributes); } }); // NOTE: в оперативной памяти создается клон рекорда, НО с другим id Record.public(Record.async({ clone: FuncG([], RecordInterface) }, { default: function*() { return (yield this.collection.clone(this)); } })); // NOTE: в коллекции создается копия рекорда, НО с другим id Record.public(Record.async({ copy: FuncG([], RecordInterface) }, { default: function*() { return (yield this.collection.copy(this)); } })); Record.public(Record.async({ decrement: FuncG([String, MaybeG(Number)], RecordInterface) }, { default: function*(asAttribute, step = 1) { if (!_.isNumber(this[asAttribute])) { throw new Error(`doc.attribute \`${asAttribute}\` is not Number`); } this[asAttribute] -= step; return (yield this.save()); } })); Record.public(Record.async({ increment: FuncG([String, MaybeG(Number)], RecordInterface) }, { default: function*(asAttribute, step = 1) { if (!_.isNumber(this[asAttribute])) { throw new Error(`doc.attribute \`${asAttribute}\` is not Number`); } this[asAttribute] += step; return (yield this.save()); } })); Record.public(Record.async({ toggle: FuncG(String, RecordInterface) }, { default: function*(asAttribute) { if (!_.isBoolean(this[asAttribute])) { throw new Error(`doc.attribute \`${asAttribute}\` is not Boolean`); } this[asAttribute] = !this[asAttribute]; return (yield this.save()); } })); Record.public(Record.async({ touch: FuncG([], RecordInterface) }, { default: function*() { this.updatedAt = new Date(); return (yield this.save()); } })); Record.public(Record.async({ updateAttribute: FuncG([String, MaybeG(AnyT)], RecordInterface) }, { default: function*(name, value) { this[name] = value; return (yield this.save()); } })); Record.public(Record.async({ updateAttributes: FuncG(Object, RecordInterface) }, { default: function*(aoAttributes) { var voAttrValue, vsAttrName; for (vsAttrName in aoAttributes) { if (!hasProp.call(aoAttributes, vsAttrName)) continue; voAttrValue = aoAttributes[vsAttrName]; this[vsAttrName] = voAttrValue; } return (yield this.save()); } })); Record.public(Record.async({ isNew: FuncG([], Boolean) }, { default: function*() { if (this.id == null) { return true; } return !((yield this.collection.includes(this.id))); } })); Record.public(Record.async({ reload: FuncG([], RecordInterface) }, { default: function*() { var response; if (this.id == null) { return; } response = (yield this.collection.take(this.id)); yield this.reloadRecord(response); return this; } })); Record.public(Record.async({ reloadRecord: FuncG(UnionG(Object, RecordInterface)) }, { default: function*(response) { var asAttr, ref; if (response != null) { ref = this.constructor.attributes; for (asAttr in ref) { if (!hasProp.call(ref, asAttr)) continue; this[asAttr] = response[asAttr]; } this[ipoInternalRecord] = response[ipoInternalRecord]; } } })); // TODO: не учтены установки значений, которые раньше не были установлены Record.public(Record.async({ changedAttributes: FuncG([], DictG(String, Array)) }, { default: function*() { var ref, ref1, transform, vhResult, voNewValue, voOldValue, vsAttrName; vhResult = {}; ref = this.constructor.attributes; for (vsAttrName in ref) { if (!hasProp.call(ref, vsAttrName)) continue; ({transform} = ref[vsAttrName]); voOldValue = (ref1 = this[ipoInternalRecord]) != null ? ref1[vsAttrName] : void 0; voNewValue = transform.call(this.constructor).objectize(this[vsAttrName]); if (!_.isEqual(voNewValue, voOldValue)) { vhResult[vsAttrName] = [voOldValue, voNewValue]; } } return vhResult; } })); Record.public(Record.async({ resetAttribute: FuncG(String) }, { default: function*(asAttribute) { var attrConf, transform; if (this[ipoInternalRecord] != null) { if ((attrConf = this.constructor.attributes[asAttribute]) != null) { ({transform} = attrConf); this[asAttribute] = (yield transform.call(this.constructor).normalize(this[ipoInternalRecord][asAttribute])); } } } })); Record.public(Record.async({ rollbackAttributes: Function }, { default: function*() { var ref, transform, voOldValue, vsAttrName; if (this[ipoInternalRecord] != null) { ref = this.constructor.attributes; for (vsAttrName in ref) { if (!hasProp.call(ref, vsAttrName)) continue; ({transform} = ref[vsAttrName]); voOldValue = this[ipoInternalRecord][vsAttrName]; this[vsAttrName] = (yield transform.call(this.constructor).normalize(voOldValue)); } } } })); Record.public(Record.static(Record.async({ restoreObject: FuncG([SubsetG(Module), Object], RecordInterface) }, { default: function*(Module, replica) { var Facade, collection, facade, instance, ref; if ((replica != null ? replica.class : void 0) === this.name && (replica != null ? replica.type : void 0) === 'instance') { Facade = (ref = Module.prototype.ApplicationFacade) != null ? ref : Module.prototype.Facade; facade = Facade.getInstance(replica.multitonKey); collection = facade.retrieveProxy(replica.collectionName); if (replica.isNew) { // NOTE: оставлено временно для обратной совместимости. Понятно что в будущем надо эту ветку удалить. instance = (yield collection.build(replica.attributes)); } else { instance = (yield collection.find(replica.id)); } return instance; } else { return (yield this.super(Module, replica)); } } }))); Record.public(Record.static(Record.async({ replicateObject: FuncG(RecordInterface, Object) }, { default: function*(instance) { var changedAttributes, changedKeys, ipsMultitonKey, replica; replica = (yield this.super(instance)); ipsMultitonKey = Symbol.for('~multitonKey'); replica.multitonKey = instance.collection[ipsMultitonKey]; replica.collectionName = instance.collection.getProxyName(); replica.isNew = (yield instance.isNew()); if (replica.isNew) { throw new Error("Replicating record is `new`. It must be seved previously"); } else { changedAttributes = (yield instance.changedAttributes()); if ((changedKeys = Object.keys(changedAttributes)).length > 0) { throw new Error(`Replicating record has changedAttributes ${changedKeys}`); } replica.id = instance.id; } return replica; } }))); Record.public({ init: FuncG([Object, CollectionInterface]) }, { default: function(aoProperties, aoCollection) { var voAttrValue, vsAttrName; this.super(...arguments); this.collection = aoCollection; for (vsAttrName in aoProperties) { if (!hasProp.call(aoProperties, vsAttrName)) continue; voAttrValue = aoProperties[vsAttrName]; this[vsAttrName] = voAttrValue; } } }); Record.public({ toJSON: FuncG([], Object) }, { default: function() { return this.constructor.objectize(this); } }); Record.initialize(); return Record; }).call(this); }; }).call(this);