Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions lib/descriptor-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,13 @@ DescriptorBuilder.prototype.addNamedProperty = function(p, validate) {
propsByName = this.propertiesByName;

if (validate) {
this.assertNotDefined(p, ns.name);
this.assertNotDefined(p, ns.localName);
this.assertNotDefined(p);
}

propsByName[ns.name] = propsByName[ns.localName] = p;
if (!this.propertiesByName[ns.name])
propsByName[ns.name] = propsByName[ns.localName] = p;
else if (!this.propertiesByName[ns.localName])
propsByName[ns.localName] = p;
};

DescriptorBuilder.prototype.removeNamedProperty = function(p) {
Expand Down Expand Up @@ -172,15 +174,18 @@ DescriptorBuilder.prototype.setIdProperty = function(p, validate) {
this.idProperty = p;
};

DescriptorBuilder.prototype.assertNotDefined = function(p, name) {
var propertyName = p.name,
DescriptorBuilder.prototype.assertNotDefined = function(p) {
var ns = p.ns,
propertyName = p.name,
definedProperty = this.propertiesByName[propertyName];

if (definedProperty) {
if (this.propertiesByName[ns.name] && this.propertiesByName[ns.localName]) {
throw new Error(
'property <' + propertyName + '> already defined; ' +
'override of <' + definedProperty.definedBy.ns.name + '#' + definedProperty.ns.name + '> by ' +
'<' + p.definedBy.ns.name + '#' + p.ns.name + '> not allowed without redefines');
'override of <' + definedProperty.definedBy.ns.name + '#' +
definedProperty.ns.name + '> by ' +
'<' + p.definedBy.ns.name + '#' + p.ns.name +
'> not allowed without redefines');
}
};

Expand Down Expand Up @@ -230,4 +235,4 @@ DescriptorBuilder.prototype.addTrait = function(t, inherited) {

types.push(t);
typesByName[typeName] = t;
};
};
44 changes: 31 additions & 13 deletions lib/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ export default function Properties(model) {
Properties.prototype.set = function(target, name, value) {

var property = this.model.getPropertyDescriptor(target, name);

var propertyName = property && property.name;
var propertyName = getPropertyName(target, name, property);

if (isUndefined(value)) {
// unset the property, if the specified value is undefined;
Expand All @@ -37,14 +36,20 @@ Properties.prototype.set = function(target, name, value) {
if (propertyName in target) {
target[propertyName] = value;
} else {
defineProperty(target, property, value);
Object.defineProperty(target, propertyName, {
enumerable: !property.isReference,
writable: true,
value: value,
configurable: true
});
}
} else {
target.$attrs[name] = value;
}
}
};


/**
* Returns the named property of the given element
*
Expand All @@ -61,11 +66,16 @@ Properties.prototype.get = function(target, name) {
return target.$attrs[name];
}

var propertyName = property.name;
var propertyName = getPropertyName(target, name, property);

// check if access to collection property and lazily initialize it
if (!target[propertyName] && property.isMany) {
defineProperty(target, property, []);
Object.defineProperty(target, propertyName, {
enumerable: !property.isReference,
writable: true,
value: [],
configurable: true
});
}

return target[propertyName];
Expand Down Expand Up @@ -103,11 +113,19 @@ function isUndefined(val) {
return typeof val === 'undefined';
}

function defineProperty(target, property, value) {
Object.defineProperty(target, property.name, {
enumerable: !property.isReference,
writable: true,
value: value,
configurable: true
});
}

function getPropertyName(target, name, descriptor) {
if (!target || !name || !descriptor)
return undefined;

var propertyName = descriptor.name;

if (name.includes(':')) {
var parts = name.split(':');

if (parts[0] !== target.$descriptor.ns.prefix)
propertyName = descriptor.ns.name;
}

return propertyName;
}
23 changes: 23 additions & 0 deletions test/fixtures/model/multiple-inherited-properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "MultipleInheritance",
"uri": "http://multipleinheritance",
"prefix": "mh",
"types": [
{
"name": "AnotherRoot",
"properties": [
{
"name": "any",
"type": "AnotherRoot",
"isMany": true
}
]
},
{
"name": "MultipleInherited",
"superClass": [ "AnotherRoot", "props:Root" ],
"properties": [
]
}
]
}
9 changes: 8 additions & 1 deletion test/fixtures/model/properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@
{ "name": "idNumeric", "type": "Integer", "isAttr": true, "redefines": "BaseWithId#id", "isId": true }
]
},
{
"name": "BaseWithAlreadyDefinedId",
"superClass": [ "BaseWithId" ],
"properties": [
{ "name": "id", "type": "Integer", "isAttr": true, "isId": true }
]
},
{
"name": "Attributes",
"superClass": [ "BaseWithId" ],
Expand Down Expand Up @@ -171,4 +178,4 @@
]
}
]
}
}
148 changes: 148 additions & 0 deletions test/spec/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ describe('properties', function() {
expect(inheritedAnyProperty).to.exist;
});


it('should NOT add already defined property without redefine', function() {

// when
var getType = function() {
model.getType('props:BaseWithAlreadyDefinedId');
};

// then
expect(getType).to.throw(Error);
});

});


Expand Down Expand Up @@ -455,4 +467,140 @@ describe('properties', function() {

});

describe('multiple inherited properties', function() {

// const createModel = createModelBuilder('test/fixtures/model/');
var mhModel = createModel([
'properties',
'multiple-inherited-properties'
]);

describe('descriptor', function() {

it('should provide type', function() {

// when
var Type = mhModel.getType('mh:MultipleInherited');
var descriptor = model.getElementDescriptor(Type);

// then
expect(Type).to.exist;
expect(descriptor).to.exist;
expect(descriptor.propertiesByName.any).to.exist;
expect(descriptor.propertiesByName['mh:any']).to.exist;
expect(descriptor.propertiesByName['props:any']).to.exist;
});

});

describe('instance', function() {

it('should create instance', function() {

// when
var instance = mhModel.create('mh:MultipleInherited');

// then
expect(instance).to.exist;
});

describe('get', function() {

it('access via original name', function() {

// given
var instance = mhModel.create('mh:MultipleInherited');

// when
var property = instance.get('any');

// then
expect(property).to.exist;
});


it('access via local name', function() {

// given
var instance = mhModel.create('mh:MultipleInherited');

// when
var property = instance.get('mh:any');

// then
expect(property).to.exist;
});


it('access via other name', function() {

// given
var instance = mhModel.create('mh:MultipleInherited');

// when
var property = instance.get('props:any');

// then
expect(property).to.exist;
});

}); // describe(multiple inherited properties/instance/get)


describe('set', function() {

it('via original name', function() {

// given
var instance = mhModel.create('mh:MultipleInherited');

// when
instance.set('any', [ 'test' ]);
var originalProperty = instance.get('any');
var localProperty = instance.get('mh:any');
var otherProperty = instance.get('props:any');

// then
expect(originalProperty.length).to.equal(1);
expect(localProperty.length).to.equal(1);
expect(otherProperty.length).to.equal(0);
});

it('via local name', function() {

// when
var instance = mhModel.create('mh:MultipleInherited');
instance.set('mh:any', [ 'test' ]);
var originalProperty = instance.get('any');
var localProperty = instance.get('mh:any');
var otherProperty = instance.get('props:any');

// then
expect(originalProperty.length).to.equal(1);
expect(localProperty.length).to.equal(1);
expect(otherProperty.length).to.equal(0);
});


it('via other name', function() {

// when
var instance = mhModel.create('mh:MultipleInherited');
instance.set('props:any', [ 'test' ]);
var originalProperty = instance.get('any');
var localProperty = instance.get('mh:any');
var otherProperty = instance.get('props:any');

// then
expect(originalProperty.length).to.equal(0);
expect(localProperty.length).to.equal(0);
expect(otherProperty.length).to.equal(1);
});

}); // describe(multiple inherited properties/instance/set)

}); // describe(multiple inherited properties/instance)

}); // describe(multiple inherited properties)

});