jagi / meteor-astronomy

Model layer for Meteor
https://atmospherejs.com/jagi/astronomy
MIT License
604 stars 67 forks source link

Transform parent class instance into a child one #667

Open davidsavoie1 opened 6 years ago

davidsavoie1 commented 6 years ago

Hi!

If I create an instance of a Parent class, how can I make a copy of it then save it as a Child class instance that inherits from Parent? Do I only change the type property?

Thanks!

davidsavoie1 commented 6 years ago

Well, it seems that only changing the type property won't cut it! I've tried with this and it seems to be working...

const child = new Child(parent.copy().raw());

Is there a better way you can think of that I'm missing here?

Many thanks again for your awesome library!

davidsavoie1 commented 6 years ago

Here's another thing I noticed... If I use a publication to restrict some of the fields of the Child class, as in this example:

Meteor.publish('Child.all.descriptions', function () {
  return Child.find({}, { fields: { code: 1, desc: 1, client: 1, state: 1 } });
});

If I don't include the type field in the fields selector, the data doesn't seem to get to the client, as if Astronomy didn't have enough information to figure out which class I'm asking for. If I do publish without fields selector then no problem. Is it expected behavior?

Do I only have to include the type field and be done with it?

lukejagodzinski commented 6 years ago
const child = new Child(parent.raw());

should be just enough to create a child that has set all its fields set to values from the parent.

If it goes about publication, I should probably fix that to automatically publish the type field even if a user does not provide it.

davidsavoie1 commented 6 years ago

OK thanks! I just stumbled on yet another bug I've had a hard time to find that was due to this type field problem... I didn't remember I solved it already somewhere else! 😜

As for the child/parent issue, I guess I don't need to copy the parent first, since using new child will create a copy itself! Makes sense!

Thanks again!

Genroa commented 6 years ago

I agree that child class objects should be allocated wih the right class, even if they are queried from the parent class' find method. Because of this, overriding methods/defining new properties has no use because after saving, if you findOne again, it will be cast to a Parent instance.

red-meadow commented 6 years ago

Hello!

I have a slightly different question: what is a proper way to transform a document from one child class to another (not create a copy)? I tried the solution mentioned above but the problem is that I don't change the fields of the existing document but instead remove it and create another one with the same id. Please, take a look at the code:

const Area = Class.create({
  name: 'Area',
  collection: new Mongo.Collection('areas'),
  typeField: 'type',
  fields: {
    size: Number
  }
})

const Forest = Area.inherit({
  name: 'Forest',
  fields: {
    plants: [String]
  }
})

const Wasteland = Area.inherit({
  name: 'Wasteland',
  fields: {
    pollution: Number
  }
})
// server.js
// create an instance of the first child class
let newForest = new Forest()
newForest.size = 500
newForest.plants = ["Oak"]
newForest.save()  // {_id: "6rrTAkieFmgjwv62T", type: "Forest", size: 500, plants: Array(1)}

// transform it to the the second child class
let forest = Forest.findOne()
let wasteland = new Wasteland(forest.raw())
wasteland.pollution = 700
forest.remove()    // to avoid duplicate key error
wasteland.save()  // {_id: "6rrTAkieFmgjwv62T", type: "Wasteland", size: 500, pollution: 700}

The final document is just what I need but remove and insert operations fire the related events which are not desired in this case.

Is there a better way to do it? Is switching classes with Astronomy a right way of thinking at all? Than you in advance!

lukejagodzinski commented 6 years ago

@red-meadow

Is switching classes with Astronomy a right way of thinking at all?

Everything depends on your use case. Don't know why you're doing it but if your use case requires that and it's not doable in another way that's ok.

The final document is just what I need but remove and insert operations fire the related events which are not desired in this case.

Using Astronomy requires triggering all the events that you've created. If you don't need to validate a document, you could just use plain collections. There is also another way of doing it but not perfect:

const forest = Forest.findOne();
const wasteland = new Wasteland(forest.raw());
wasteland.pollution = 700;
try {
  wasteland.validate();
  // Use Mongo collection directly to update document.
  Wasteland.getCollection().update(wasteland._id, { $set: wasteland.raw() });
} catch (err) {
  // Do something with validation error.
}

Haven't tested it but should work.