Pasar argumentos para requerir (al cargar el módulo)

114

¿Es posible pasar argumentos al cargar un módulo usando require?

Tengo el módulo, login.js que proporciona la funcionalidad de inicio de sesión. Requiere una conexión a la base de datos y quiero que se use la misma conexión a la base de datos en todos mis módulos. Ahora exporto una función login.setDatabase (...) que me permite especificar una conexión de base de datos, y eso funciona bien. Pero prefiero pasar la base de datos y cualquier otro requisito cuando cargue el módulo.

var db = ...
var login = require("./login.js")(db);

Soy bastante nuevo con NodeJS y generalmente desarrollo usando Java y Spring Framework, así que sí ... esta es una inyección de constructor :) ¿Es posible hacer algo como el código que proporcioné anteriormente?

Andreas Selenwall
fuente
También recomiendo mirar las respuestas a esta pregunta. Como se señaló en mi respuesta, un modismo común es pasar el appobjeto a los módulos requeridos.
David Weldon
En lugar de pasar todo este argumento por db, puede usar una implementación singleton y llamar a db.getInstance () donde sea necesario.
wulfgarpro

Respuestas:

237

Según sus comentarios en esta respuesta , hago lo que está tratando de hacer así:

module.exports = function (app, db) {
    var module = {};

    module.auth = function (req, res) {
        // This will be available 'outside'.
        // Authy stuff that can be used outside...
    };

    // Other stuff...
    module.pickle = function(cucumber, herbs, vinegar) {
        // This will be available 'outside'.
        // Pickling stuff...
    };

    function jarThemPickles(pickle, jar) {
        // This will be NOT available 'outside'.
        // Pickling stuff...

        return pickleJar;
    };

    return module;
};

Estructuro casi todos mis módulos así. Parece que funciona bien para mí.

flotanteLomas
fuente
¿Es appnecesario el argumento o puedo omitirlo? Mi módulo no hará un uso explícito de este argumento de la aplicación, pero no sé si node.js lo requiere para algo interno. Si está bien, la declaración de mi módulo se vería así:module.exports = function (db) {
Ulysses Alves
Eso era específico de una aplicación Express, por lo que definitivamente no es necesario.
floatingLomas
@floatingLomas El mecanismo de serialización en Python se llama pickle: stackoverflow.com/questions/11218477/…
SadSeven
1
Entonces, ¿qué sucede si hace referencia al módulo varias veces? El primero require(mymodule)(myargs)le daría un módulo con el que trabajar. Sin embargo, si lo hace referencia a otro lugar de otro módulo? en el sistema básico parece haber un caché involucrado, pero en este sistema, el caché devolvería el método del generador desnudo en llamadas posteriores a require(), y si le pasaba args require()(someargs), obtendría un módulo diferente ... tal vez me falta algo
Tom H
2
@TomH En ese caso, querrá almacenarlo en caché. Para hacer eso, tendría que estar var module;fuera de la función de exportación, y luego, tan pronto como ingrese a la, querrá verificar si moduleya está definido, y si es así, simplemente devuélvalo (y si no, inicialícelo). ¿Tiene sentido?
floatingLomas
37

No estoy seguro de si esto seguirá siendo útil para la gente, pero con ES6 tengo una forma de hacerlo que encuentro limpia y útil.

class MyClass { 
  constructor ( arg1, arg2, arg3 )
  myFunction1 () {...}
  myFunction2 () {...}
  myFunction3 () {...}
}

module.exports = ( arg1, arg2, arg3 ) => { return new MyClass( arg1,arg2,arg3 ) }

Y luego obtienes tu comportamiento esperado.

var MyClass = require('/MyClass.js')( arg1, arg2, arg3 )
vovkman
fuente
32

Si. En su loginmódulo, simplemente exporte una única función que tome dbcomo argumento. Por ejemplo:

module.exports = function(db) {
  ...
};
David Weldon
fuente
1
Probé esto. En login.js module.exports = function(app,db) { ... } module.exports.auth = function(req,res) { ... authentication stuff } , llama a la función "anónima" y establece las variables appy db, pero, al hacer esto, mi aplicación no encuentra la authfunción. ¿Qué estoy haciendo mal? Si elimino la función anónima, la authfunción vuelve a ser accesible.
Andreas Selenwall
Como todo en javascript, exportses un objeto. Puede asignarle propiedades (múltiples exportaciones) o puede asignarle un valor. Parece que asignó exportaciones a una función, pero luego asignó autenticación como una propiedad de la función que asignó a las exportaciones. Por lo tanto, tendría que hacer lo var auth = require("./login.js").authque puede no ser lo que pretendía. Si desea utilizar el patrón de la pregunta original, probablemente sea mejor ceñirse a un único valor de exportación. Si esto aún no tiene sentido, recomendaría publicar un resumen para que lo vea.
David Weldon
1
Después de leer esto de nuevo, parece que puede haber asignado authcomo propiedad del exportsobjeto, y luego en el módulo que asignó exportsa una función (anulando así la asignación anterior). Si invierte el orden de la asignación, debería poder acceder a la authfunción como esperaba. De nuevo, es difícil saberlo sin ver el código.
David Weldon
1
Muchas gracias por tu ayuda. Lo que no me di cuenta era exactamente lo que pretendías con require ('login'). Auth. Pensé que no había diferencia en var login = require('login')y var login = require('login')(app), pero hay una gran diferencia, no hay magia en la función anónima devuelta, es solo otra función / objeto. En lugar de tener una module.exports.auth, la función anomima ahora devuelve la función auth (entre otras), es decir return { auth: authFunction, login: loginFunction}. Entonces ahora funciona. Gracias.
Andreas Selenwall