ORM’s Dreaming Big: pt 2 (The instance)

Before we went over the Schema which is about validation, indexes, population and schematypes. Here we’ll go into what people will be most likely using, the instance. So, what is the instance?

An Instance is

  • Holds values you wish to store or have retrieved
  • Something that can be created, requested, updated and deleted
  • Something that has properties which can match conditions

Basically an instance is the actual values you want to store or retrieve. Probably the most important part about the database as without it, well, you have nothing.

Generic yet Important Things

Callbacks

Callbacks can be implemented in one of two ways; callback(err,obj) or Promises.

Constructor(ObjectId, function(err,instance){
  if(err) throw err;
  console.log("this is our instance", instance);
});

Constructor(ObjectId).next(function(instance){
  console.log("this is our instance", instance); 
}).catch(function(err){
  throw err;
});

This is meant to support anything that you want.

Handles and Values

Instances are technically either an ObjectID handle or the Actual Values. They both have the same interface however with ObjectID’s you do not have to load all of the values nor do you have all of the values. While with an instance you do. This is to support as much IO or as little IO as you desire without having to change the interface.

Creating

Creating an instance should be as simple as creating an object in javascript

Standard – Construct and Save
var instance = new Constructor({
  property:"value"
});
instance.property2 = "value2";

instance.save(function(err){
  if(err) throw new Error("creating caused an error");
  console.log("finshed creating");
});

Now, we haven’t gotten into “Constructors” or “Models” however hopefully this sort of syntax is familiar to you. It’s simple. We want to create a new instance, so we construct it. Because values may be added or removed, the object is not saved right after creating. Additionally, its important that this is done asynchrnously. We don’t know when the file will be saved or how the file will be saved, only that the file will be saved.

Calling the Constructor – Less IO

When all of the values are already in the json object, constructing the object is a waste of time and resources.

Constructor({
  property:"value",
  property2:"value2"
}, function(err,objectid){
  if(err) throw new Error("error when creating");
  console.log("finished creating");
});

You may notice that the standard’s callback has no objectid but the create does. This is because if when you’ve succesfully saved, an ObjectID is already set for you in addition, you already have an interface to interact with the Instance. So there is no point in returning anything. While when using create, it will give you the ObjectID handle to provide you an interface to interact with. However, the handle will not have any properties in it so I would suggest you use instances if you want them.

Static Method – Obvious

In addition,  you may also call the static method. This will return the instance.

Constructor.create({
  property:"value",
  property2:"value2"
}, function(err,instance){
  if(err) throw new Error("error when creating");
  console.log("finished creating");
});

Retrieving

Generally the way retrieving will work is through the Constructors static methods. However, we are going for sugar here.

Standard – ObjectID Populating

If we have an ObjectID Handle, we can populate it into an actual Instance. It’s important to note there is a difference between an ObjectID Value and an ObjectID Handle. The value is the bytes/buffer that actually gets indexed. The ObjectID Handle has all the methods of a normal Instance. Generally all ObjectID Values will be transformed into Instances when retrieving an instance. In addition, anywhere you can use an ObjectID Value you can use an ObjectID Handle

objectidHandle.populate(function(err,instance){
  if(err) throw new Error("error in populating");
  console.log("populated the instance");
});
By ObjectID Value – Opposite
//Retreiving
Constructor(objectidValue,function(err,instance){
  if(err) throw new Error("error in retrieving");
  console.log("retrieved the instance");
});

The above is simple. We use our constructor with the object id and it will return an instance. This is the exact opposite as the initial where we send in some values to create an instance and we receive an objectID handle and this will retreive the instance based on the Handle or Value.

Static Method – Obvious
//Retreiving
Constructor.get(objectidValue,function(err,instance){
  if(err) throw new Error("error in retrieving");
  console.log("retrieved the instance");
});

Updating

Standard – save

With your Constructed/Retrieved Object it is the exact same as it was before, simply save.

//Updating
instance.property = "new value";
instance.save(function(err){
  if(err) throw new Error("updating caused an error");
  console.log("finished");
});
Static Method – Obvious

You may also update just by calling the update method of your constructor

Constructor.update(ObjectId, 
  {property:"new value"},
  function(err, instance){
    if(err) throw new Error("error when using update");
    console.log("ran update");
});

Deleting

Deleting is the last part of our crud interface here. As you might Imagine, it’s more of the same

Standard – destroy
//Updating
instance.destroy(function(err){
  if(err) throw new Error("destroying caused an error");
  console.log("finished");
});
Static Method – Obvious

You may also update just by calling the update method of your constructor

Constructor.destroy(ObjectId, function(err, instance){
    if(err) throw new Error("error when using update");
    console.log("ran update");
});

 Property Setting and Getting

Digestors and Verbose

All properties on an instance are actually getter and setter functions.

Object.defineProperty(instance, "propertyname", {
  get: function(){
    return Schema.propertyname.verbose(
      instance._rawValues.propertyname
    );
  },
  set: function(v){
    var ds = Schema.propertyname.digestors;
    var l = ds.length;
    var vv = void(0);
    for(var i=0;i<l;i++){
      vv = ds[i](v);
      if(typeof vv != "undefined") break;
    }
    if(i===l){
      throw new Error("cannot digest ",v);
    }
    instance._rawValues.propertyname = vv;
  }
});

For the getter we are returning the verbose value. For the Setter, we are digesting the value to its raw type.

Marking Dirty Properties and resetting

The first thing that can be done is to mark dirty properties. This also ties in with the way digesters and getters. In addition when setting, we also set which properties are dirty. This is done so that only the dirty values are actually updated

set: function(v){
    var vv = Schema.property.digest(v);
    if( vv == instance._initvalues.property ){
      delete instance._dirty.property
    }else{
      instance._dirty.property = vv;
    }
    instance._values.property = vv;
   }

Instance.prototype.save = function(cb){
  return Instance.update(this.id,this._dirty,cb);
}

Instance.prototype.reset = function(){
  for(var i in this._dirty){
    if(this._dirty[i]){
      this._dirty[i] = false;
      this._values[i] = this._initvalues[i]
    }
  }
}

 

Resync with the sender

At times you may want to ensure your instance is the exact same as the one on the database or the location that sent you the instance. All that needs to be done is resync

Instance.prototype.resync = function(cb){
  var _this = this;
  Instance(this.id,function(err,values){
    if(err) return cb(err);
    for(var i in values){
      _this[i] = values[i];
    }
    cb(void(0), _this);
  });
}
Listening for Updates

You may also use an event emitter that sends “update” event with property name and value

Instance.prototype.syncTo = function(ee){
  var _this = this
  ee.on("update",function(prop,val){
    _this[prop] = val;
  })
};

Dom and QueryString Interactions

And of course, you will need some dom interactions. Idealy, I would use an available library such as qs and serializeObject and deserialize to ensure I don’t mess up. From there I would properly set either the values in the object or the values in the query string or form. In addition its also possibly to bind the Instance to the form by using syncTo.

That is the instance

Perhaps there is too much sugar here. Perhaps not enough in the right areas. I’ve considered streaming as an alterantive plan however, In the end, I believe a simple API is a good api. Perhaps I should prioritize a bit. What is for sure in is all of the static methods, ObjectID.populate, instance saving, instance destroying and using the Constructor, well as a constructor. In addition, the dom/querystring aspects is pretty important since without it we’re back at square one: A decent ORM with refusal to believe the DOM or urls that don’t use JSON exist. Everything else is a bit up in the air.

Leave a Reply

Your email address will not be published. Required fields are marked *

AlphaOmega Captcha Classica  –  Enter Security Code
     
 

Solve : *
10 × 16 =