sylvainpolletvillard / ObjectModel

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

Wrapping an object extended from ObjectModel inhibits that objects internal access to private members. #99

Closed Ravenex closed 5 years ago

Ravenex commented 5 years ago

I am trying to implement lazy loading of properties, or in my case groups of properties, from a database as that group of properties is needed. To accomplish this I want to wrap an object extended from ObjectModel in a outer proxy that can intercept the request for that property and load it before returning it's value. When I do this the object's ability to internally reference it's own private properties breaks. As an example:

const ObjectModel = require('objectmodel').ObjectModel

class Person extends ObjectModel({ name: String, _religion: [String], lazyProps: Array }) {

  getName() { return this.name }
  getReligion() { return this._religion }

  isLazy(key) {return this.lazyProps.indexOf(key) != -1}

  lazyLoad(key) {
    var val = "Jewish" //this would be loaded from a datastore
    this[key] = val
  }

}

const lazyProxy = {

  get(o, key) {
    if (typeof key === "string" && o.isLazy(key)) {
      o.lazyLoad(key)
    }
    return o[key]
  },

  set(o, key, val) {
    if (typeof key === "string" && o.isLazy(key)) {
      o.lazyLoad(key)
    }
      o[key] = val
  }
}

const Joe = new Person({ name: "Joe", lazyProps: ["_religion" ] })
console.log(Joe.getName()) //Joe
console.log(Joe.getReligion()) //undefined
Joe.lazyLoad("_religion")
console.log(Joe.getReligion()) //Jewish

const Ann = new Person({ name: "Ann", lazyProps: ["_religion" ] })
const lazyAnn = new Proxy(Ann, lazyProxy)
console.log(lazyAnn.getName()) //Ann
console.log(lazyAnn.getReligion()) //expect Jewish but receive: TypeError: cannot access to private property _religion
sylvainpolletvillard commented 5 years ago

yeah it's a bug, due to your isLazy method in the getter

shorter way to reproduce:

const O = ObjectModel({ _priv: String }).defaults({
  getPriv(){ 
     this.randomMethod();
     return this._priv
 },
  randomMethod(){ }
})

const o = new O({ _priv: "test" })
o.getPriv(); // cannot access to private

basically private access is granted for exactly one method call. If there is another method call in the getter, the access pass is exhausted and you get rejected.

Hard to find out but probably easy to fix. Stay tuned

sylvainpolletvillard commented 5 years ago

Fixed and released in v3.7.5

Thank you for the report !

Ravenex commented 5 years ago

Thank you for the extremely fast response.