Is there a way to set a default prototype?
sylvainpolletvillard opened this issue · 8 comments
Is there a way to set a default prototype? The following doesn't seem to work.
const {EventEmitter} = require('events')
const Crypto = ObjectModel({
name: String
}).defaultTo(EventEmitter.prototype)
I'm setting the prototype as follows but it would be nice to have a default.
Object.setPrototypeOf(
Crypto({
name: 'Tezos'
})
, EventEmitter.prototype
)
Originally posted by @RichAyotte in #93 (comment)
It looks like base is hardcoded to Object, if I'm reading this correctly.
ObjectModel/src/object-model.js
Line 422 in 1c23c0f
Is setting the prototype a feature that you'd consider?
So, to make things clear and avoid confusion:
- model instances inherit from the model prototype, that's why you put methods and other stuff in the
prototype
property of the model constructor, i.e.Crypto.prototype
- with this prototype link, you get
Crypto({ name: "Tezos" }) instanceof Crypto === true
- if you reassign completely the prototype with
Object.setPrototypeOf
, you lose this link. This can cause some issues, for example if you have defined other stuff inObjectModel.prototype
orModel.prototype
- the
defaultTo
is to used to set a default value to pass to the constructor when it is called without arguments. It does not change the model prototype and will not be used if a value is specified in the model constructor.
So the best option I think for your usecase is to use the extend
method:
const Crypto = ObjectModel({ name: String }).extend(EventEmitter)
Basically it is equivalent to Object.assign(Crypto.prototype, EventEmitter.prototype)
but let you override the model prototype afterwards if needed.
That definitely works. Not sure how I missed the extend method. The prototype remains Model but mixed in with EventEmitter prototype which is fine. Thank you for the detailed and quick response.
No problem, glad to help
@sylvainpolletvillard I spoke too fast. As soon as I tried to listen for an event, I got a TypeError. Here's the MWE.
'use strict'
const {EventEmitter} = require('events')
const {ObjectModel} = require('objectmodel')
const Bar = ObjectModel({
name: String
}).extend(EventEmitter)
const bar = Bar({
name: 'Foo'
})
bar.on('data', console.log)
bar.emit('data', 'hello world')
Error:
/node_modules/objectmodel/dist/object-model.js:102
isModelInstance = i => i && is(Model, getProto(i).constructor),
^
TypeError: Cannot read property 'constructor' of null
at isModelInstance (/node_modules/objectmodel/dist/object-model.js:102:52)
at cast (/node_modules/objectmodel/dist/object-model.js:256:69)
at getProxy (/node_modules/objectmodel/dist/object-model.js:288:36)
at newPath (/node_modules/objectmodel/dist/object-model.js:332:38)
at controlMutation (/node_modules/objectmodel/dist/object-model.js:226:5)
at Object.set (/node_modules/objectmodel/dist/object-model.js:331:13)
at _addListener (events.js:217:29)
at Proxy.addListener (events.js:271:10)
at Object.proxifyFn [as apply] (/node_modules/objectmodel/dist/object-model.js:292:26)
at Object.<anonymous> (/src/ee-test.js:24:5)
Maybe you can shed some light on the importance of this check?
ObjectModel/src/object-model.js
Line 50 in 1c23c0f
Would the following be a safe fix or would it potentially cause more problems?
isModelInstance = i => i && getProto(i) && is(Model, getProto(i).constructor),
Ah, right, this lib creates objects with no prototypes (https://github.com/Gozala/events/blob/master/events.js#L86 )
Your suggested fix looks perfect, let me add some tests and release a new patch version
Just released v3.7.7, can you confirm it fixes your issue ?
v3.7.7 works well. Thanks again!