1

I'm writing a object Factory Node module in ES6. I'm using the stamptit library https://github.com/stampit-org/stampit to get base objects started but enhance them and return my own objects through my factory.

Lets say I have these two files in '../../models/':

MYBASEJSONDATA.json

{
   "addresses": {
       "type": "ADDRESSJSONDATA.json",
       "multi": true,
       "required": true
   },
   .
   .
   .
}

ADDRESSJSONDATA.json

{
   "street_name": "SAMPLE",
   "zip_code": "75024"
   .
   .
   .
}

This is my current code with some comments:

import stampit from 'stampit';
import * as fs from 'fs';

// Allows me to run Node's async internal functions as a generator
function runNodeFunc( gen, iter) {
  (iter = gen((err, data) => (err && iter.throw(err)) ||
              iter.next(data))).next();
}

// Loop through an object's keys and values
function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for(let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}

// function coroutine(generatorFunction) {
//   return function (...args) {
//     let generatorObject = generatorFunction(...args);
//     generatorObject.next();
//     return generatorObject;
//   };
// }

function createModel(defModel) {
  let model = stampit();
  let customTypes = new Map(defModel.linked_custom_types);

  for(let [key, value] of objectEntries(defModel)) {
    if(customTypes.has(value.type)) {
      // This does not work here because the runNodeFunc in getModels
      // Does not pause execution
      let cusMod = getModel(value.type);
      model = model.props({[key]: cusMod});
    } else {
      // if it's a multi type create array
      model = model.static({[key]: value.multi ? [] : ''});
    }
  }
}

function getModel(fileName) {
  console.log(fileName);
  // IDEALLY, this function would allow an external yield
  runNodeFunc(function* readFile(resume) {
    let mod = yield fs.readFile(`../../models/${fileName}.json`,
                                resume);
    createModel(JSON.parse(mod));
  });
}

getModel('MYBASEJSONDATA');
// export default BodhiModel;

Basically, each one of the json files is going to be used to construct a new object. For now I'm doing simple mappings using the subproperties of each file(for example, addresses.multi tells me that it will take many objects of the "ADDRESSJSONDATA" type), but the goal is to add more complexity to the objects.

I'm not too sure about using the 'stampit' library, I could very well just create my own objects and it feels redundant but that is a decision I can change later.

I would like to have a sort of recursive process to create these complex objects. I know I can implement that easily with a

yield *createModel(subType)

inside my createModel function so it would look something like this:

function createModel(fileName) {
  let model = stampit();
  // CALL getModels here! *1
  let customTypes = new Map(defModel.linked_custom_types);

  for(let [key, value] of objectEntries(defModel)) {
    if(customTypes.has(value.type)) {
      // ADD THIS HERE *2
      let cusMod = yield *createModel(value.type);
      model = model.props({[key]: cusMod});
    } else {
      model = model.static({[key]: value.multi ? [] : ''});
    }
  }
}

By adding *2, I'm passing the control. I've used to for tree traversals etc. But the tricky part is that in *1 I'm using that function which is surrounded by a helper function to use a generator to fetch the files. But this in itself does NOT pause execution since the getModels function is not a generator itself.

I've tried many ways, and I haven't found how I can keep the helper function runNodeFunc AND implement a recursive generator. If I have to just bite the bullet and wrap everything in a callback then I will, but I want to ask if this method of object creation is optimal and if I'm doing extra things that can be simplified. I'm also concerned with have generators create the object that this module is supposed to return.

I forgot to mention, there is a reference to "linked_custom_types" in the createModel function. These are just the nested objects for the particular main model being generated.

SirMyztiq
  • 49
  • 4
  • Are you using **co** or something like that? – Amit Oct 15 '15 at 06:47
  • @Amit No, just straight ES generators. I'm transpiling using Babel.js – SirMyztiq Oct 15 '15 at 06:49
  • Looks like you're working very hard to do something that's been done (done better I might say), and falling to anti-patterns in the process. I suggest you look at [**co**](https://github.com/tj/co) or find yourself a different implementation of these concepts. – Amit Oct 15 '15 at 07:00
  • @Amit It sure feels like it. I've done something similar in Python but I can't remember now. I'm also positive there is an antipattern here but I can't see it. I've been blinded because I'm absolutely just hard heading this. I know its been done but I've spent a lot of time hunting for examples and other ways. Do you have any in mind? I'll look at co, I considered it for a second but I foolishly dismissed it as a polyfill for Promises. – SirMyztiq Oct 15 '15 at 07:14
  • No, it's not at all a polyfill. Have a look, it will probably show you how to do that correctly (and utilize the features already there). One thing to remember is that you can't "cheat" asynchronicity. No synchronous code that depeneds on asynchronous results will work. – Amit Oct 15 '15 at 07:21

0 Answers0