Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add strict mode #48

Merged
merged 3 commits into from
Dec 20, 2022
Merged
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
6 changes: 5 additions & 1 deletion lib/moddle.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,19 @@ import {
* var moddle = new Moddle([pkg]);
*
* @param {Array<Package>} packages the packages to contain
*
* @param { { strict?: boolean } } [config] moddle configuration
nikku marked this conversation as resolved.
Show resolved Hide resolved
*/
export default function Moddle(packages) {
export default function Moddle(packages, config = {}) {

this.properties = new Properties(this);

this.factory = new Factory(this, this.properties);
this.registry = new Registry(packages, this.properties);

this.typeCache = {};

this.config = config;
}


Expand Down
52 changes: 47 additions & 5 deletions lib/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Properties.prototype.set = function(target, name, value) {
throw new TypeError('property name must be a non-empty string');
}

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

var propertyName = property && property.name;

Expand All @@ -38,7 +38,7 @@ Properties.prototype.set = function(target, name, value) {
if (property) {
delete target[propertyName];
} else {
delete target.$attrs[name];
delete target.$attrs[stripGlobal(name)];
}
} else {

Expand All @@ -51,7 +51,7 @@ Properties.prototype.set = function(target, name, value) {
defineProperty(target, property, value);
}
} else {
target.$attrs[name] = value;
target.$attrs[stripGlobal(name)] = value;
}
}
};
Expand All @@ -66,10 +66,10 @@ Properties.prototype.set = function(target, name, value) {
*/
Properties.prototype.get = function(target, name) {

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

if (!property) {
return target.$attrs[name];
return target.$attrs[stripGlobal(name)];
}

var propertyName = property.name;
Expand Down Expand Up @@ -123,6 +123,44 @@ Properties.prototype.defineModel = function(target, model) {
this.define(target, '$model', { value: model });
};

/**
* Return property with the given name on the element.
*
* @param {any} target
* @param {string} name
*
* @return {object | null} property
*/
Properties.prototype.getProperty = function(target, name) {

var model = this.model;

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

if (property) {
return property;
}

if (name.includes(':')) {
return null;
}

const strict = model.config.strict;

if (typeof strict !== 'undefined') {
const error = new TypeError(`unknown property <${ name }> on <${ target.$type }>`);

if (strict) {
throw error;
} else {

// eslint-disable-next-line no-undef
typeof console !== 'undefined' && console.warn(error);
}
}

return null;
};

function isUndefined(val) {
return typeof val === 'undefined';
Expand All @@ -135,4 +173,8 @@ function defineProperty(target, property, value) {
value: value,
configurable: true
});
}

function stripGlobal(name) {
return name.replace(/^:/, '');
}
4 changes: 2 additions & 2 deletions test/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function createModelBuilder(base) {
throw new Error('[test-util] must specify a base directory');
}

function createModel(packageNames) {
function createModel(packageNames, config = {}) {

var packages = map(packageNames, function(f) {
var pkg = cache[f];
Expand All @@ -38,7 +38,7 @@ export function createModelBuilder(base) {
return pkg;
});

return new Moddle(packages);
return new Moddle(packages, config);
}

return createModel;
Expand Down
143 changes: 143 additions & 0 deletions test/spec/moddle.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,38 @@ describe('moddle', function() {
expect(element.get('props:count')).to.eql(10);
});


it('should access global name', function() {

// when
const element = moddle.create('props:ComplexCount', {
':xmlns': 'http://foo'
});

// then
expect(element.get(':xmlns')).to.eql('http://foo');
nikku marked this conversation as resolved.
Show resolved Hide resolved
expect(element.get('xmlns')).to.eql('http://foo');

// available as extension attribute
expect(element.$attrs).to.have.property('xmlns');
});


it('should access global name (no prefix)', function() {

// when
const element = moddle.create('props:ComplexCount', {
'xmlns': 'http://foo'
});

// then
expect(element.get(':xmlns')).to.eql('http://foo');
expect(element.get('xmlns')).to.eql('http://foo');

// available as extension attribute
expect(element.$attrs).to.have.property('xmlns');
});

});


Expand All @@ -376,4 +408,115 @@ describe('moddle', function() {

});


describe('property access (lax)', function() {

const moddle = createModel([
'properties'
], {
strict: false
});


describe('typed', function() {

it('should access unknown attribute', function() {

// when
const element = moddle.create('props:ComplexCount', {
foo: 'bar'
});

// then
expect(element.get('foo')).to.eql('bar');
});

});

});


describe('property access (strict)', function() {

const moddle = createModel([
'properties'
], {
strict: true
});


it('should configure in strict mode', function() {

// then
expect(moddle.config.strict).to.be.true;
});


describe('typed', function() {

it('should access basic', function() {

// when
const element = moddle.create('props:ComplexCount', {
count: 10
});

// then
expect(element.get('count')).to.eql(10);
expect(element.get('props:count')).to.eql(10);

// available under base name
expect(element.count).to.exist;
});


it('should access global name', function() {

// when
const element = moddle.create('props:ComplexCount', {
':xmlns': 'http://foo'
});

// then
expect(element.get(':xmlns')).to.eql('http://foo');

expect(() => {
element.get('xmlns');
}).to.throw(/unknown property <xmlns> on <props:ComplexCount>/);

// available as extension attribute
nikku marked this conversation as resolved.
Show resolved Hide resolved
expect(element.$attrs).to.have.property('xmlns');
});


it('fail accessing unknown property', function() {

// when
const element = moddle.create('props:ComplexCount');

// then
expect(() => {
element.get('foo');
}).to.throw(/unknown property <foo> on <props:ComplexCount>/);

expect(() => {
element.set('foo', 10);
}).to.throw(/unknown property <foo> on <props:ComplexCount>/);
});


it('fail instantiating with unknown property', function() {

// then
expect(() => {
moddle.create('props:ComplexCount', {
foo: 10
});
}).to.throw(/unknown property <foo> on <props:ComplexCount>/);
});

});

});

});