Reputation: 3064
I'm trying to make a subclass of an image library on github called Jimp. As far as I can tell from the docs, you don't instantiate the class in the usual way. Instead of saying new Jimp()
, it seems the class has a static method called read
that acts as a constructor. From the docs...
Jimp.read("./path/to/image.jpg").then(function (image) {
// do stuff with the image
}).catch(function (err) {
// handle an exception
});
It looks like from the docs, that that image
returned by read()
is an instance allowing the caller to do stuff like image.resize( w, h[, mode] );
and so on.
I'd like to allow my subclass callers to begin with a different static method that reads an image and does a bunch of stuff, summarized as follows...
class MyJimpSubclass extends Jimp {
static makeAnImageAndDoSomeStuff(params) {
let image = null;
// read in a blank image and change it
return Jimp.read("./lib/base.png").then(_image => {
console.log(`image is ${_image}`);
image = _image;
let foo = image.bar(); // PROBLEM!
// ...
// ...
.then(() => image);
}
bar() {
// an instance method I wish to add to the subclass
}
// caller
MyJimpSubclass.makeAnImageAndDoSomeStuff(params).then(image => {
//...
});
You might be able to guess that nodejs gets angry on the line let foo = image.bar();
, saying
TypeError image.bar is not a function
.
I think this is understandable, because I got that image using Jimp.read()
. Of course that won't return an instance of my subclass.
MyJimpSubclass.read()
. Same problem.Second idea: Implement my own static read method. Same problem.
static read(params) {
return super.read(params);
}
Third idea: Ask SO
Upvotes: 1
Views: 158
Reputation: 781028
This might be a way to do it. Start with your own read
method, and have it change the prototype of the returned object.
static read(...params) {
return super.read(...params).then(image) {
image.prototype = MyJimpSubclass;
resolve(image);
}
}
Upvotes: 1
Reputation: 224921
The implementation of Jimp.read
refers to Jimp
specifically, so you would have to copy and change it in your subclass (ick, but not going to break anything since the constructor is also part of the API) or make a pull request to have it changed to this and have subclassing explicitly supported:
static read(src) {
return new Promise((resolve, reject) => {
void new this(src, (err, image) => {
if (err) reject(err);
else resolve(image);
});
});
}
Alternatively, you could just implement all your functionality as a set of functions on a module. This would be next on my list after making a pull request. Would not recommend a proxy.
const makeAnImageAndDoSomeStuff = (params) =>
Jimp.read("./lib/base.png").then(image => {
console.log(`image is ${image}`);
let foo = bar(image);
// …
return image;
});
function bar(image) {
// …
}
module.exports = {
makeAnImageAndDoSomeStuff,
bar,
};
Even changing the prototype would be better than a proxy (but this is just a worse version of the first option, reimplementing read
):
static read(src) {
return super.read(src)
.then(image => {
Object.setPrototypeOf(image, this.prototype);
return image;
});
}
Upvotes: 1
Reputation: 26547
You have a couple of options. The cleanest is probably to make a subclass like you started, but then implement the Jimp
static method on it, as well as your own. In this case, it's not really inheritance, so don't use extends
.
class MyJimp {
static read(...args) {
return Jimp.read.apply(Jimp, args);
}
static makeAnImage(params) {
return this.read(params)
.then(image => {
// do stuff
return image
});
}
}
From there, I would make an object which has all of the new functions you want to apply to image
:
const JimpImageExtension = {
bar: () => { /* do something */ }
};
Finally, in your static methods, get the image and use Object.assign()
to apply your new functions to it:
class MyJimp {
static read(...args) {
return Jimp.read.apply(Jimp, args)
.then(image => Object.assign(image, JimpImageExtension));
}
static makeAnImage(params) {
return this.read(params)
.then(image => {
// do stuff
image.bar();
return image;
});
}
}
This should do the trick by applying your extra functions to the image. You just need to make sure that you apply it at every point that can generate an image (if there is more than just read
). Since in the other functions, it's using your version of read()
, you only need to add the functions in the one.
Another approach would be if Jimp
makes their image class accessible, you could also add them to the prototype of that (though usually in libraries like this, that class is frequently inaccessible or not actually a class at all).
Upvotes: 1