0

I have a module as follows:

class mailer() {
   constructor() {
     this.template = "";
     this.email = "";
   }

   setTemplate(tplName) {
     this.template = tplName;
     return this;
   }

   sendEmail(email) {
     //some stuff
   }
}

module.exports = mailer;

And to consume that, well you just do:

const mailer = require('mymodule');

(new mailer()).setTemplate("foo").sendEmail('foo@bar.com');

The reason why I'm using a class is because several processes can consume that module at the same time so I need the variables to be isolated. This works as intended, but I will like to make the code cleaner by removing if possible the "new" part. I can export an instance of the class instead of exporting the class itself but then Node will cache after the first require and my code will be using the same instance every time.

DomingoSL
  • 14,920
  • 24
  • 99
  • 173
  • https://stackoverflow.com/questions/15666144/how-to-remove-module-after-require-in-node-js. Not recommended at all, but just seemed relevant – Ankit Sep 07 '18 at 12:16

2 Answers2

4

If it's just the new that is bothering you, export a factory function instead that you can call without new to create new instances:

module.exports = function() { return new mailer() };

const createMailer = require('mymodule');

createMailer().setTemplate("foo").sendEmail('foo@bar.com');

No, you should not try to export an instance, and you should not try to do any magic that refreshes an export after accessing it.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Create a factory with a getter in the Mailer file, and export the factory:

const mailerFactory = {
  get mailer() {
    return new Mailer();
  }
};

module.exports = mailerFactory;

Now you can create a new instance by calling:

const mailer = require("./Mailer").mailer;

mailer.setTemplate("foo").sendEmail('foo@bar.com');

Working example - look at the console.

Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 1
    This is pretty much the same suggestion of @Bergi, BUT the fact that you used GET makes it soooo much elegant! Thanks! (and to be honest with you I did not know about get) – DomingoSL Sep 07 '18 at 12:49
  • 1
    @DomingoSL The answer is correct, but a thing like this one is not elegant at all. A getter like this one is much more obscure and can hardly be recommended. Consider doing this in real life only if you want to troll a fellow developer who will rack his/her brain before figuring out that `require("./Mailer").mailer !== require("./Mailer").mailer`. Btw, `.mailer` takes more chars to type than `()`. – Estus Flask Sep 07 '18 at 13:41
  • @estus I did change the naming suggested by Ori Drori to make clearer in my code. I do not agree with the fact that this is not elegant. Using () to execute a function that returns an object with functions within has been always a workaround in JS in my opinion. Although is true that ".mailer" is larger than "()", you only write it once in the require, after that, you spare the "()" every time you have to send an email. – DomingoSL Sep 10 '18 at 14:09
  • @DomingoSL Calling factory function is conventional practice that every dev understands without saying. A descriptor provides a good portion of voodoo that may confuse even you if you forget what was the deal with this module. Elegant or not, you won't see this trick in well-written module exports because of these concerns, not because other devs don't know how to use descriptors. – Estus Flask Sep 10 '18 at 14:33
  • @DomingoSL - you'll need to call .mailer every time you want a new instance - it's the same thing as calling a function. – Ori Drori Sep 10 '18 at 14:34
  • @DomingoSL *you spare the "()" every time you have to send an email* - of course, you don't need to write this every time. A module with factory function is imported very same way, `const mailer = require('mymodule')()`. Just shorter and cleaner. – Estus Flask Sep 10 '18 at 14:35