Node + ES6 classes: Setting up a set of cached objects - node.js

I've tried to search for instance caching and singletons on Google and StackOverflow without success, seeing only posts about module.exports, if you know a post that answers this question, feel free to reference it. Thank you!
I have an application that needs to work on a set of objects that rarely change, and hence need to be cached for performance optimisation.
Here is a toy example where a single property is set directly.
When I call the application, I export an object that will contain the set of cached objects in assets_cached.js:
const Assets = {};
module.exports.Assets = Assets;
In another module of the application I have an ES6 class:
const _ = require('lodash')
const { Assets } = require('./assets_cached')
class Asset {
constructor(id, some_property) {
if (id in Assets) {
// Update instance data with cached properties
_.assign(this, Assets_cached[id]);
} else {
// If it's not cached, create a new object
this.id = id;
this.some_property = some_property;
// Cache this object
Assets_cached[id] = this;
}
}
getProperty() {
return this.some_property;
}
setProperty(value) {
this.some_property = value;
// Is there a way of avoiding having to do this double assignment?
Assets_cached[id].some_property = value;
}
}
module.exports = Asset;
How may I avoid having to set the some_property twice (in the current instance and the cache, while ensuring that other instances are updated in parallel)?
Ideally I'd like to do something like:
if (id in Assets) {
this = Assets.cached[id]
}
inside the constructor, but this is not possible.
What's the most elegant and correct way of making this work?

Ideally I'd like to do something like this = Assets.cached[id] inside the constructor
The magic keyword here is return. You can just return an arbitrary object from the constructor and it will be used instead of this.
constructor(id, some_property) {
if (id in Assets) {
// use cached instance instead of creating a new one
return Assets_cached[id];
} else {
this.id = id;
this.some_property = some_property;
// Cache this object
Assets_cached[id] = this;
}
}

Here is the approach to the comment that was made some half an hour ago ...
const { Assets_cached } = require('./assets_cached');
// const { AssetStore } = require('./assetstore');
class Asset {
constructor(id, some_property) { // clean/lean constructor.
this.id = id;
this.some_property = some_property;
}
getProperty() {
return this.some_property;
}
setProperty(value) {
this.some_property = value;
}
}
function isAsset(type) {
// poor man's approach ... change to something more feasible.
return (type instanceof Asset);
}
function createAsset(id, some_property) { // factory that also handles caching.
var
asset = Assets_cached[id];
// asset = AssetStore.get(id);
if (!(asset && isAsset(asset))) {
asset = Assets_cached[id] = (new Asset(id, some_property));
// AssetStore.put(id, (asset = new Asset(id, some_property)));
}
return asset;
}
module.exports = {
create : createAsset,
isAsset : isAsset
};
Note
One also should consider providing a minimal API to Assets_cached, something like put/set, get and delete instead of Assets_cached being an entirely exposed, plain key-value store.

Related

Could haxe macro be used to detect when object is dirty (any property has been changed)

Let say we have an object:
#:checkDirty
class Test {
var a:Int;
var b(default, default):String;
var c(get, set):Array<Int>;
public function new() {
...
}
public function get_c() {
...
}
public function set_c(n) {
...
}
}
Could we write a macro checkDirty so that any change to field/properties would set property dirty to true. Macro would generate dirty field as Bool and clearDirty function to set it to false.
var test = new Test();
trace(test.dirty); // false
test.a = 12;
trace(test.dirty); // true
test.clearDirty();
trace(test.dirty); //false
test.b = "test"
trace(test.dirty); //true
test.clearDirty();
test.c = [1,2,3];
trace(test.dirty); //true
Just to note - whenever you consider proxying access to an object, in my experience, there are always hidden costs / added complexity. :)
That said, you have a few approaches:
First, if you want it to be pure Haxe, then either a macro or an abstract can get the job done. Either way, you're effectively transforming every property access into a function call that sets the value and also sets dirty.
For example, an abstract using the #:resolve getter and setter can be found in the NME source code, replicated here for convenience:
#:forward(decode,toString)
abstract URLVariables(URLVariablesBase)
{
public function new(?inEncoded:String)
{
this = new URLVariablesBase(inEncoded);
}
#:resolve
public function set(name:String, value:String) : String
{
return this.set(name,value);
}
#:resolve
public function get(name:String):String
{
return this.get(name);
}
}
This may be an older syntax, I'm not sure... also look at the operator overloading examples on the Haxe manual:
#:op(a.b) public function fieldRead(name:String)
return this.indexOf(name);
#:op(a.b) public function fieldWrite(name:String, value:String)
return this.split(name).join(value);
Second, I'd just point out that if the underlying language / runtime supports some kind of Proxy object (e.g. JavaScript Proxy), and macro / abstract isn't working as expected, then you could build your functionality on top of that.
I wrote a post (archive) about doing this kind of thing (except for emitting events) before - you can use a #:build macro to modify class members, be it appending an extra assignment into setter or replacing the field with a property.
So a modified version might look like so:
class Macro {
public static macro function build():Array<Field> {
var fields = Context.getBuildFields();
for (field in fields.copy()) { // (copy fields so that we don't go over freshly added ones)
switch (field.kind) {
case FVar(fieldType, fieldExpr), FProp("default", "default", fieldType, fieldExpr):
var fieldName = field.name;
if (fieldName == "dirty") continue;
var setterName = "set_" + fieldName;
var tmp_class = macro class {
public var $fieldName(default, set):$fieldType = $fieldExpr;
public function $setterName(v:$fieldType):$fieldType {
$i{fieldName} = v;
this.dirty = true;
return v;
}
};
for (mcf in tmp_class.fields) fields.push(mcf);
fields.remove(field);
case FProp(_, "set", t, e):
var setter = Lambda.find(fields, (f) -> f.name == "set_" + field.name);
if (setter == null) continue;
switch (setter.kind) {
case FFun(f):
f.expr = macro { dirty = true; ${f.expr}; };
default:
}
default:
}
}
if (Lambda.find(fields, (f) -> f.name == "dirty") == null) fields.push((macro class {
public var dirty:Bool = false;
}).fields[0]);
return fields;
}
}
which, if used as
#:build(Macro.build())
#:keep class Some {
public function new() {}
public var one:Int;
public var two(default, set):String;
function set_two(v:String):String {
two = v;
return v;
}
}
Would emit the following JS:
var Some = function() {
this.dirty = false;
};
Some.prototype = {
set_two: function(v) {
this.dirty = true;
this.two = v;
return v;
}
,set_one: function(v) {
this.one = v;
this.dirty = true;
return v;
}
};

Unable to use variable outside of class in the class

I am making a simple note taking app to learn node and ES6. I have 3 modules - App, NotesManager and Note. I am importing the Note class into the NotesManager and am trying to instantiate it in its addNote function. The problem is that even though the import is correct, it turns out to be undefined inside the class definition. A simpler solution would be to just instantiate the NotesManager class and add the Note class to its constructor however, I want to have NotesManager as a static utility class.
Here is my code.
Note.js
class Note {
constructor(title, body) {
this.title = title;
this.body = body;
}
}
module.exports = Note;
NotesManager.js
const note = require("./Note");
console.log("Note: ", note); //shows correctly
class NotesManager {
constructor() {}
static addNote(title, body) {
const note = new note(title, body); //Fails here as note is undefined
NotesManager.notes.push(note);
}
static getNote(title) {
if (title) {
console.log(`Getting Note: ${title}`);
} else {
console.log("Please provide a legit title");
}
}
static removeNote(title) {
if (title) {
console.log(`Removing Note: ${title}`);
} else {
console.log("Please provide a legit title");
}
}
static getAll() {
//console.log("Getting all notes ", NotesManager.notes, note);
}
}
NotesManager.notes = []; //Want notes to be a static variable
module.exports.NotesManager = NotesManager;
App.js
console.log("Starting App");
const fs = require("fs"),
_ = require("lodash"),
yargs = require("yargs"),
{ NotesManager } = require("./NotesManager");
console.log(NotesManager.getAll()); //works
const command = process.argv[2],
argv = yargs.argv;
console.log(argv);
switch (command) {
case "add":
const title = argv.title || "No title given";
const body = argv.body || "";
NotesManager.addNote(title, body); //Fails here
break;
case "list":
NotesManager.getAll();
break;
case "remove":
NotesManager.removeNote(argv.title);
break;
case "read":
NotesManager.getNote(argv.title);
break;
default:
notes.getAll();
break;
}
Is it possible for me to create a strict utility class which I can use without instantiating like in Java? Pretty new here and have tried searching for it without any luck. Thank you for your help.
When you do this:
const note = new note(title, body);
you redefine note shadowing the original note from the outer scope. You need to pick a different variable name.
Something like this should work better:
static addNote(title, body) {
const some_note = new note(title, body); //Fails here as note is undefined
NotesManager.notes.push(some_note);
}

passing function to a class in nodejs

I have a function that I need to pass to a class I have defined in nodeJs.
The use case scenario is I want to give the implementer of the class the control of what to do with the data received from createCall function. I don't mind if the method becomes a member function of the class. Any help would be appreciated.
//Function to pass. Defined by the person using the class in their project.
var someFunction = function(data){
console.log(data)
}
//And I have a class i.e. the library.
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall(){
var result = new Promise (function(resolve,reject) {
resolve(callApi());
});
//doesn't work. Keeps saying notificationHandler is not a function
result.then(function(resp) {
this.notificationHandler(resp);
}) ;
//I want to pass this resp back to the function I had passed in the
// constructor.
//How do I achieve this.
}
callApi(){ ...somecode... }
}
// The user creates an object of the class like this
var obj = new A("abc#gmail.com", someFunction);
obj.createCall(); // This call should execute the logic inside someFunction after the resp is received.
Arrow functions (if your Node version supports them) are convenient here:
class A {
constructor(user, handler) {
this.user = user;
this.notificationHandler = handler;
}
createCall() {
var result = new Promise(resolve => {
// we're fine here, `this` is the current A instance
resolve(this.callApi());
});
result.then(resp => {
this.notificationHandler(resp);
});
}
callApi() {
// Some code here...
}
}
Inside arrow functions, this refers to the context that defined such functions, in our case the current instance of A. The old school way (ECMA 5) would be:
createCall() {
// save current instance in a variable for further use
// inside callback functions
var self = this;
var result = new Promise(function(resolve) {
// here `this` is completely irrelevant;
// we need to use `self`
resolve(self.callApi());
});
result.then(function(resp) {
self.notificationHandler(resp);
});
}
Check here for details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

Node Singleton Use

The author of this article uses singletons for the service layer in this example Node api:
https://html5hive.org/how-to-create-rest-api-with-node-js-and-express/
He states, "We only want there to ever be one instance of our player service, so instead of exporting the class itself, we’ll export a new instance of it. Module files in node are only ever executed once, so this effectively gives us a singleton."
'use strict';
var uuid = require('node-uuid');
class PlayersService {
constructor() {
this.players = [];
}
getPlayers() {
return this.players;
}
getSinglePlayer(playerId) {
var player = this.players.filter(p => p.id === playerId)[0];
return player || null;
}
addPlayer(info) {
// prevent a bit of bad/duplicate data
if (!info || this.players.filter(p => (p.firstName === info.firstName && p.lastName === info.lastName)).length > 0) {
return false;
}
info.id = uuid.v4();
this.players.push(info);
return true;
}
updatePlayer(playerId, info) {
var player = this.getSinglePlayer(playerId);
if (player) {
player.firstName = info.firstName ? info.firstName : player.firstName;
player.lastName = info.lastName ? info.lastName : player.lastName;
player.displayName = info.displayName ? info.displayName : player.displayName;
return true;
}
return false;
}
}
module.exports = new PlayersService();
Which seems reasonable since the function of these services is to provide the same implementation for the controllers that use them.
However, in this post:
On Design Patterns: When to use the Singleton?
the poster asks for a legitimate use case for singletons other than a Logger class. Several people responded to his question by saying that singletons should never be used.
But isn't the use of singletons for services like the one I've copied here a legitimate use case and a best practice so that you are not creating multiple instances that provide the same implementation? Thanks.

Javascript accessing object and array defined in modular function

This is a bit foreign to me and I'm probably not understanding it correctly. This is what I have:
var imgModule = (function() {
var imgLocations = {};
var images = [];
imgLocations.setImage = function(img, location) {
imgLocations[img] = location;
}
imgLocations.getImg = function(img) {
return imgLocations[img];
}
imgLocations.setImageArray = function(img) {
images.push(img);
}
imgLocations.getImageArray = function() {
return images;
}
return imgLocations;
}());
I want to be able to access the imgLocations Object and images array from outside this function. The setting functions work, but
document.getElementById("but").onclick = function() {
console.log(imgModule.imgLocations.getImageArray());
console.log(imgModule.imgLocations.getImg(imgName));
}
Both return "undefined". How do I access these variables? And how can I improve this function? Please be patient with me and explain what I'm doing wrong :) I'm trying to learn it the right way instead of defining a global variable outside all functions.
The reason why this isn't working, is because your imgModule is returning the imgLocations object. That being the case, imgModule will actually be the imgLocations object. So you would access your methods like so:
imgModule.setImage()
imgModule.getImg()
imgModule.getImageArray()
imgModule.setImageArray()
And as #gillesc stated. If you are wanting to keep the current syntax of imgModule.imgLocations.getImg() then you could return the imgLocations like so
return {
imgLocations: imgLocations
}
doing so would allow you to add more functionality to your module
return {
imgLocations: imgLocations,
otherObject: otherObject
}
...
imgModule.otherObject.someFunctionCall();
The problem is you are returning the object created and are not setting it as a property of an object.
So in your case this is how it would work.
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
What you need to do is return it like this
return {
imgLocations: imgLocations
}
If you want the API you are attending to create and still have access to the array which you can not do currently.
You don't access imgModule.imgLocations, since what you return is imgLocations, you should access them as:
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
It seems you try to write module pattern.
For deep understanding, I recommend you following article:
The Module Pattern, by Addy Osmani
and pay attention to example with counter:
var testModule = (function () {
var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})();
// Usage:
// Increment our counter
testModule.incrementCounter();
// Check the counter value and reset
// Outputs: counter value prior to reset: 1
testModule.resetCounter();

Resources