sylvainpolletvillard / ObjectModel

Strong Dynamically Typed Object Modeling for JavaScript
http://objectmodel.js.org
MIT License
467 stars 28 forks source link

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

Closed oleg-l closed 8 years ago

oleg-l commented 8 years ago

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(…)
sylvainpolletvillard commented 8 years ago

The object passed to model A is not an instance of M, it is validated against M definition through duck typing, so does not inherit from M defaults.

Currently, Array models items are not automatically casted to model instances based on the Array model definition. So the current way to do this would be new A([ M({ n : 1, s : 'aaa' }) ]);

I think it could make sense to add automatic model casting here too. It is a similar issue to https://github.com/sylvainpolletvillard/ObjectModel/issues/24 . To make things clear, I guess I should bring automatic model casting to every kind of Model, and use the same strategy to find the suitable model and warn when ambiguous.

However this change will imply that A([x])[0] !== x which can be odd in some scenarios. This is the limit of duck typing: approximation can lead to confusion.

oleg-l commented 8 years ago

Ok, I see. Don't you think, it would be useful to add user-defined constructor argument casting? Something like this:

var M = new Model({
    s : String
}).cast({
    s : function(value) { return value.toString() }
});
var m = new M({ s : new Date() }); // Ok, s was converted to string

But this idea is for another issue, I think

sylvainpolletvillard commented 8 years ago

Yes, property casting is a different issue and I would recommend using a factory function for this, instead of the Model constructor. See How do I declare a constructor function to be called on instanciation before validating my model ? in http://objectmodel.js.org/#common-questions

oleg-l commented 8 years ago

Yes, I do it that way. But factories make model definition longer. I like your 'defaults({...})' style. Casting that way would make code more compact. Thank you! Closing issue.

sylvainpolletvillard commented 8 years ago

Every library is a tradeoff between user code size and API size. I think factory functions are the best compromise, since they are very flexible and straight-forward to use. But it should be relatively easy to extend the library the way you want and add a Model.prototype.cast function.

sylvainpolletvillard commented 8 years ago

Automatic model casting will be available in v2.5 for array items and function arguments and return value

sylvainpolletvillard commented 8 years ago

Your code example is now valid since v2.5.2 👍

oleg-l commented 8 years ago

Super!