Giter VIP home page Giter VIP logo

objectmodel's Introduction

Strong Dynamically Typed Object Modeling for JavaScript

Downloads Version Status License


What is this library ?

ObjectModel intends to bring strong dynamic type checking to your web applications. Contrary to static type-checking solutions like TypeScript or Flow, ObjectModel can also validate data at runtime: JSON from the server, form inputs, content from localStorage, external libraries...

By leveraging ES6 Proxies, this library ensures that your variables always match the model definition and validation constraints you added to them. Thanks to the generated exceptions, it will help you spot potential bugs and save you time spent on debugging. ObjectModel is also very easy to master: no new language to learn, no new tools, no compilation step, just a minimalist and intuitive API in a plain old JS micro-library.

Validating at runtime also brings many other benefits: you can define your own types, use them in complex model definitions with custom assertions that can even change depending on your application state. Actually it goes much further than just type safety. Go on and see for yourself.

Installation

Add the library to your project dependencies with NPM:

$ npm install objectmodel

or just download the library from Github

Basic usage example

import { ObjectModel } from "objectmodel"

const Order = new ObjectModel({
	product: { name: String, quantity: Number },
	orderDate: Date
});

const myOrder = new Order({
	product: { name: "Apple Pie", quantity: 1 },
	orderDate: new Date()
});

myOrder.product.quantity = 2; // no exceptions thrown
myOrder.product.quantity = false; //try to assign a Boolean
// โŒ TypeError: expecting product.quantity to be Number, got Boolean false

Documentation

For more examples, documentation and questions, please refer to the project website: objectmodel.js.org

Changelog and Release History

Please refer to Github Releases

Bug reports and pull requests are welcome.

Distributed under the MIT license. See LICENSE for more information.

objectmodel's People

Contributors

crobinson42 avatar dependabot[bot] avatar doodzik avatar gitter-badger avatar gotjoshua avatar streeter avatar sylvainpolletvillard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

objectmodel's Issues

package.json module entry should points to compiled sources

ERROR in vendor.f3ca509cb6487d3d6fd2.js from UglifyJs
Unexpected token: operator (>) [vendor.f3ca509cb6487d3d6fd2.js:111600,27]
/***/ }),

/***/ "./node_modules/objectmodel/dist/object-model.js":
/***/ (function(module, exports, __webpack_require__) {

/*!
 * Objectmodel v3.1.0
 * http://objectmodel.js.org
 *
 * Copyright (c) 2017 Sylvain Pollet-Villard
 * Licensed under the MIT license
 */

(function (global, factory) {
	 true ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(factory((global.window = global.window || {})));
}(this, (function (exports) { 'use strict';

const _constructor = "_constructor";
const _validate = "_validate";
const ObjectProto     = Object.prototype;
const bettertypeof    = x => ObjectProto.toString.call(x).match(/\s([a-zA-Z]+)/)[1];

It is problem with module entry. It should point to compiled es6 sources.
jsforum/jsforum#5
https://github.com/rollup/rollup/wiki/pkg.module

Similar issues:
shuhei/material-colors#13
Hypercubed/mini-signals#16
vesparny/brcast#7

This problem loves me ๐Ÿ˜†

Getting property types from a model instance

How can I get the property type of a model instance attribute? Using the example snippet below

var User = new Model({
      name: String,
      female: [Boolean],
      birth: Date,
      choices: ['a', 'b', 'c']
   });

    var joe = new User({
      name: "Joe",
      birth: new Date(),
      choices: 'c'
   });

How can I get the attribute type of name from the instance "joe"? Using typeof I get a type of "function" which is actually not correct. Thanks

Model.Array does not work inside Model

As expected:

var Cards = Model.Array([Number, "J","Q","K"]);
var a = new Cards([2]);
a.push(1);
a.push(false); // ERROR
console.log(a);

Not as expected:

var Container = Model({
    items: Model.Array([Number])
});

var a = new Container({items: []});
a.items.push(1);
a.items.push(false); // OK?
console.log(JSON.stringify(a));

Constructors and Inheritance for Models

Trying to build an Active Record like layer on top of ObjectModel, but it isn't working out so well. This is the first of several issues I have run into. I'm circumventing them in various ways, but ideally the core would be better suited to these sorts of behaviors.

The constructors used by ObjectModel are highly irregular, and there doesn't seem to be a good way to do basic inheitance using common patterns. Using Model.extend() also gives goofy results. I finally had to fall back to using Model as just a validation utility.

Basically the problem is that traditional patterns don't work for the following (also couldn't get Model.extend() to give working results):

var ActiveRecord = Model({});

// How do I set a constructor for ActiveRecord?

var People = // How do I properly inherit ActiveRecord, and also extend the constructor

Is there a way to stop extra keys in the Object model?

I was wondering if there is an easy way to prevent extra keys in the object:

This example should throw an error that "two" is not defined in the model.
var test = Model({one:String})
test({one: "one", two: "two"})

Thank you very much in advance and apologies if I missed this in the documentation.

Custom error collectors catching all errors and not just the first one

The current validators always throw on the first failure and give poor access to error information. Validators also provide no human readability options.

  • Asserts need an optional human readable description. The easiest way to do this seems to be with with an Assert class, similar to the Model class, that can be used to define an assertion callback, as well as a human readable description.
  • Types need an optional human readable description. Current readability is only tolerable for simple types, but not much help when you start stacking on assertions. Under the current strategy where only the first error is thrown (and where that is the assert, in assertion failure) this need is somewhat hidden, but it becomes more apparent once error information is collected in a more meaningful way. I anticipate working around this in my code by adding a Model.describeModel() method (or something similar), and overriding toString().
  • checkDefinition should be refactored to collect errors rather than throwing a single error. This is very important for complex models, or situations where you care about all errors, not just the first. These changes could then be exposed either in validate, or via a new method. There are a few ways this can be done. If you want I can send you what I put together, which is highly flexible.

Array as an object when running in browser

The following code works OK in node, but gives unexpected result when running in browser:

var Model = require('objectmodel');

// Default is [1, 0, 0, 1, 0, 0]
var Matrix = Model.Array(Number);
    //.assert(v => v.length == 6);

Matrix.prototype.toString = function () {
    var [a, b, c, d, e, f] = this;
    return `matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
};

var Shape = Model({
    matrix: Matrix,
    path: String
}).defaults({matrix: new Matrix([1, 0, 0, 1, 0, 0])});

var Picture2 = Model({
    matrix: [Matrix],
    shapes: Model.Array(Shape)
}).defaults({matrix: undefined, shapes: []});

var picture = new Picture2({
    shapes: [
        new Shape({
            path: ''
        }),
        new Shape({
            path: ''
        }),
        new Shape({
            path: ''
        }),
        new Shape({
            path: ''
        })
    ]
});

console.log(JSON.parse(JSON.stringify(picture)));

Instead of this:

{shapes: [..., ..., ..., ...]}

I've got this:

{shapes: {0: ..., 1: ..., 2: ...}}

database _pk is handled as private

I have a naming collision. My database (ArangoDb) only accepts _key as primary key.
When I create a record offline I have to add {_key : uuid} in order to sync with the db when coming online again. if _key is missing in the record, the db will create it's own _key.

The problem is that ObjectModel sees _key as private and strips it when the record is sent to the db server.
How can I solve this?

Cannot read property 'length' of undefined

$ node --version
v6.5.0
$ cat node_modules/objectmodel/package.json | grep version
"version": "2.2.0"
// Default is [1, 0, 0, 1, 0, 0]
var Matrix = Model.Array(Number).assert(v => v.length == 6);

Matrix.prototype.toString = function () {
    var [a, b, c, d, e, f] = this;
    return `matrix(${a} ${b} ${c} ${d} ${e} ${f})`;
};

var Shape = Model({
    matrix: Matrix,
    path: String
}).defaults({matrix: new Matrix([1, 0, 0, 1, 0, 0])});

new Shape({path: ''}); // OK

var Picture2 = Model({
    matrix: [Matrix],
    shapes: Model.Array(Shape)
}).defaults({shapes: []});

new Picture2(); // Error: Cannot read property 'length' of undefined

Automatic model casting with union types

After assignment a plain object to a dynamic property that property becomes typeless (in the example below I expect to get User instance but got an Object):

var Model = require('objectmodel');

var User = new Model({username: String, email: String})
    .defaults({username: 'foo', email: 'foo@foo'});
var Article = new Model({title: String, user: [User]})
    .defaults({title: 'foo', user: new User()});

var a = new Article();
console.log(a.user instanceof User); // true
// a.user.email = 1; // TypeError: expecting email to be String, got Number 1
a.user = {username: 'foo', email: 'foo'};
console.log(a.user instanceof User); // false
a.user.email = 1; // OK
a.user.foobarbaz = 123; // OK
console.log(a.user);

Constructor should ignore unknown properties

Properties not defined in the model should not be set on the instance โ€” but, as this test shows, they are still added to the properties.

const test = require('ava');
const Model = require('objectmodel');

const Record = new Model({
    name: String
});

test('Constructor should ignore unknown properties', t => {
    const raw = {
        name: 'paul',
        surname: 'simon'
    };

    const record = new Record(raw);
    t.truthy(record.name);
    t.falsy(record.surname); // this test fails, surname = "simon" but it should not
});

Question Regarding static vs dynamic typing

I saw in your Description that you say These solutions bring static typing, which means it only validate your code at build time, not runtime. Once compiled in JavaScript and run in the browser, there is no longer any guarantee that the variables you are working with have the intended types. As I can see from TypeScript Docs the Advanced Types - User-Defined Type Guards are exactly the case to detect the correct type of a variable during runtime, isn't it?

API to retrieve errors

At the moment, I place my code in a try/catch block which then emits errors like this:

TypeError expecting id to be String, got undefined

This is okay but to help with the project I'm working on, I will need a better way to extract the exact field that caused the error. Parsing the error string is sub optimal.

Another thing. Does ObjectModel test all attributes before failing?
Thanks

Validation of sub-objects not working as expected

I am finding that ObjectModel ignores the validations of sub-objects in a model.

Take this example:

var Model = require("objectmodel");

function printErrors(errors) {
	console.log("Caught these errors:");
	errors.forEach(function(error){ console.dir(error); });
}

var Address = new Model({
	city: String,
	country: String
}).assert(function(a) { 
	console.log('validation check result: ' + (a.country === "GB"));
	return a.country === "GB";
}, "Country must be GB");

var gbAddress = { city: "London", country: "GB" };
var frAddress = { city: "Paris", country: "FR" };

Address.validate(gbAddress, printErrors); // no errors
Address.validate(frAddress, printErrors); // errors


var Order = new Model({ 
	sku: String,
	address: Address
});

var gbOrder = { sku: "ABC123", address: gbAddress };
var frOrder = { sku: "ABC123", address: frAddress };

Order.validate(gbOrder, printErrors); // no errors
Order.validate(frOrder, printErrors); // no errors, but SHOULD have errors?

Here's the output I'm getting:

running validation check: true
running validation check: false
Caught these errors:
{ message: 'assertion "Country must be GB" returned false for value {\n\tcity: "Paris",\n\tcountry: "FR"\n}' }
running validation check: true
running validation check: false

When you run this, you can see that validation check result: is output 4 times (with the correct result), which correlates with the 4 calls to .validate(...) however only the second call actually causes errors. It seems the Order.validate ignores the validation of the sub-object.

Is this expected behaviour, or a bug? If it is expected, is there a way to enforce an object validation validates all the child model as well?

Null vs undefined

parseDefinition interprets [Model] as [Model, undefined], which fails against null values. I can see the logic in this perhaps, but it was a surprise, and works very poorly on certain types of data (such as JSON consumed from APIs).

IMO parseDefinition should treat [Model] as [Model, null, undefined], since null is a popular variation of "not set", and the line between null and undefined is blurry at best.

Maybe it should be another option similar to the Model.conventionFor* options?

By default:
Model.conventionForNotSet = [undefined]

To be more flexible the developer would add this to their own code:
Model.conventionForNotSet = [null, undefined]

Thoughts?

ObjectModel defaults are not set when creating Model.Array(ObjectModel)

Example:

var M = new Model({
    n : Number,
    s : String,
    f : Model.Function().return(String)
}).defaults({
    f : function() { return 'bbb'; }
});

var m = new M({ n : 1, s : 'aaa' }); // Ok, function f() is set from defaults
console.log(m.n, m.s, m.f()); // Ok

var A = Model.Array(M);
var a = new A([{ n : 1, s : 'aaa' }]);
// throws Uncaught TypeError: expecting Array[0].f to be "Function", got undefined(โ€ฆ)

[question] How to set up composed key

Thank you for this absolutely AWESOME package! Exactly what I was looking for. Since 99% of (my) debugging always boils done to sending/receiving messed up params, args , objects etc. you can imagine what a time saver this is going to be.
Also excellent documentation which I admittedly haven't completely absorbed yet. So maybe this question is already answered somewhere in the docs.
I have a _key that defaults to a new uuid()
Now _id has to be composed like 'company/'+_key
How can I accomplish that?

createKey() {
return uuid()
}
new Model({
_key : String,
_id : String
...
}).defaults({
_key : createKey(),
_id : 'company/'+ _key ???
})

Changes on sub-models are prevented

Hi,

I think we found a bug. It happens when we try to modify a property in a sub-model, after the assignation, the new value doesn't get saved in the object and still printing the original value.

This example will reproduce the bug:

var Model = require("objectmodel");

var Extra = new Model({
    status: String
});

var Order = new Model({
    sku: String,
    extra: Extra
});

var input = { sku: "ABC123", extra: { status: 'bbbb' }};

const o = new Order(input);
o.extra.status = 'aaaa';

console.log(o.extra.status); // prints 'bbbb' instead of 'aaaa'

Thanks for your time!

Cannot compose sealed ObjectModels

I'm trying to define a nested object model, where one of the child objects is sealed. For example, this works fine (using v3.0.1):

const Dependency = ObjectModel({
  name: String,
});

const Package = ObjectModel({
  name: String,
  data: {
    description: String,
    hard_dependencies: {
      one: Dependency,
      two: Dependency,
    }
  }
});

const test_item = new Package({
  name: "Test item",
  data: {
    description: "A test item",
    hard_dependencies: {
    	one: {
      	name: "module 1",
        bad_attr: false,
      },
      two: {
      	name: "module 2",
      }
    }
  }
});

But I get an error when I change the Dependency definition to this:

const Dependency = ObjectModel({
  name: String,
}).sealed=true;

The error is:

Uncaught TypeError: expecting data.hard_dependencies.one to be true, got Object { name: "module 1", nope: false } expecting data.hard_dependencies.two to be true, got Object { name: "module 2" } at window.onload (VM180:72)

This seems to be happening because Dependency now gets the value of the assignment from sealed=true, rather than the function that it should be.

I've tried:

var Dependency = ObjectModel({
  name: String,
});
Dependency.sealed=true;

but it has no effect.

Could you provide an example on how to use the sealed property?

I can't find a npm module for this library

hey there,

is there a npm module available for this library and if not can you publish it?
I would love to use your library without the need to download the dist folder.

Missing error details in nested sub-models validations

Hi,

Following #37

We miss extra information when an error happen inside a sub-model validation.

Given the example:

var Model = require("objectmodel");

function printErrors(errors) {
    console.log("Caught these errors:");
    errors.forEach(function(error){ console.log(error); });
}

var CountryCode = new Model(String)
.assert(function (a) {
    return a === "GB";
}, "Country must be GB");

var Address = new Model({
    city: String,
    address: String,
    country: CountryCode
});

var Order = new Model({
    sku: String,
    addresses: Model.Array(Address)
});

// Examples
var gbAddress = { city: "London", address: "123 Fake St", country: "GB"}; // valid address
var frAddress = { city: "Paris", address: 0, country: "FR" }; // invalid address and country

var gbOrder = { sku: "ABC123", addresses: [gbAddress] };
var frOrder = { sku: "ABC123", addresses: [frAddress] };

Order.validate(gbOrder, printErrors); // no errors
Order.validate(frOrder, printErrors); // throw errors

The actual console output is:

Caught these errors:
{ expected: [Function: String],
  received: 0,
  path: 'addresses[0].address',
  message: 'expecting addresses[0].address to be String, got Number 0' }

{ message: 'assertion "Country must be GB" returned false for value "FR"' }

The expected output would be something like:

Caught these errors:
{ expected: [Function: String],
  received: 0,
  path: 'addresses[0].address',
  message: 'expecting addresses[0].address to be String, got Number 0' }

{ expected: [Function: String],
  received: 'FR',
  path: 'addresses[0].country',
  message: 'Country must be GB' }

That will give us the ability to detect the exact path of an error.

Thanks!

async usage of Model.Array

Hello, I am trying to return instance of Model.Array asynctonius via callback or Promise in node.js.
Sadly, i can not do that, and there is no error written by node. I can return normal models, but can not return Array model. Is that bug?

Compatibility break on 'hasOwnProperty' usage

The current version (3.1) introduced compatibility break for 'hasOwnProperty' method usage.

Code snippet to reproduce:

const { Model } = require('objectmodel');

class Foo extends Model({
  id: Number,
  status: String,
}).defaults({
  status: 'pending',
}) { }

const foo = new Foo({ id: 1 }); // init only with id

console.log(JSON.stringify(foo)); // {"id":1,"status":"pending"}
console.log(foo.hasOwnProperty('id'));  // true
console.log(foo.hasOwnProperty('status'));  // false  < ------ false even if default is specified - v2.6.2 returns true

Models and assertions should have a human readable description

from #11

Asserts need an optional human readable description. The easiest way to do this seems to be with with an Assert class, similar to the Model class, that can be used to define an assertion callback, as well as a human readable description.
Types need an optional human readable description. Current readability is only tolerable for simple types, but not much help when you start stacking on assertions. Under the current strategy where only the first error is thrown (and where that is the assert, in assertion failure) this need is somewhat hidden, but it becomes more apparent once error information is collected in a more meaningful way. I anticipate working around this in my code by adding a Model.describeModel() method (or something similar), and overriding toString().

Defaults for primitive values and arrays

I think it will be more consistent if not only object models will have defaults but Strings, Numbers, Booleans, and Arrays too. Currently I can't write something like that:

var Container = Model.Array([Number, String]);
var a = Container();

nor:

var Container = Model.Array([Number, String]).defaults([]);

npm install objectmodel failed

$ npm install objectmodel
npm http 304 https://registry.npmjs.org/abbrev
npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules/grunt-cli
npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules
/usr/local/lib
โ””โ”€โ”€ [email protected] 

npm ERR! Linux 3.16.0-4-amd64
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "install" "-g" "grunt-cli"
npm ERR! node v6.3.1
npm ERR! npm  v3.10.3
npm ERR! path /usr/local/lib/node_modules/grunt-cli
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access

npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules/grunt-cli'
npm ERR!     at Error (native)
npm ERR!  { Error: EACCES: permission denied, access '/usr/local/lib/node_modules/grunt-cli'
npm ERR!     at Error (native)
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/local/lib/node_modules/grunt-cli' }
npm ERR! 
npm ERR! Please try running this command again as root/Administrator.

I think that the problem lies in grunt-cli dependency. Probably it should not be specified at all.

Requires root for no reason

Currently the post install script does a global install (npm -g) of grunt, which would require me to do a sudo npm install of objectmodel. I don't believe a global install should be required for something that is supposed to be used as a dev dependency. Why not just include grunt as a dependency?

For now just going to pull it from repo.

Serialization and deserialization arrays of objects

Suppose I have a container for different types. How can I save this container into JSON and then restore it, so, that its values will be of the same type as it was before serialization?

var Model = require('objectmodel');

var Type1 = Model({
    title: String,
    contents: String
}).defaults({title: 'title 1', contents: 'contents 1'});

var Type2 = Model({
    title: String,
    contents: String
}).defaults({title: 'title 2', contents: 'contents 2'});

var Container = Model({
    items: Model.Array(Type1, Type2)
}).defaults({items: []});

var a = new Container();

a.items.push(new Type1, new Type2);

console.log(JSON.stringify(a));

In the example above I intentionally made two types containing the same fields. Duck typing cannot work here. But, what is the solution?

Is it possible to validate without throwing an error?

I'd like to have something link an isValid() method instead of throwning an error right from the constructor, for example:

const NumberModel = Model(Number);
const number = new NumberModel('42'); // do not throw TypeError
number.isValid(); // returns false

Is is achievable with this lib?

Btw, nice lib!

Array Model Serialization in Safari Returns Object

<script type="text/javascript" src="model.js"></script>
<script>
var list = Model.Array(String);
var result = JSON.stringify(new list(['one', 'two']));
console.log(result);

// chrome
// ["one","two"]

// safari 
// "{\"0\":\"one\",\"1\":\"two\"}"

</script>

I would expect safari to serialize an ObjectModel the same way as Chrome.

Feature request: add parent and root reference to assert function arguments

Hi,

This is a nice feature that will be nice to have to improve our assertions.

The use case is when a validation needs context from a parent model.

Currently, the only available argument in a assert function is the model itself. But having the ability to access the parent and the root object will allow us to create more precise error messages (having the ability of create an validation error in the right path of the object and not in the parent/root).

Basic example of how it would work:

var Player = new Model({
    name: String,
    salary: Number
});

var Team = new Model({
    name: String,
    titles: Number,
    players: Model.Array(Player)
});

var Season = new Model({
    year: Number,
    teams: Model.Array(Team)
});

/**
 * @var player current model
 * @var parent Team model
 * @var root Season model
 */
Player.assert(function hasTeamRightSalary(player, parent, root) {
  if (parent.titles > 10) {
      return player.salary > 100000;
  }

  return true;
});

Player.assert(function hasSeasonRightSalary(player, parent, root) {
  if (root.year > 2016) {
      return player.salary < 200000
  }

  return true;
});

What are your thoughts about it?

Thanks!

Usage of extend to only inherit assertions

Hi,

We are trying to use the extend operation to encapsulate some validation logic into helper classes, but we are also finding some issues with the errors reported.

We tried to different approaches:

  • Case 1:
    We miss the root error and we get the parent element instead of the invalid property.

  • Case 2:
    We get the root error, but we miss the path and the extra info about what was wrong.

Here an example:

var debug = require('debug')('log');
var Model = require("objectmodel");

function printErrors(errors) {
    console.log("Caught these errors:");
    errors.forEach(function(error){ debug(error); });
}

function assertMandatoryCollection(collection) {
    return collection && Array.isArray(collection) && collection.length > 0
}

var MandatoryCollection = new Model.Array()
.assert(assertMandatoryCollection, 'MandatoryCollection should have at least one child');

var CountryCode = new Model(String)
.assert(function (a) {
    return a === "GB";
}, "Country must be GB");

var Carrier = Model.Object({
    code: [String],
    service: [String],
    alias: [String]
})
.assert(function (c) {
    return !(!c.alias && (!c.code || !c.service));
}, 'Should indicate code and service or indicate an alias');

var Address = new Model({
    city: String,
    country: CountryCode,
    carrier: Carrier
});

// Case 1, using extend()
var addressesExtended = Model.Array(Address)
.extend(MandatoryCollection);

var Order1 = new Model({
    sku: String,
    addresses: addressesExtended
});

// Case 2, using assert()
var addressesAsserted = Model.Array(Address)
.assert(assertMandatoryCollection, 'Addresses should have at least one child');

var Order2 = new Model({
    sku: String,
    addresses: addressesAsserted
});

// Examples
var gbAddress = { city: "London", country: "GB", carrier: {alias: "alias"} }; // valid address
var frAddress = { city: "Paris", country: "FR", carrier: {code: "code"} }; // invalid carrier, missing service property

var gbOrder = { sku: "ABC123", addresses: [gbAddress] };
var frOrder = { sku: "ABC123", addresses: [frAddress] };

Order1.validate(gbOrder, printErrors); // no errors
Order1.validate(frOrder, printErrors); // error shows parent element

Order2.validate(gbOrder, printErrors); // no errors
Order2.validate(frOrder, printErrors); // missing errors path

And this is the console output:

Caught these errors:
  log { expected: 
  log    [ { [Function]
  log        definition: [Object],
  log        assertions: [],
  log        errorCollector: [Function] },
  log      { [Function]
  log        definition: undefined,
  log        assertions: [Object],
  log        errorCollector: [Function] } ],
  log   received: { city: 'Paris', country: 'FR', carrier: { code: 'code' } },
  log   path: 'addresses[0]',
  log   message: 'expecting addresses[0] to be {\n\tcity: String,\n\tcountry: String,\n\tcarrier: {\n\t\tcode: [String],\n\t\tservice: [String],\n\t\talias: [String]\n\t}\n} or Array of undefined, got Object {\n\tcity: "Paris",\n\tcountry: "FR",\n\tcarrier: {\n\t\tcode: "code"\n\t}\n}' } +0ms
Caught these errors:
  log { message: 'assertion "Country must be GB" returned false for value "FR"' } +6ms
  log { message: 'assertion "Should indicate code and service or indicate an alias" returned false for value {\n\tcode: "code"\n}' } +1ms

Which is the right way to do this inheritance?

Are we missing something?

Thanks!

Ability to generate defaults using a function

Hi!

Would it be possible to have a way to define defaults using a function?

Ex:

var FileInfo = Model({
	name: String,
	created: [Date, Number]
}).defaults({
	name: "Untitled file",
	created: Date.now
});

// will execute the function Date.now after a new FileInfo({}) 
// and copy the returned value instead

Currently it throws an error because it found a function instead of a Number:
TypeError: expecting created to be Date or Number, got Function now

Thanks!

ES6 Example

Hi,

can you give me a full ES6 example with imports and typings?

Thx

Selcuk

Problem with extended Definitions

What I get from your Docs:

for object models, the resulting definition is the deep (recursive) merge of the definition objects

Now if I do:

var Model = require("objectmodel");

var Address = new Model({
  address: {
    company: [String],
    name: String,
    street: String,
    zip: String,
    city: String,
    country: String
  }
});

var Customer = Address.extend({
  id: String
});

Customer.create = function(properties) {
  return new Customer(properties);
};

var obj = Customer.create({<Object>});
console.dir(obj.constructor.definition);

It prints out:

{ address: 
   { company: [ [Function: String] ],
     name: [Function: String],
     street: [Function: String],
     zip: [Function: String],
     city: [Function: String],
     country: [Function: String] 
  }
}

For me id is missing here in the definition as I understand from docs or did I get sth. wrong?

Static validate method does not apply defaults

Hi!

We found another issue. You can create an object with defaults and those will apply to the object when you create the new instance and then it will validate the object. But using the static validate method does not preserve the same behavior.

Example:

var Extra = new Model({
    status: String,
    amount: Number
}).defaults({
    amount: 0
});

var input1 = { status: 'ready'};
var input2 = { status: 'stopped'};

var extra = new Extra(input1); // valid object
console.log('Valid input1'); // print it to console

Extra.validate(input2); // throws invalid amount error
console.log('Valid input2'); // never gets here

We think validate should be aware of the defaults values before it perform the assertions. So both cases static and new instance should return the same result (valid or invalid).

Thanks!

function model - allowing Object

Hi,
if ill using Object as function argument model, it will throw an error if ill use any object like number or string as argument. so it ust be possible to allow any type in the same modeling syntax or like Model.Primitive

TypeScript integration

Hey, I've just found out your project and I find it extremely interesting ๐Ÿ‘ Are there any plans for typings for TypeScript? I use it exclusively when I write js code, because I find type checks very helpful for my productivity.

Predefined assertions

Maybe I missed sth but I'm wondering why common assertions like min, max, range etc. or especially assertion checking undeclared properties are not implemented by default by this library?
Why do I need to write custom assertion in order to check whether some additional properties were provided rather than just setting single option e.g. "additionalProperties: false". When I want to use this lib in several projects I would need to write all assertion again or include them from some dedicated repo to every project. IMHO it's an overkill and if it won't be supported then I will need to find some other lib.

Btw, when I add a custom assertion, for instance: Model(Number).assert(value => value >= 0 && value <= 100, 'Some msg') and asserting some deep property, let's say foo.bar.baz, I can't see property path in the error msg. What I get instead is: `TypeError: assertion "'Some msg" returned false for value 2000. For simple expectations like String I get beautiful path: 'expecting foo.bar.baz to be String, got undefined'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.